2020-12-16 02:32:32 +01:00
#!/usr/bin/env python
2020-10-06 23:22:11 +02:00
# -*- coding: utf-8 -*-
2020-01-07 07:25:22 +01:00
2020-12-16 02:32:32 +01:00
import raspyrfm
2020-01-07 07:25:22 +01:00
import sensors
import sys
import time
import threading
import math
import json
from datetime import datetime
2020-12-29 00:18:03 +01:00
import apiserver
import os
2020-12-16 02:32:32 +01:00
try :
#python2.7
import SocketServer as socketserver
from urlparse import urlparse , parse_qs
from SimpleHTTPServer import SimpleHTTPRequestHandler as Handler
except ImportError :
#python3
import socketserver
from http . server import SimpleHTTPRequestHandler as Handler
from urllib . parse import urlparse , parse_qs
2020-10-06 23:22:11 +02:00
lock = threading . Lock ( )
2020-12-29 00:18:03 +01:00
script_dir = os . path . dirname ( __file__ )
with open ( script_dir + " /lacrossegw.conf " ) as jfile :
2020-12-16 02:32:32 +01:00
config = json . load ( jfile )
if raspyrfm . raspyrfm_test ( 2 , raspyrfm . RFM69 ) :
print ( " Found RaspyRFM twin " )
rfm = raspyrfm . RaspyRFM ( 2 , raspyrfm . RFM69 ) #when using the RaspyRFM twin
elif raspyrfm . raspyrfm_test ( 1 , raspyrfm . RFM69 ) :
print ( " Found RaspyRFM single " )
rfm = raspyrfm . RaspyRFM ( 1 , raspyrfm . RFM69 ) #when using a single single 868 MHz RaspyRFM
2020-01-07 07:25:22 +01:00
else :
2020-12-16 02:32:32 +01:00
print ( " No RFM69 module found! " )
exit ( )
2020-01-07 07:25:22 +01:00
try :
2020-12-16 02:32:32 +01:00
from influxdb import InfluxDBClient
influxClient = InfluxDBClient (
host = config [ " influxdb " ] [ " host " ] ,
port = config [ " influxdb " ] [ " port " ] ,
username = config [ " influxdb " ] [ " user " ] ,
password = config [ " influxdb " ] [ " pass " ]
)
createDb = True
for db in influxClient . get_list_database ( ) :
if db [ " name " ] == config [ " influxdb " ] [ " database " ] :
createDb = False
break
if createDb :
influxClient . create_database ( config [ " influxdb " ] [ " database " ] )
influxClient . switch_database ( config [ " influxdb " ] [ " database " ] )
influxClient . alter_retention_policy ( " autogen " , duration = " 2h " , replication = 1 , shard_duration = " 1h " )
influxClient . create_retention_policy ( " one_week " , duration = " 1w " , replication = 1 , shard_duration = ' 24h ' )
influxClient . create_retention_policy ( " one_year " , database = config [ " influxdb " ] [ " database " ] , duration = " 365d " , replication = 1 , shard_duration = ' 1w ' )
influxClient . create_continuous_query ( " three_min " , ' SELECT mean(T) as " T " , mean(RH) as " RH " , mean(AH) as " AH " , mean(DEW) as " DEW " INTO " one_week " . " lacrosse " from " lacrosse " GROUP BY time(3m),* ' )
influxClient . create_continuous_query ( " three_hour " , ' SELECT mean(T) as " T " , mean(RH) as " RH " , mean(AH) as " AH " , mean(DEW) as " DEW " INTO " one_week " . " lacrosse " from " lacrosse " GROUP BY time(3h),* ' )
else :
influxClient . switch_database ( config [ " influxdb " ] [ " database " ] )
2020-10-06 23:22:11 +02:00
except Exception as ex :
2020-12-16 02:32:32 +01:00
influxClient = None
print ( " influx init error: " + ex . __class__ . __name__ + " " + ( ' ' . join ( ex . args ) ) )
2020-01-07 07:25:22 +01:00
try :
2020-12-16 02:32:32 +01:00
import paho . mqtt . client as mqtt
mqttClient = mqtt . Client ( )
mqttClient . connect ( " localhost " , 1883 , 60 )
mqttClient . loop_start ( )
2020-01-07 07:25:22 +01:00
except :
2020-12-16 02:32:32 +01:00
mqttClient = None
print ( " mqtt init error " )
2020-01-07 07:25:22 +01:00
2020-02-05 01:15:56 +01:00
rfm . set_params (
2020-01-07 07:25:22 +01:00
Freq = 868.300 , #MHz center frequency
Datarate = 9.579 , #kbit/s baudrate
2020-12-16 02:32:32 +01:00
ModulationType = raspyrfm . rfm69 . FSK , #modulation
2020-01-07 07:25:22 +01:00
SyncPattern = [ 0x2d , 0xd4 ] , #syncword
Bandwidth = 100 , #kHz bandwidth
RssiThresh = - 100 , #-100 dB RSSI threshold
)
class BaudChanger ( threading . Thread ) :
2020-12-16 02:32:32 +01:00
baud = False
def __init__ ( self ) :
threading . Thread . __init__ ( self )
def run ( self ) :
while True :
time . sleep ( 15 )
print ( " Change baudrate " )
if self . baud :
rfm . set_params ( Datarate = 9.579 )
else :
rfm . set_params ( Datarate = 17.241 )
self . baud = not self . baud
2020-01-07 07:25:22 +01:00
baudChanger = BaudChanger ( )
baudChanger . daemon = True
baudChanger . start ( )
def writeInflux ( payload ) :
2020-12-16 02:32:32 +01:00
if not influxClient :
return
T = payload [ " T " ]
wr = {
" measurement " : " lacrosse " ,
" fields " : {
" T " : T
} ,
2020-12-29 00:18:03 +01:00
" tags " : { " sensor " : payload [ " id " ] if not ( " room " in payload ) else payload [ " room " ] }
2020-12-16 02:32:32 +01:00
}
if ( " RH " in payload ) :
wr [ " fields " ] [ " RH " ] = payload [ ' RH ' ]
wr [ " fields " ] [ " DEW " ] = payload [ " DEW " ]
wr [ " fields " ] [ " AH " ] = payload [ " AH " ]
influxClient . write_points ( [ wr ] )
2020-01-07 07:25:22 +01:00
2020-10-06 23:22:11 +02:00
def getCacheSensor ( id , sensorConfig = None ) :
2020-12-16 02:32:32 +01:00
sensor = None
if ( id in cache ) and ( ( datetime . now ( ) - cache [ id ] [ " ts " ] ) . total_seconds ( ) < 180 ) :
sensor = cache [ id ] [ " payload " ]
if sensorConfig is not None :
if ( ' tMax ' in sensorConfig ) and ( sensor [ " T " ] > sensorConfig [ " tMax " ] ) :
sensor [ " tStatus " ] = " high "
if ( ' tMin ' in sensorConfig ) and ( sensor [ " T " ] < sensorConfig [ " tMin " ] ) :
sensor [ " tStatus " ] = " low "
if ( ' tStatus ' not in sensor ) and ( ' tMax ' in sensorConfig or ' tMin ' in sensorConfig ) :
sensor [ " tStatus " ] = " ok "
if ( ' RH ' in sensor ) :
outSensor = getCacheSensor ( sensorConfig [ " idOutside " ] ) if ' idOutside ' in sensorConfig else None
if ( outSensor is not None ) and ( ' AH ' in outSensor ) :
sensor [ " AHratio " ] = round ( ( outSensor [ " AH " ] / sensor [ " AH " ] - 1 ) * 100 )
sensor [ " RHvent " ] = round ( outSensor [ " DD " ] / sensor [ " SDD " ] * 100 )
if ( ' rhMax ' in sensorConfig ) and ( sensor [ " RH " ] > sensorConfig [ " rhMax " ] ) :
#too wet!
sensor [ " rhStatus " ] = " high "
if " AHratio " in sensor :
if sensor [ " AHratio " ] < = - 10 :
sensor [ " window " ] = " open "
elif sensor [ " AHratio " ] > = 10 :
sensor [ " window " ] = " close "
if ( ' rhMin ' in sensorConfig ) and ( sensor [ " RH " ] < sensorConfig [ " rhMin " ] ) :
#too dry
sensor [ " rhStatus " ] = " low "
if " AHratio " in sensor :
if sensor [ " AHratio " ] > = 10 :
sensor [ " window " ] = " open "
elif sensor [ " AHratio " ] < = - 10 :
sensor [ " window " ] = " close "
if ' rhStatus ' not in sensor and ( ' rhMin ' in sensorConfig or ' rhMax ' in sensorConfig ) :
sensor [ " rhStatus " ] = " ok "
return sensor
class MyHttpRequestHandler ( Handler ) :
def getjson ( self ) :
self . send_response ( 200 )
self . send_header ( ' Content-type ' , ' application/json ' )
self . end_headers ( )
resp = { " sensors " : [ ] }
idlist = [ ]
lock . acquire ( )
for csens in config [ " sensors " ] :
id = csens [ " id " ]
sensor = getCacheSensor ( id , csens )
if sensor is not None :
idlist . append ( id )
else :
sensor = { }
sensor [ " room " ] = csens [ " name " ]
2020-12-29 00:18:03 +01:00
sensor [ " id " ] = id
2020-12-16 02:32:32 +01:00
resp [ " sensors " ] . append ( sensor )
for id in cache :
if id in idlist :
continue
sensor = getCacheSensor ( id )
if sensor is not None :
resp [ " sensors " ] . append ( sensor )
lock . release ( )
self . wfile . write ( json . dumps ( resp ) . encode ( ) )
def do_GET ( self ) :
url = urlparse ( self . path )
p = url . path
q = parse_qs ( url . query )
if ' name ' in q :
name = q [ ' name ' ] [ 0 ]
if p == ' /data ' :
self . getjson ( )
elif p == ' / ' :
self . path = ' index.html '
return Handler . do_GET ( self )
elif p == ' /history ' and influxClient and name :
resp = influxClient . query (
" SELECT mean(T), mean(RH) FROM one_week.lacrosse WHERE (sensor = $name) AND time >= now() - 4h GROUP BY time(3m) fill(none) " ,
bind_params = { ' name ' : name }
)
self . send_response ( 200 )
self . send_header ( ' Content-type ' , ' application/json ' )
self . end_headers ( )
self . wfile . write ( json . dumps ( resp . raw [ ' series ' ] [ 0 ] [ ' values ' ] ) . encode ( ) )
else :
return self . send_error ( 404 , self . responses . get ( 404 ) [ 0 ] )
class Server ( socketserver . ThreadingMixIn , socketserver . TCPServer ) :
pass
2020-10-06 23:22:11 +02:00
2020-01-07 07:25:22 +01:00
cache = { }
2020-10-06 23:22:11 +02:00
server = Server ( ( ' 0.0.0.0 ' , 8080 ) , MyHttpRequestHandler )
server_thread = threading . Thread ( target = server . serve_forever )
server_thread . daemon = True
server_thread . start ( )
2020-12-29 00:18:03 +01:00
apisrv = apiserver . ApiServer ( 1990 )
2020-12-16 02:32:32 +01:00
print ( " Waiting for sensors... " )
2020-10-06 23:22:11 +02:00
2020-01-07 07:25:22 +01:00
while 1 :
2020-12-16 02:32:32 +01:00
rxObj = rfm . receive ( 7 )
try :
sensorObj = sensors . rawsensor . CreateSensor ( rxObj )
sensorData = sensorObj . GetData ( )
payload = { }
2020-12-29 00:18:03 +01:00
id = sensorData [ " ID " ]
payload [ " id " ] = id
2020-12-16 02:32:32 +01:00
T = sensorData [ " T " ] [ 0 ]
payload [ " T " ] = T
except :
continue
payload [ " rssi " ] = rxObj [ 1 ]
payload [ " afc " ] = rxObj [ 2 ]
payload [ " batlo " ] = sensorData [ ' batlo ' ]
payload [ " init " ] = sensorData [ " init " ]
lock . acquire ( )
for csens in config [ " sensors " ] :
2020-12-29 00:18:03 +01:00
if id == csens [ " id " ] :
2020-12-16 02:32:32 +01:00
payload [ " room " ] = csens [ " name " ]
break
if ' RH ' in sensorData :
RH = int ( sensorData [ ' RH ' ] [ 0 ] )
payload [ " RH " ] = RH
a = 7.5
b = 237.4
SDD = 6.1078 * 10 * * ( a * T / ( b + T ) )
DD = RH / 100.0 * SDD
v = math . log10 ( DD / 6.1078 )
payload [ " DEW " ] = round ( b * v / ( a - v ) , 1 )
payload [ " AH " ] = round ( 10 * * 5 * 18.016 / 8314.3 * DD / ( T + 273.15 ) , 1 )
payload [ " SDD " ] = SDD
payload [ " DD " ] = DD
DD = RH / 80.0 * SDD
v = math . log10 ( DD / 6.1078 )
payload [ " DEW80 " ] = round ( b * v / ( a - v ) , 1 )
DD = RH / 60.0 * SDD
v = math . log10 ( DD / 6.1078 )
payload [ " DEW60 " ] = round ( b * v / ( a - v ) , 1 )
2020-12-29 00:18:03 +01:00
apipayl = {
" decode " : [ {
" protocol " : " lacrosse " ,
" params " : payload ,
" class " : " weather "
} ]
}
apisrv . send ( apipayl )
if not id in cache :
cache [ id ] = { }
cache [ id ] [ " count " ] = 1
cache [ id ] [ " payload " ] = payload
cache [ id ] [ " payload " ] [ " tMin " ] = T
cache [ id ] [ " payload " ] [ " tMax " ] = T
2020-12-16 02:32:32 +01:00
else :
2020-12-29 00:18:03 +01:00
payload [ " tMin " ] = cache [ id ] [ " payload " ] [ " tMin " ]
payload [ " tMax " ] = cache [ id ] [ " payload " ] [ " tMax " ]
2020-12-16 02:32:32 +01:00
if payload [ " tMin " ] > T :
payload [ " tMin " ] = T
if payload [ " tMax " ] < T :
payload [ " tMax " ] = T
2020-12-29 00:18:03 +01:00
cache [ id ] [ " payload " ] = payload
2020-12-16 02:32:32 +01:00
2020-12-29 00:18:03 +01:00
cache [ id ] [ " ts " ] = datetime . now ( )
cache [ id ] [ " count " ] + = 1
2020-12-16 02:32:32 +01:00
2020-12-29 00:18:03 +01:00
line = u " id: {:2} " . format ( id )
2020-12-16 02:32:32 +01:00
line + = u ' room {:12} ' . format ( payload [ " room " ] [ : 12 ] if ( " room " in payload ) else " --- " )
line + = u ' T: {:5} \u00b0 C ' . format ( payload [ " T " ] )
if " RH " in payload :
line + = ' RH: {:2} % ' . format ( payload [ " RH " ] )
line + = " battery: " + ( " LOW " if payload [ " batlo " ] else " OK " )
line + = " init: " + ( " true " if payload [ " init " ] else " false " )
print ( ' ------------------------------------------------------------------------------ ' )
2020-12-29 00:18:03 +01:00
print ( line ) . encode ( " utf-8 " )
2020-12-16 02:32:32 +01:00
lock . release ( )
try :
if influxClient :
writeInflux ( payload )
except :
pass
try :
if mqttClient :
2020-12-29 00:18:03 +01:00
mqttClient . publish ( ' home/lacrosse/ ' + payload [ ' id ' ] , json . dumps ( payload ) )
2020-12-16 02:32:32 +01:00
except :
pass