jeelinklog.py 63 KB

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