jeelinklog.py 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215
  1. #!/usr/bin/python3 -u
  2. # -*- coding: utf-8 -*-
  3. #
  4. import serial
  5. import time
  6. from time import localtime, strftime
  7. from datetime import datetime
  8. import configparser
  9. from influxdb import InfluxDBClient
  10. import os
  11. import sys
  12. import paho.mqtt.client as mqtt
  13. import yaml
  14. import json
  15. import statistics
  16. import math
  17. from time import sleep
  18. #import math
  19. #import numpy as np
  20. #import httplib
  21. # Change working dir to the same dir as this script
  22. os.chdir(sys.path[0])
  23. # stop program if nothing is received from JeeLink for 5 min (will be restarted by systemd)
  24. serialReceived_maxAge = 300
  25. config = configparser.ConfigParser()
  26. config.read('jeelinklog.ini')
  27. serialport = config['jeelink'].get('serialport')
  28. serialbaud = int(config['jeelink'].get('baudrate'))
  29. if config['jeelink'].get('initTimeout') != None:
  30. serialInitTimeout = int(config['jeelink'].get('initTimeout'))
  31. else:
  32. serialInitTimeout = 2
  33. jeelinkInitCmd = config['jeelink'].get('initCmd')
  34. if jeelinkInitCmd == None:
  35. jeelinkInitCmd = "30t 3m v" # init cmd for TX29 AND TX35 sensors (17.241 and 9.579 kbps toggle every 30s)
  36. #jeelinkInitCmd = "0t 1m v" # init cmd for TX29 sensors only (17.241 kbps, no toggle)
  37. mqtt_topic_prefix = config['mqtt'].get('topic_prefix')
  38. topic_prefix_outside_temphum = config['mqtt'].get('topic_prefix_outside_temphum')
  39. mqtt_topic_atemp = config['mqtt'].get('topic_outside_temp')
  40. mqtt_topic_ahum = config['mqtt'].get('topic_outside_hum')
  41. mqtt_topic_ahum = config['mqtt'].get('topic_outside_dew')
  42. mqtt_topic_ahum = config['mqtt'].get('topic_outside_abshum')
  43. mqtt_topic_domoticz_in = "domoticz/in"
  44. mqtt_subtopic_notify = config['mqtt'].get('subtopic_notify')
  45. logfile_new_sensors = config['main'].get('logfile_new_sensors')
  46. path_new_sensors_folder = config['main'].get('path_new_sensors_folder')
  47. log_path = config['main'].get('log_path')
  48. if not os.path.exists(log_path):
  49. os.makedirs(log_path)
  50. verbosemode = False
  51. #sensors_conf_file = "/home/pi/jeelink_sensors.csv"
  52. sensordata_maxage = int(config['sensors'].get('data_maxage'))
  53. #minUpdateInterval = 60
  54. #aTempHumPublishInterval = 60
  55. #override_updateinterval_on_change = False
  56. #atemp_sensor_idx = 94
  57. #atemp_sensor_idx_2 = 113
  58. average_value_steps = int(config['sensors'].get('average_value_steps'))
  59. if average_value_steps == 0:
  60. average_value_steps = 1
  61. if len(sys.argv) > 1 and str(sys.argv[1]) == "-v":
  62. verbosemode = True
  63. def touch(fname, times=None):
  64. with open(fname, 'a'):
  65. os.utime(fname, times)
  66. def on_connect(client, userdata, flags, rc):
  67. if verbosemode:
  68. print("MQTT connected with result code " + str(rc) + "\n")
  69. #client.subscribe("wetter/atemp")
  70. def on_disconnect(client, userdata, rc):
  71. if rc != 0:
  72. print("Unexpected MQTT disconnection. Will auto-reconnect\n")
  73. #def on_message(client, userdata, msg):
  74. # #print(msg.topic + " " + str(msg.payload))
  75. # global atemp
  76. # atemp = msg.payload
  77. # dont edit below
  78. #starting values only..
  79. ##atemp = 61
  80. ##ahum = 101
  81. ##atemp1 = 61
  82. ##ahum1 = 101
  83. ##atemp2 = 61
  84. ##ahum2 = 101
  85. ##atemp_last = 61
  86. ##ahum_last = 101
  87. checkLastUpdateInterval = 60
  88. checkLastUpdateInterval_lastRun = 0
  89. lastPublishTime = 0
  90. lastStoreTime = 0
  91. sensors = dict()
  92. sensors_id_to_name = dict()
  93. #sensors_idx = dict()
  94. sensors_lastTemp = dict()
  95. sensors_lastHum = dict()
  96. sensors_lastUpdate = dict()
  97. sensors_lastReceivedValue_temp = dict()
  98. sensors_lastReceivedTime = dict()
  99. sensors_lastValues_temp = dict()
  100. sensors_lastValues_hum = dict()
  101. sensors_lastAvgValue_temp = dict()
  102. sensors_lastAvgValue_hum = dict()
  103. sensors_lastValues_lastIndex = dict()
  104. #sensors_unavailable = dict()
  105. sensors_batteryState = dict()
  106. sensors_new_alreadyNotified = dict()
  107. sensors_outside_sensors = []
  108. outSens_lastRun = 0
  109. outSens_store_lastRun = 0
  110. influx_default_fieldname_temperature = 'Temperature'
  111. influx_default_fieldname_humidity = 'Humidity'
  112. influx_default_fieldname_dewpoint = 'Dewpoint'
  113. influx_default_fieldname_abshum = 'AbsHumidity'
  114. influx_default_datatype_temperature = 'float'
  115. influx_default_datatype_humidity = 'int'
  116. influx_default_datatype_dewpoint = 'float'
  117. influx_default_datatype_abshum = 'float'
  118. sensors_yaml = yaml.load(open(config['main'].get('sensors_config_yml')), Loader=yaml.FullLoader)
  119. def list_duplicates(seq):
  120. seen = set()
  121. seen_add = seen.add
  122. # adds all elements it doesn't know yet to seen and all other to seen_twice
  123. seen_twice = set( x for x in seq if x in seen or seen_add(x) )
  124. # turn the set into a list (as requested)
  125. return list( seen_twice )
  126. # Dew point calculation based on formula and JS code found on: https://www.wetterochs.de/wetter/feuchte.html#f4
  127. # dew point from temperature, relative humidity
  128. # in °C
  129. def dewpoint(temp, relHum):
  130. if temp >= 0:
  131. a = 7.5
  132. b = 237.3
  133. else:
  134. a = 7.6
  135. b = 240.7
  136. c = math.log(vaporPressure(temp, relHum)/6.1078) * math.log10(math.e)
  137. return (b * c) / (a - c)
  138. def celsiusToKelvin(temp):
  139. return temp + 273.15
  140. def absoluteHumidity(relHum, temp):
  141. # AF = absolute Feuchte in g Wasserdampf pro m3 Luft
  142. # R* = 8314.3 J/(kmol*K) (universelle Gaskonstante)
  143. # mw = 18.016 kg/kmol (Molekulargewicht des Wasserdampfes)
  144. # r = relative Luftfeuchte
  145. # T = Temperatur in °C
  146. # TK = Temperatur in Kelvin (TK = T + 273.15)
  147. # TD = Taupunkttemperatur in °C
  148. #
  149. # mw/R* = 18.016 / 8314.3 = 0,0021668691290908
  150. #
  151. # AF(r,TK) = 10^5 * mw/R* * DD(T,r)/TK
  152. # unused 2nd formula: AF(TD,TK) = 10^5 * mw/R* * SDD(TD)/TK
  153. mw = 18.016
  154. R = 8314.3
  155. return 10**5 * mw/R * vaporPressure(temp, relHum) / celsiusToKelvin(temp)
  156. # Saettingungsdampfdruck / saturation vapour pressure
  157. # from temperature in hPa
  158. def saturationVaporPressure(temp):
  159. if temp >= 0:
  160. a = 7.5
  161. b = 237.3
  162. else:
  163. a = 7.6
  164. b = 240.7
  165. return 6.1078 * math.exp(((a*temp)/(b + temp))/ math.log10(math.e))
  166. # Dampfdruck / vapour pressure
  167. # from temperature, relative humidity in hPa
  168. def vaporPressure(temp, relHum):
  169. return relHum/100 * saturationVaporPressure(temp)
  170. if verbosemode:
  171. print("JeeLink2MQTT by Flo Kra")
  172. print("=======================================================================")
  173. print()
  174. print("JeeLink initCmd: " + jeelinkInitCmd)
  175. print()
  176. print()
  177. # load InfluxDB configuration
  178. influxdb_yaml = yaml.load(open(config['main'].get('influx_config_yml')), Loader=yaml.SafeLoader)
  179. if verbosemode:
  180. print("loading InfluxDB configuration...")
  181. print("InfluxDB Instances:")
  182. print(json.dumps(influxdb_yaml, indent=4))
  183. influxclient = dict()
  184. for instance in influxdb_yaml:
  185. i_host = influxdb_yaml[instance].get('host', None)
  186. i_port = int(influxdb_yaml[instance].get('port', 8086))
  187. i_username = influxdb_yaml[instance].get('username', None)
  188. i_password = influxdb_yaml[instance].get('password', None)
  189. i_database = influxdb_yaml[instance].get('database', None)
  190. if i_host != None and i_database != None:
  191. if i_username != None and i_password != None:
  192. influxclient[instance] = InfluxDBClient(i_host, i_port, i_username, i_password, i_database)
  193. else:
  194. influxclient[instance] = InfluxDBClient(i_host, i_port, i_database)
  195. if verbosemode:
  196. print()
  197. print()
  198. # load sensors configuration
  199. if verbosemode:
  200. print("loading sensors configuration: ")
  201. print(json.dumps(sensors_yaml, indent=3))
  202. print()
  203. for key in sensors_yaml:
  204. #print(key, '->', sensors_yaml[key])
  205. if verbosemode: print("Sensor name:", key)
  206. sensorName = key
  207. if sensors_yaml[key].get('LaCrosseID') >= 0:
  208. if verbosemode: print("LaCrosseID:", sensors_yaml[key].get('LaCrosseID'))
  209. sensorId = sensors_yaml[key].get('LaCrosseID')
  210. sensors_id_to_name[sensorId] = sensorName
  211. if sensors_yaml[key].get('DomoticzIdx'):
  212. if verbosemode: print("DomoticzIdx:", sensors_yaml[key].get('DomoticzIdx'))
  213. #sensors_idx[sensorId] = sensors_yaml[key].get('DomoticzIdx')
  214. if sensors_yaml[key].get('Topic_Temp'):
  215. if verbosemode: print("Topic_Temp:", sensors_yaml[key].get('Topic_Temp'))
  216. if sensors_yaml[key].get('Topic_Hum'):
  217. if verbosemode: print("Topic_Hum:", sensors_yaml[key].get('Topic_Hum'))
  218. if sensors_yaml[key].get('InfluxDB_Instance'):
  219. if verbosemode:
  220. print("InfluxDB_Instance:", sensors_yaml[key].get('InfluxDB_Instance'))
  221. if sensors_yaml[key].get('InfluxDB_Instance') not in influxdb_yaml:
  222. print("Error: invalid InfluxDB instance '" + sensors_yaml[key].get('InfluxDB_Instance') + "' configured for sensor '" + sensorName + "'")
  223. if sensors_yaml[key].get('isOutsideTempSensor'):
  224. if verbosemode: print("isOutsideTempSensor:", sensors_yaml[key].get('isOutsideTempSensor'))
  225. sensors_outside_sensors.append(sensorId)
  226. else:
  227. print("WARNING: Sensor " + key + " has no LaCrosseID!")
  228. if verbosemode:
  229. print()
  230. if verbosemode:
  231. print()
  232. print()
  233. #print(json.dumps(sensor))
  234. #print("sensors_id_to_name =",sensors_id_to_name)
  235. #print("outside sensors: ", sensors_outside_sensors)
  236. mqttc = mqtt.Client()
  237. mqttc.on_connect = on_connect
  238. mqttc.on_disconnect = on_disconnect
  239. ##mqttc.on_message = on_message
  240. if config['mqtt'].get('user') != "" and config['mqtt'].get('password') != "":
  241. mqttc.username_pw_set(config['mqtt'].get('user'), config['mqtt'].get('password'))
  242. mqttc.connect(config['mqtt'].get('server'), config['mqtt'].getint('port'), 60)
  243. mqttc.loop_start()
  244. #mqttc.loop_forever()
  245. ser = serial.Serial(port=serialport,
  246. baudrate = serialbaud,
  247. parity=serial.PARITY_NONE,
  248. stopbits=serial.STOPBITS_ONE,
  249. bytesize=serial.EIGHTBITS,
  250. timeout=1)
  251. checkLastUpdateInterval_lastRun = time.time() # first check after timeout expired
  252. # set serialReceivedLastTime to current time so that we can detect if nothing is received for some time
  253. serialReceivedLastTime = int(time.time())
  254. # init JeeLink device
  255. sleep(serialInitTimeout)
  256. ser.flushInput()
  257. ser.write(jeelinkInitCmd.encode('ascii')) # send init command to Jeelink
  258. #ser.write('v\r\n'.encode('ascii')) # get JeeLink version info
  259. sleep(0.2)
  260. try:
  261. while True:
  262. msg_was_sent = 0
  263. #clear serial buffer to remove junk and noise
  264. ###ser.flushInput()
  265. #read buffer until cr/lf
  266. serLine = ser.readline().strip()
  267. # catch exception on invalid char coming in: UnicodeDecodeError: 'ascii' codec can't decode byte 0xf4 in position 6: ordinal not in range(128)
  268. try:
  269. serLine = serLine.decode('ascii')
  270. except:
  271. serLine = False
  272. if(serLine):
  273. if serLine.find('LaCrosseITPlusReader') != -1:
  274. if verbosemode:
  275. print("Jeelink version/config: " + serLine)
  276. print()
  277. print()
  278. elif serLine.find('OK 9') != -1:
  279. # set serialReceivedLastTime to current time so that we can detect if nothing is received for some time
  280. serialReceivedLastTime = int(time.time())
  281. if verbosemode:
  282. print("RX: " + serLine + " => LaCrosse sensor")
  283. # uns interessieren nur reinkommende Zeilen die mit "OK 9 " beginnen
  284. # 0 1 2 3 4 5 6
  285. # OK 9 ID XXX XXX XXX XXX
  286. # | | | | | | |
  287. # | | | | | | --- Humidity incl. WeakBatteryFlag
  288. # | | | | | |------ Temp * 10 + 1000 LSB
  289. # | | | | |---------- Temp * 10 + 1000 MSB
  290. # | | | |-------------- Sensor type (1 or 2) +128 if NewBatteryFlag
  291. # | | |----------------- Sensor ID
  292. # | |------------------- fix "9"
  293. # |---------------------- fix "OK"
  294. serLineParts = serLine.split(' ')
  295. #print("Len: " + str(len(serLineParts)))
  296. if(len(serLineParts) == 7): # check correct size of incoming data
  297. #addr = serLineParts[2]
  298. #addr = "{0:x}".format(int(serLineParts[2]))
  299. #addr = hex((int(serLineParts[2])))
  300. addr = int(serLineParts[2])
  301. addrhex = "{0:x}".format(int(serLineParts[2]))
  302. lastUpdate = sensors_lastUpdate.get(addr, None)
  303. lastTemp = sensors_lastTemp.get(addr, None)
  304. lastHum = sensors_lastHum.get(addr, None)
  305. currentsensor_name = sensors_id_to_name.get(addr, None)
  306. # extract sensor data received from JeeLink
  307. if int(serLineParts[3]) >= 128:
  308. batt_new = 1
  309. type = int(serLineParts[3]) - 128
  310. else:
  311. batt_new = 0
  312. type = int(serLineParts[3])
  313. temp = (int(serLineParts[4])*256 + int(serLineParts[5]) - 1000)/10.0
  314. if int(serLineParts[6]) >= 128:
  315. batt_low = 1
  316. hum = int(serLineParts[6]) - 128
  317. else:
  318. batt_low = 0
  319. hum = int(serLineParts[6])
  320. if hum > 100:
  321. hum = 100
  322. if batt_new == 1:
  323. sensors_batteryState[addr] = 2
  324. elif batt_low == 1:
  325. sensors_batteryState[addr] = 1
  326. else:
  327. sensors_batteryState[addr] = 0
  328. lastValues_lastIndex = sensors_lastValues_lastIndex.get(addr, None)
  329. if sensors_lastValues_temp.get(addr, None) == None:
  330. sensors_lastValues_temp[addr] = [None] * average_value_steps
  331. if sensors_lastValues_hum.get(addr, None) == None:
  332. sensors_lastValues_hum[addr] = [None] * average_value_steps
  333. data_okay = False
  334. if sensors_lastReceivedValue_temp.get(addr, None) == None:
  335. # this is the first time we receive from that sensor in that session
  336. if verbosemode: print("first received from sensor",str(addr))
  337. sensors_lastReceivedValue_temp[addr] = temp
  338. sensors_lastReceivedTime[addr] = int(time.time())
  339. #lastValues_lastIndex = 0
  340. else:
  341. lastValue = sensors_lastReceivedValue_temp.get(addr, None)
  342. max_off_value = float(config['sensors'].get('max_off_value'))
  343. ignore_off_value_timeout = int(config['sensors'].get('ignore_off_value_timeout'))
  344. if lastValue != None and ((temp >= (lastValue - max_off_value) and temp <= (lastValue + max_off_value)) or ((int(time.time()) - sensors_lastReceivedTime.get(addr)) > ignore_off_value_timeout)): # discard off values
  345. sensors_lastReceivedValue_temp[addr] = temp
  346. sensors_lastReceivedTime[addr] = int(time.time())
  347. #print("Last Value=",lastValue,"currValue=",temp)
  348. data_okay = True
  349. else:
  350. if verbosemode: print("skipped sensor reading - Last Value=",lastValue,"currValue=",temp)
  351. if data_okay:
  352. if lastValues_lastIndex == None:
  353. lastValues_lastIndex = 0
  354. elif lastValues_lastIndex == (average_value_steps - 1):
  355. lastValues_lastIndex = 0
  356. else:
  357. lastValues_lastIndex += 1
  358. if verbosemode: print("lastValues_lastIndex =", lastValues_lastIndex)
  359. sensors_lastValues_lastIndex[addr] = lastValues_lastIndex
  360. sensors_lastValues_temp[addr][lastValues_lastIndex] = temp
  361. sensors_lastValues_hum[addr][lastValues_lastIndex] = hum
  362. sensors_lastUpdate[addr] = int(time.time())
  363. if verbosemode: print("sensors_lastValues_temp =", sensors_lastValues_temp[addr])
  364. if currentsensor_name is None:
  365. if batt_new == 1 and not sensors_new_alreadyNotified.get('addr', False):
  366. notifystr = "NEW sensor with ID " + str(addr)
  367. mqttc.publish(mqtt_topic_prefix+"/" + mqtt_subtopic_notify, notifystr, qos=0, retain=False)
  368. if not os.path.exists(log_path+'/new'):
  369. os.makedirs(log_path+'/new')
  370. fname = log_path + '/new/' + str(addr)
  371. if not os.path.exists(fname):
  372. try:
  373. touch(fname)
  374. except:
  375. pass
  376. try:
  377. f = open(log_path+'/'+logfile_new_sensors, 'a')
  378. f.write(str(datetime.now())+": "+notifystr+"\n")
  379. f.close()
  380. except:
  381. # guat dann hoit ned...
  382. pass
  383. sensors_new_alreadyNotified[addr] = True
  384. else:
  385. if not os.path.exists(log_path+'/unknown'):
  386. os.makedirs(log_path+'/unknown')
  387. fname = log_path + '/unknown/' + str(addr)
  388. if not os.path.exists(fname):
  389. try:
  390. touch(fname)
  391. except:
  392. pass
  393. if verbosemode:
  394. print("unknown sensor ID " + str(addr))
  395. if verbosemode:
  396. print("LaCrosse-ID: " + str(addr) + " = 0x" + str(addrhex) + " Type: " + str(type) + " Batt_New: " + str(batt_new) + " Batt_Low: " + str(batt_low) + " Temp: " + str(temp) + " Hum: " + str(hum) + " Name: " + str(currentsensor_name))
  397. print()
  398. #if senddata:
  399. # sensors_lastUpdate[str(addr)] = int(time.time())
  400. # sensors_lastTemp[str(addr)] = temp
  401. # sensors_lastHum[str(addr)] = hum
  402. #
  403. # isAtemp = False
  404. # if int(currentsensor_idx) == atemp_sensor_idx:
  405. # atemp1 = temp
  406. # ahum1 = hum
  407. # isAtemp = True
  408. # elif int(currentsensor_idx) == atemp_sensor_idx_2:
  409. # atemp2 = temp
  410. # ahum2 = hum
  411. # isAtemp = True
  412. #
  413. # if isAtemp:
  414. # if atemp1 <= atemp2:
  415. # atemp = atemp1
  416. # ahum = ahum1
  417. # else:
  418. # atemp = atemp2
  419. # ahum = ahum2
  420. #
  421. # if atemp < 61 and ahum < 101:
  422. # if atemp != atemp_last or ahum != ahum_last or ((time.time() - atemphum_lastUpdate) > aTempHumPublishInterval):
  423. # atemphum_lastUpdate = time.time()
  424. # atemp_last = atemp
  425. # ahum_last = ahum
  426. # mqttc.publish(mqtt_topic_atemp, str(atemp), qos=0, retain=True)
  427. # mqttc.publish(mqtt_topic_ahum, str(ahum), qos=0, retain=True)
  428. # mqttc.publish(mqtt_topic_atemphum_lastUpdate, strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=False)
  429. #
  430. # domoticz_json = "{\"idx\":" + str(currentsensor_idx) + ",\"nvalue\":0,\"svalue\":\"" + str(temp) + ";" + str(hum) + ";1\"}"
  431. # #if verbosemode:
  432. # # print(domoticz_json)
  433. # mqttc.publish(mqtt_topic_domoticz_in, domoticz_json, qos=0, retain=False)
  434. #
  435. # mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/temperature", str(temp), qos=0, retain=False)
  436. # mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/humidity", str(hum), qos=0, retain=False)
  437. # mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/battery", str(batterystate), qos=0, retain=False)
  438. # mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/lastUpdate", strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=False)
  439. # mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/availability", "available", qos=0, retain=False)
  440. #
  441. # lacrosse_json = "{\"temperature\":" + str(temp) + ", \"humidity\":" + str(hum) + ", \"battery\":\"" + str(batterystate) + "\"}"
  442. # mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/json", lacrosse_json, qos=0, retain=False)
  443. #
  444. # tmptext = str(temp) + "° " + str(hum) + "%"
  445. # mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/TempHumText", tmptext, qos=0, retain=False)
  446. #
  447. # if verbosemode:
  448. # print("MQTT published")
  449. #
  450. # try:
  451. # touch("/tmp/jeelink2mqtt_running")
  452. # except:
  453. # # guat dann ned...
  454. # pass
  455. #
  456. #else:
  457. # if verbosemode:
  458. # if currentsensor_name is None:
  459. # print("MQTT published")
  460. # else:
  461. # print("MQTT publishing surpressed (interval not expired)")
  462. #
  463. #
  464. #if verbosemode:
  465. # print("\n")
  466. else:
  467. if verbosemode: print("RX: " + serLine + " => ignored invalid data")
  468. pass
  469. else:
  470. if verbosemode:
  471. print("RX: " + serLine)
  472. pass
  473. # publish on MQTT on set interval
  474. publishNow = False
  475. if (int(time.time()) - lastPublishTime) > int(config['sensors'].get('publish_interval')):
  476. if lastPublishTime == 0:
  477. lastPublishTime = int(time.time())
  478. else:
  479. publishNow = True
  480. lastPublishTime = int(time.time())
  481. #for sensor in
  482. #sensors_lastReceivedTime[str(addr)]
  483. # store to InfluxDB on set interval
  484. storeNow = False
  485. if (int(time.time()) - lastStoreTime) > int(config['sensors'].get('store_interval')):
  486. if lastStoreTime == 0:
  487. lastStoreTime = int(time.time())
  488. else:
  489. storeNow = True
  490. lastStoreTime = int(time.time())
  491. if publishNow or storeNow:
  492. #print("available sensor data: ", sensors_lastUpdate)
  493. for id in sensors_lastUpdate:
  494. if (time.time() - sensors_lastUpdate[id]) < sensordata_maxage:
  495. s_name = sensors_id_to_name.get(id, None)
  496. if verbosemode: print("current data available for:", id, s_name)
  497. sensorDataComplete = True
  498. sum_temp=0
  499. for val in sensors_lastValues_temp[id]:
  500. if val is None:
  501. sensorDataComplete = False
  502. else:
  503. sum_temp = sum_temp + val
  504. sum_hum=0
  505. for val in sensors_lastValues_hum[id]:
  506. if val is None:
  507. sensorDataComplete = False
  508. else:
  509. sum_hum = sum_hum + val
  510. if sensorDataComplete:
  511. s_currAvgTemp = round(sum_temp / len(sensors_lastValues_temp[id]), 1)
  512. s_currAvgHum = int(sum_hum / len(sensors_lastValues_hum[id]))
  513. # calc dewpoint
  514. if config['sensors'].getboolean('calculate_dewpoint', False):
  515. s_currDewpoint = round(dewpoint(s_currAvgTemp, s_currAvgHum), 1)
  516. # calc absolute humidity
  517. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  518. s_currAbsHum = round(absoluteHumidity(s_currAvgHum, s_currAvgTemp), 2)
  519. sensors_lastAvgValue_temp[id] = s_currAvgTemp
  520. sensors_lastAvgValue_hum[id] = s_currAvgHum
  521. if verbosemode: print("s_currAvgTemp =", s_currAvgTemp, "s_currAvgHum =", s_currAvgHum)
  522. else:
  523. if verbosemode: print("s_currAvgTemp/s_currAvgHum: not yet enough readings available")
  524. if sensorDataComplete:
  525. # as Home Assistant MQTT binary sensor can only work with 2 states (unless using value templates):
  526. # -> removed "NEW" state string from /battery topic and added /batteryNew topic
  527. if sensors_batteryState.get(id,None) == 2:
  528. s_battNew = True
  529. s_battState = "OK"
  530. elif sensors_batteryState.get(id, None) == 1:
  531. s_battNew = False
  532. s_battState = "LOW"
  533. elif sensors_batteryState.get(id, None) == 0:
  534. s_battNew = False
  535. s_battState = "OK"
  536. if s_battNew:
  537. s_battNew_str = "YES"
  538. else:
  539. s_battNew_str = "NO"
  540. if s_name is not None:
  541. s_domIdx = sensors_yaml[s_name].get('DomoticzIdx', None)
  542. s_topic_temp = sensors_yaml[s_name].get('Topic_Temp', None)
  543. s_topic_hum = sensors_yaml[s_name].get('Topic_Hum', None)
  544. s_topic_dew = sensors_yaml[s_name].get('Topic_Dew', None)
  545. s_topic_absHum = sensors_yaml[s_name].get('Topic_AbsHum', None)
  546. s_influxInstance = sensors_yaml[s_name].get('InfluxDB_Instance', None)
  547. s_isOutsideTempSensor = sensors_yaml[s_name].get('isOutsideTempSensor', None)
  548. if s_domIdx is not None and publishNow:
  549. domoticz_json = "{\"idx\":" + str(s_domIdx) + ",\"nvalue\":0,\"svalue\":\"" + str(s_currAvgTemp) + ";" + str(s_currAvgHum) + ";1\"}"
  550. if verbosemode:
  551. print("Domoticz JSON:", domoticz_json)
  552. mqttc.publish(mqtt_topic_domoticz_in, domoticz_json, qos=0, retain=False)
  553. if s_topic_temp is not None and len(s_topic_temp)>5 and publishNow:
  554. if verbosemode: print("publishing temp on ", s_topic_temp)
  555. mqttc.publish(s_topic_temp, str(s_currAvgTemp), qos=0, retain=False)
  556. if s_topic_dew is not None and len(s_topic_dew)>5 and publishNow and config['sensors'].getboolean('calculate_dewpoint', False):
  557. if verbosemode: print("publishing dewpoint on ", s_topic_dew)
  558. mqttc.publish(s_topic_dew, str(s_currDewpoint), qos=0, retain=False)
  559. if s_topic_absHum is not None and len(s_topic_absHum)>5 and publishNow and config['sensors'].getboolean('calculate_absolute_humidity', False):
  560. if verbosemode: print("publishing absolute humidty on ", s_topic_absHum)
  561. mqttc.publish(s_topic_absHum, str(s_currAbsHum), qos=0, retain=False)
  562. if s_topic_hum is not None and len(s_topic_hum)>5 and publishNow:
  563. if verbosemode: print("publishing hum on ", s_topic_temp)
  564. mqttc.publish(s_topic_hum, str(s_currAvgHum), qos=0, retain=False)
  565. if publishNow:
  566. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/temperature", str(s_currAvgTemp), qos=0, retain=False)
  567. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/humidity", str(s_currAvgHum), qos=0, retain=False)
  568. if config['sensors'].getboolean('calculate_dewpoint', False):
  569. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/dewpoint", str(s_currDewpoint), qos=0, retain=False)
  570. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  571. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/absoluteHumidity", str(s_currAbsHum), qos=0, retain=False)
  572. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/battery", s_battState, qos=0, retain=False)
  573. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/batteryNew", s_battNew_str, qos=0, retain=False)
  574. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/availability", "available", qos=0, retain=False)
  575. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/lastUpdate", strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=False)
  576. # publish global lastUpdate for monitoring purposes
  577. mqttc.publish(mqtt_topic_prefix+"/lastUpdate", strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=False)
  578. # JSON output
  579. lacrosse_json = "{\"temperature\":" + str(s_currAvgTemp) \
  580. + ", \"humidity\":" + str(s_currAvgHum) \
  581. + ", \"battery\":\"" + str(s_battState) + "\""
  582. if config['sensors'].getboolean('calculate_dewpoint', False):
  583. lacrosse_json = lacrosse_json + ", \"dewpoint\":" + str(s_currDewpoint)
  584. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  585. lacrosse_json = lacrosse_json + ", \"absoluteHumidity\":" + str(s_currAbsHum)
  586. lacrosse_json = lacrosse_json + "}"
  587. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/json", lacrosse_json, qos=0, retain=False)
  588. tmptext = str(s_currAvgTemp) + "° " + str(s_currAvgHum) + "%"
  589. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/TempHumText", tmptext, qos=0, retain=False)
  590. if s_influxInstance is not None and storeNow:
  591. ### write to InfluxDB here
  592. if s_influxInstance in influxdb_yaml:
  593. influx_measurement = influxdb_yaml[s_influxInstance].get('measurement', None)
  594. t_utc = datetime.utcnow()
  595. t_str = t_utc.isoformat() + 'Z'
  596. if influx_measurement is not None:
  597. influx_fieldnames = influxdb_yaml[s_influxInstance].get('fieldnames', None)
  598. if influx_fieldnames == None:
  599. influx_fieldname_temperature = influx_default_fieldname_temperature
  600. influx_fieldname_humidity = influx_default_fieldname_humidity
  601. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  602. influx_fieldname_abshum = influx_default_fieldname_abshum
  603. else:
  604. if influxdb_yaml[s_influxInstance]['fieldnames'].get('temperature', None) != None:
  605. influx_fieldname_temperature = influxdb_yaml[s_influxInstance]['fieldnames'].get('temperature')
  606. else:
  607. influx_fieldname_temperature = influx_default_fieldname_temperature
  608. if influxdb_yaml[s_influxInstance]['fieldnames'].get('humidity', None) != None:
  609. influx_fieldname_humidity = influxdb_yaml[s_influxInstance]['fieldnames'].get('humidity')
  610. else:
  611. influx_fieldname_humidity = influx_default_fieldname_humidity
  612. if influxdb_yaml[s_influxInstance]['fieldnames'].get('dewpoint', None) != None:
  613. influx_fieldname_dewpoint = influxdb_yaml[s_influxInstance]['fieldnames'].get('dewpoint')
  614. else:
  615. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  616. if influxdb_yaml[s_influxInstance]['fieldnames'].get('abshum', None) != None:
  617. influx_fieldname_abshum = influxdb_yaml[s_influxInstance]['fieldnames'].get('abshum')
  618. else:
  619. influx_fieldname_abshum = influx_default_fieldname_abshum
  620. influx_datatypes = influxdb_yaml[s_influxInstance].get('datatypes', None)
  621. if influx_datatypes == None:
  622. influx_datatype_temperature = influx_default_datatype_temperature
  623. influx_datatype_humidity = influx_default_datatype_humidity
  624. else:
  625. if influxdb_yaml[s_influxInstance]['datatypes'].get('temperature', None) != None:
  626. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('temperature')
  627. if tmpdt == 'float' or tmpdt == 'int':
  628. influx_datatype_temperature = tmpdt
  629. else:
  630. influx_datatype_temperature = influx_default_datatype_temperature
  631. else:
  632. influx_datatype_temperature = influx_default_datatype_temperature
  633. if influxdb_yaml[s_influxInstance]['datatypes'].get('humidity', None) != None:
  634. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('humidity')
  635. if tmpdt == 'float' or tmpdt == 'int':
  636. influx_datatype_humidity = tmpdt
  637. else:
  638. influx_datatype_humidity = influx_default_datatype_humidity
  639. else:
  640. influx_datatype_humidity = influx_default_datatype_humidity
  641. if influxdb_yaml[s_influxInstance]['datatypes'].get('dewpoint', None) != None:
  642. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('dewpoint')
  643. if tmpdt == 'float' or tmpdt == 'int':
  644. influx_datatype_dewpoint = tmpdt
  645. else:
  646. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  647. else:
  648. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  649. if influxdb_yaml[s_influxInstance]['datatypes'].get('abshum', None) != None:
  650. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('abshum')
  651. if tmpdt == 'float' or tmpdt == 'int':
  652. influx_datatype_abshum = tmpdt
  653. else:
  654. influx_datatype_abshum = influx_default_datatype_abshum
  655. else:
  656. influx_datatype_abshum = influx_default_datatype_abshum
  657. if influx_datatype_temperature == 'int':
  658. influx_value_temp = int(s_currAvgTemp)
  659. else:
  660. influx_value_temp = float(s_currAvgTemp)
  661. if influx_datatype_humidity == 'int':
  662. influx_value_hum = int(s_currAvgHum)
  663. else:
  664. influx_value_hum = float(s_currAvgHum)
  665. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False):
  666. if influx_datatype_dewpoint == 'int':
  667. influx_value_dewpoint = int(s_currDewpoint)
  668. else:
  669. influx_value_dewpoint = float(s_currDewpoint)
  670. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False):
  671. if influx_datatype_abshum == 'int':
  672. influx_value_abshum = int(s_currAbsHum)
  673. else:
  674. influx_value_abshum = float(s_currAbsHum)
  675. influx_json = [
  676. {
  677. 'measurement': influx_measurement,
  678. 'tags': {
  679. 'sensor': s_name
  680. },
  681. 'time': t_str,
  682. 'fields': {
  683. influx_fieldname_temperature: influx_value_temp,
  684. influx_fieldname_humidity: influx_value_hum
  685. }
  686. }
  687. ]
  688. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False):
  689. influx_json[0]["fields"][influx_fieldname_dewpoint] = influx_value_dewpoint
  690. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False):
  691. influx_json[0]["fields"][influx_fieldname_abshum] = influx_value_abshum
  692. try:
  693. if verbosemode:
  694. print("write to InfluxDB...")
  695. print(influx_json)
  696. influxclient[s_influxInstance].write_points(influx_json)
  697. if verbosemode: print("DONE!")
  698. except Exception as e:
  699. print("Error writing to InfluxDB")
  700. print(influx_json)
  701. print(e)
  702. else:
  703. if verbosemode:
  704. print("Error: invalid InfluxDB instance '" + s_influxInstance + "' configured for sensor '" + s_name + "'")
  705. else: # this is an unknown sensor
  706. if publishNow:
  707. if s_battNew:
  708. if config['mqtt'].getboolean('publish_unknown_new_sensors'):
  709. mqttc.publish(mqtt_topic_prefix+"/NewUnknownSensor/"+str(id)+"/temperature", str(s_currAvgTemp), qos=0, retain=False)
  710. mqttc.publish(mqtt_topic_prefix+"/NewUnknownSensor/"+str(id)+"/humidity", str(s_currAvgHum), qos=0, retain=False)
  711. mqttc.publish(mqtt_topic_prefix+"/NewUnknownSensor/"+str(id)+"/batteryNew", s_battNew_str, qos=0, retain=False)
  712. else:
  713. if config['mqtt'].getboolean('publish_unknown_sensors'):
  714. mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(id)+"/temperature", str(s_currAvgTemp), qos=0, retain=False)
  715. mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(id)+"/humidity", str(s_currAvgHum), qos=0, retain=False)
  716. mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(id)+"/battery", s_battState, qos=0, retain=False)
  717. if verbosemode: print()
  718. # outside sensors
  719. if (int(time.time()) - outSens_lastRun) > int(config['sensors'].get('publish_interval_outside', 60)):
  720. outSens_lastRun = int(time.time())
  721. sum_out_sensors_temp = 0
  722. sum_out_sensors_temp_min = 100
  723. sum_out_sensors_temp_max = -50
  724. sum_out_sensors_hum = 0
  725. sum_out_sensors_hum_min = 100
  726. sum_out_sensors_hum_max = -50
  727. count_used_out_sensors = 0
  728. # "declare" variables with invalid high values - will be overwritten later
  729. out_temp_publishvalue = 200
  730. out_hum_publishvalue = 200
  731. out_dewpoint = 200
  732. out_abshum = 200
  733. # median
  734. if(config['sensors'].getboolean('outside_sensors_use_median')):
  735. out_sensors_temp_median_values = []
  736. out_sensors_hum_median_values = []
  737. for id in sensors_outside_sensors:
  738. lupd = sensors_lastUpdate.get(id, None)
  739. tdiff = 0
  740. if lupd is not None:
  741. tdiff = int(time.time()) - lupd
  742. if lupd is not None and (tdiff < sensordata_maxage):
  743. tmpval_t = sensors_lastAvgValue_temp.get(id, None)
  744. tmpval_h = sensors_lastAvgValue_hum.get(id, None)
  745. if tmpval_t is not None and tmpval_h is not None:
  746. sum_out_sensors_temp = sum_out_sensors_temp + tmpval_t
  747. sum_out_sensors_hum = sum_out_sensors_hum + tmpval_h
  748. if tmpval_t < sum_out_sensors_temp_min:
  749. sum_out_sensors_temp_min = tmpval_t
  750. if tmpval_t < sum_out_sensors_hum_min:
  751. sum_out_sensors_hum_min = tmpval_h
  752. if tmpval_t > sum_out_sensors_temp_max:
  753. sum_out_sensors_temp_max = tmpval_t
  754. if tmpval_t > sum_out_sensors_hum_max:
  755. sum_out_sensors_hum_max = tmpval_h
  756. # median
  757. if(config['sensors'].getboolean('outside_sensors_use_median')):
  758. out_sensors_temp_median_values.append(tmpval_t)
  759. out_sensors_hum_median_values.append(tmpval_h)
  760. count_used_out_sensors += 1
  761. lacrosse_json = None
  762. if count_used_out_sensors > 0:
  763. out_temp_avg = round((sum_out_sensors_temp / count_used_out_sensors), 1)
  764. out_hum_avg = int(round((sum_out_sensors_hum / count_used_out_sensors), 0))
  765. if(config['sensors'].getboolean('outside_sensors_use_median')):
  766. # median
  767. out_temp_median = round(statistics.median(out_sensors_temp_median_values), 1)
  768. out_hum_median = int(round(statistics.median(out_sensors_hum_median_values), 0))
  769. out_temp_publishvalue = out_temp_median
  770. out_hum_publishvalue = out_hum_median
  771. else:
  772. out_temp_publishvalue = out_temp_avg
  773. out_hum_publishvalue = out_hum_avg
  774. # calc dewpoint
  775. if config['sensors'].getboolean('calculate_dewpoint', False):
  776. out_dewpoint = round(dewpoint(out_temp_avg, out_hum_avg), 1)
  777. # calc absolute humidity
  778. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  779. out_abshum = round(absoluteHumidity(out_hum_avg, out_temp_avg), 2)
  780. mqttc.publish(mqtt_topic_atemp, str(out_temp_publishvalue), qos=0, retain=True)
  781. mqttc.publish(mqtt_topic_ahum, str(out_hum_publishvalue), qos=0, retain=True)
  782. mqttc.publish(topic_prefix_outside_temphum + '/temperature', str(out_temp_publishvalue), qos=0, retain=True)
  783. mqttc.publish(topic_prefix_outside_temphum + '/humidity', str(out_hum_publishvalue), qos=0, retain=True)
  784. if config['sensors'].getboolean('calculate_dewpoint', False):
  785. mqttc.publish(topic_prefix_outside_temphum + '/dewpoint', str(out_dewpoint), qos=0, retain=True)
  786. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  787. mqttc.publish(topic_prefix_outside_temphum + '/absoluteHumidity', str(out_abshum), qos=0, retain=True)
  788. mqttc.publish(topic_prefix_outside_temphum + '/temp_average', str(out_temp_avg), qos=0, retain=True)
  789. mqttc.publish(topic_prefix_outside_temphum + '/hum_average', str(out_hum_avg), qos=0, retain=True)
  790. if(config['sensors'].getboolean('outside_sensors_use_median')):
  791. mqttc.publish(topic_prefix_outside_temphum + '/temp_median', str(out_temp_median), qos=0, retain=True)
  792. mqttc.publish(topic_prefix_outside_temphum + '/hum_median', str(out_hum_median), qos=0, retain=True)
  793. mqttc.publish(topic_prefix_outside_temphum + '/lastUpdate', strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=True)
  794. tmptext = str(out_temp_publishvalue) + "° " + str(out_hum_publishvalue) + "%"
  795. mqttc.publish(topic_prefix_outside_temphum + "/TempHumText", tmptext, qos=0, retain=False)
  796. lacrosse_json = "{\"temperature\":" + str(out_temp_publishvalue) + \
  797. ", \"humidity\":" + str(out_hum_publishvalue) + "\"" \
  798. ", \"usedSensors\":" + str(count_used_out_sensors)
  799. min = 100
  800. max = 100
  801. if count_used_out_sensors > 1:
  802. mqttc.publish(topic_prefix_outside_temphum + '/usedSensors', str(count_used_out_sensors), qos=0, retain=True)
  803. lacrosse_json = lacrosse_json + \
  804. ", \"temp_average\":" + str(out_temp_avg) + \
  805. ", \"hum_average\":" + str(out_hum_avg) + \
  806. ", \"temp_median\":" + str(out_temp_median) + \
  807. ", \"hum_median\":" + str(out_temp_median)
  808. if sum_out_sensors_temp_min < 100:
  809. mqttc.publish(topic_prefix_outside_temphum + '/temp_min', str(sum_out_sensors_temp_min), qos=0, retain=False)
  810. lacrosse_json = lacrosse_json + ", \"temp_min\":" + str(sum_out_sensors_temp_min)
  811. if sum_out_sensors_temp_max > -50:
  812. mqttc.publish(topic_prefix_outside_temphum + '/temp_max', str(sum_out_sensors_temp_max), qos=0, retain=False)
  813. lacrosse_json = lacrosse_json + ", \"temp_max\":" + str(sum_out_sensors_temp_max)
  814. if sum_out_sensors_hum_min < 100:
  815. mqttc.publish(topic_prefix_outside_temphum + '/hum_min', str(sum_out_sensors_hum_min), qos=0, retain=False)
  816. lacrosse_json = lacrosse_json + ", \"hum_min\":" + str(sum_out_sensors_hum_min)
  817. if sum_out_sensors_hum_max > -50:
  818. mqttc.publish(topic_prefix_outside_temphum + '/hum_max', str(sum_out_sensors_hum_max), qos=0, retain=False)
  819. lacrosse_json = lacrosse_json + ", \"hum_max\":" + str(sum_out_sensors_hum_max)
  820. if lacrosse_json is not None:
  821. if config['sensors'].getboolean('calculate_dewpoint', False):
  822. lacrosse_json = lacrosse_json + ", \"dewpoint\":" + str(out_dewpoint)
  823. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  824. lacrosse_json = lacrosse_json + ", \"absoluteHumidity\":" + str(out_abshum)
  825. lacrosse_json = lacrosse_json + "}"
  826. mqttc.publish(topic_prefix_outside_temphum + "/json", lacrosse_json, qos=0, retain=False)
  827. # combined outside sensors to InfluxDB
  828. o_hasValidData = False
  829. if out_temp_publishvalue < 200 and out_hum_publishvalue < 200:
  830. o_hasValidData = True
  831. o_storeNow = False
  832. if (int(time.time()) - outSens_store_lastRun) > int(config['sensors'].get('store_interval_outside', 300)):
  833. if outSens_store_lastRun == 0:
  834. outSens_store_lastRun = int(time.time())
  835. else:
  836. o_storeNow = True
  837. outSens_store_lastRun = int(time.time())
  838. if o_storeNow and o_hasValidData:
  839. outSens_store_lastRun = int(time.time())
  840. o_influxInstance = config['sensors'].get('influxdb_instance_combined_outside_sensors')
  841. o_influxSensorName = config['sensors'].get('influxdb_sensorname_combined_outside_sensors', None)
  842. if o_influxInstance in influxdb_yaml and o_influxSensorName is not None:
  843. influx_measurement = influxdb_yaml[o_influxInstance].get('measurement', None)
  844. t_utc = datetime.utcnow()
  845. t_str = t_utc.isoformat() + 'Z'
  846. if influx_measurement is not None:
  847. influx_fieldnames = influxdb_yaml[o_influxInstance].get('fieldnames', None)
  848. if influx_fieldnames == None:
  849. influx_fieldname_temperature = influx_default_fieldname_temperature
  850. influx_fieldname_humidity = influx_default_fieldname_humidity
  851. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  852. influx_fieldname_abshum = influx_default_fieldname_abshum
  853. else:
  854. if influxdb_yaml[o_influxInstance]['fieldnames'].get('temperature', None) != None:
  855. influx_fieldname_temperature = influxdb_yaml[o_influxInstance]['fieldnames'].get('temperature')
  856. else:
  857. influx_fieldname_temperature = influx_default_fieldname_temperature
  858. if influxdb_yaml[o_influxInstance]['fieldnames'].get('humidity', None) != None:
  859. influx_fieldname_humidity = influxdb_yaml[o_influxInstance]['fieldnames'].get('humidity')
  860. else:
  861. influx_fieldname_humidity = influx_default_fieldname_humidity
  862. if influxdb_yaml[o_influxInstance]['fieldnames'].get('dewpoint', None) != None:
  863. influx_fieldname_dewpoint = influxdb_yaml[o_influxInstance]['fieldnames'].get('dewpoint')
  864. else:
  865. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  866. if influxdb_yaml[o_influxInstance]['fieldnames'].get('abshum', None) != None:
  867. influx_fieldname_abshum = influxdb_yaml[o_influxInstance]['fieldnames'].get('abshum')
  868. else:
  869. influx_fieldname_abshum = influx_default_fieldname_abshum
  870. influx_datatypes = influxdb_yaml[o_influxInstance].get('datatypes', None)
  871. if influx_datatypes == None:
  872. influx_datatype_temperature = influx_default_datatype_temperature
  873. influx_datatype_humidity = influx_default_datatype_humidity
  874. else:
  875. if influxdb_yaml[o_influxInstance]['datatypes'].get('temperature', None) != None:
  876. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('temperature')
  877. if tmpdt == 'float' or tmpdt == 'int':
  878. influx_datatype_temperature = tmpdt
  879. else:
  880. influx_datatype_temperature = influx_default_datatype_temperature
  881. else:
  882. influx_datatype_temperature = influx_default_datatype_temperature
  883. if influxdb_yaml[o_influxInstance]['datatypes'].get('humidity', None) != None:
  884. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('humidity')
  885. if tmpdt == 'float' or tmpdt == 'int':
  886. influx_datatype_humidity = tmpdt
  887. else:
  888. influx_datatype_humidity = influx_default_datatype_humidity
  889. else:
  890. influx_datatype_humidity = influx_default_datatype_humidity
  891. if influxdb_yaml[o_influxInstance]['datatypes'].get('dewpoint', None) != None:
  892. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('dewpoint')
  893. if tmpdt == 'float' or tmpdt == 'int':
  894. influx_datatype_dewpoint = tmpdt
  895. else:
  896. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  897. else:
  898. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  899. if influxdb_yaml[o_influxInstance]['datatypes'].get('abshum', None) != None:
  900. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('abshum')
  901. if tmpdt == 'float' or tmpdt == 'int':
  902. influx_datatype_abshum = tmpdt
  903. else:
  904. influx_datatype_abshum = influx_default_datatype_abshum
  905. else:
  906. influx_datatype_abshum = influx_default_datatype_abshum
  907. if influx_datatype_temperature == 'int':
  908. influx_value_temp = int(out_temp_publishvalue)
  909. else:
  910. influx_value_temp = float(out_temp_publishvalue)
  911. if influx_datatype_humidity == 'int':
  912. influx_value_hum = int(out_hum_publishvalue)
  913. else:
  914. influx_value_hum = float(out_hum_publishvalue)
  915. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False):
  916. if influx_datatype_dewpoint == 'int':
  917. influx_value_dewpoint = int(out_dewpoint)
  918. else:
  919. influx_value_dewpoint = float(out_dewpoint)
  920. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False):
  921. if influx_datatype_abshum == 'int':
  922. influx_value_abshum = int(out_abshum)
  923. else:
  924. influx_value_abshum = float(out_abshum)
  925. influx_json = [
  926. {
  927. 'measurement': influx_measurement,
  928. 'tags': {
  929. 'sensor': o_influxSensorName
  930. },
  931. 'time': t_str,
  932. 'fields': {
  933. influx_fieldname_temperature: influx_value_temp,
  934. influx_fieldname_humidity: influx_value_hum
  935. }
  936. }
  937. ]
  938. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False) and out_dewpoint < 200:
  939. influx_json[0]["fields"][influx_fieldname_dewpoint] = influx_value_dewpoint
  940. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False) and out_abshum < 200:
  941. influx_json[0]["fields"][influx_fieldname_abshum] = influx_value_abshum
  942. try:
  943. if verbosemode:
  944. print("write to InfluxDB...")
  945. print(influx_json)
  946. influxclient[o_influxInstance].write_points(influx_json)
  947. if verbosemode: print("DONE!")
  948. except Exception as e:
  949. print("Error writing to InfluxDB")
  950. print(influx_json)
  951. print(e)
  952. # handle outdated sensor values once a minute
  953. if (int(time.time()) - checkLastUpdateInterval_lastRun) > checkLastUpdateInterval:
  954. # exit program if nothing has been received from JeeLink for 5 min - will be restarted by systemd
  955. now = int(time.time())
  956. if (now - serialReceivedLastTime) > serialReceived_maxAge:
  957. print("Nothing received from JeeLink for " + str(now - serialReceivedLastTime) + "s - there is something wrong. Exiting program so that it will be restarted by systemd.")
  958. quit()
  959. checkLastUpdateInterval_lastRun = int(time.time())
  960. #print("check lastUpdate")
  961. for key in sensors_yaml:
  962. #print(key, '->', sensors_yaml[key])
  963. #print("Sensor name:", key)
  964. sensorId = sensors_yaml[key].get('LaCrosseID', None)
  965. if sensorId >= 0:
  966. lupd = sensors_lastUpdate.get(sensorId, None)
  967. tdiff = 0
  968. if lupd is not None:
  969. tdiff = int(time.time()) - lupd
  970. if lupd is None or (tdiff > sensordata_maxage):
  971. mqttc.publish(mqtt_topic_prefix+"/"+ key +"/availability", "unavailable", qos=0, retain=False)
  972. notifystr = "received no data from sensor '" + key + "' with ID " + str(sensorId) + " for " + str(tdiff) + "s"
  973. mqttc.publish(mqtt_topic_prefix+"/" + mqtt_subtopic_notify, notifystr, qos=0, retain=False)
  974. except KeyboardInterrupt:
  975. print('\n')
  976. exit()