jeelinklog.py 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166
  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'):
  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. # JSON output
  541. lacrosse_json = "{\"temperature\":" + str(s_currAvgTemp) \
  542. + ", \"humidity\":" + str(s_currAvgHum) \
  543. + ", \"battery\":\"" + str(s_battState) + "\""
  544. if config['sensors'].getboolean('calculate_dewpoint', False):
  545. lacrosse_json = lacrosse_json + ", \"dewpoint\":" + str(s_currDewpoint)
  546. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  547. lacrosse_json = lacrosse_json + ", \"absoluteHumidity\":" + str(s_currAbsHum)
  548. lacrosse_json = lacrosse_json + "}"
  549. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/json", lacrosse_json, qos=0, retain=False)
  550. tmptext = str(s_currAvgTemp) + "° " + str(s_currAvgHum) + "%"
  551. mqttc.publish(mqtt_topic_prefix+"/"+ s_name +"/TempHumText", tmptext, qos=0, retain=False)
  552. if s_influxInstance is not None and storeNow:
  553. ### write to InfluxDB here
  554. if s_influxInstance in influxdb_yaml:
  555. influx_measurement = influxdb_yaml[s_influxInstance].get('measurement', None)
  556. t_utc = datetime.utcnow()
  557. t_str = t_utc.isoformat() + 'Z'
  558. if influx_measurement is not None:
  559. influx_fieldnames = influxdb_yaml[s_influxInstance].get('fieldnames', None)
  560. if influx_fieldnames == None:
  561. influx_fieldname_temperature = influx_default_fieldname_temperature
  562. influx_fieldname_humidity = influx_default_fieldname_humidity
  563. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  564. influx_fieldname_abshum = influx_default_fieldname_abshum
  565. else:
  566. if influxdb_yaml[s_influxInstance]['fieldnames'].get('temperature', None) != None:
  567. influx_fieldname_temperature = influxdb_yaml[s_influxInstance]['fieldnames'].get('temperature')
  568. else:
  569. influx_fieldname_temperature = influx_default_fieldname_temperature
  570. if influxdb_yaml[s_influxInstance]['fieldnames'].get('humidity', None) != None:
  571. influx_fieldname_humidity = influxdb_yaml[s_influxInstance]['fieldnames'].get('humidity')
  572. else:
  573. influx_fieldname_humidity = influx_default_fieldname_humidity
  574. if influxdb_yaml[s_influxInstance]['fieldnames'].get('dewpoint', None) != None:
  575. influx_fieldname_dewpoint = influxdb_yaml[s_influxInstance]['fieldnames'].get('dewpoint')
  576. else:
  577. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  578. if influxdb_yaml[s_influxInstance]['fieldnames'].get('abshum', None) != None:
  579. influx_fieldname_abshum = influxdb_yaml[s_influxInstance]['fieldnames'].get('abshum')
  580. else:
  581. influx_fieldname_abshum = influx_default_fieldname_abshum
  582. influx_datatypes = influxdb_yaml[s_influxInstance].get('datatypes', None)
  583. if influx_datatypes == None:
  584. influx_datatype_temperature = influx_default_datatype_temperature
  585. influx_datatype_humidity = influx_default_datatype_humidity
  586. else:
  587. if influxdb_yaml[s_influxInstance]['datatypes'].get('temperature', None) != None:
  588. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('temperature')
  589. if tmpdt == 'float' or tmpdt == 'int':
  590. influx_datatype_temperature = tmpdt
  591. else:
  592. influx_datatype_temperature = influx_default_datatype_temperature
  593. else:
  594. influx_datatype_temperature = influx_default_datatype_temperature
  595. if influxdb_yaml[s_influxInstance]['datatypes'].get('humidity', None) != None:
  596. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('humidity')
  597. if tmpdt == 'float' or tmpdt == 'int':
  598. influx_datatype_humidity = tmpdt
  599. else:
  600. influx_datatype_humidity = influx_default_datatype_humidity
  601. else:
  602. influx_datatype_humidity = influx_default_datatype_humidity
  603. if influxdb_yaml[s_influxInstance]['datatypes'].get('dewpoint', None) != None:
  604. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('dewpoint')
  605. if tmpdt == 'float' or tmpdt == 'int':
  606. influx_datatype_dewpoint = tmpdt
  607. else:
  608. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  609. else:
  610. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  611. if influxdb_yaml[s_influxInstance]['datatypes'].get('abshum', None) != None:
  612. tmpdt = influxdb_yaml[s_influxInstance]['datatypes'].get('abshum')
  613. if tmpdt == 'float' or tmpdt == 'int':
  614. influx_datatype_abshum = tmpdt
  615. else:
  616. influx_datatype_abshum = influx_default_datatype_abshum
  617. else:
  618. influx_datatype_abshum = influx_default_datatype_abshum
  619. if influx_datatype_temperature == 'int':
  620. influx_value_temp = int(s_currAvgTemp)
  621. else:
  622. influx_value_temp = float(s_currAvgTemp)
  623. if influx_datatype_humidity == 'int':
  624. influx_value_hum = int(s_currAvgHum)
  625. else:
  626. influx_value_hum = float(s_currAvgHum)
  627. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False):
  628. if influx_datatype_dewpoint == 'int':
  629. influx_value_dewpoint = int(s_currDewpoint)
  630. else:
  631. influx_value_dewpoint = float(s_currDewpoint)
  632. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False):
  633. if influx_datatype_abshum == 'int':
  634. influx_value_abshum = int(s_currAbsHum)
  635. else:
  636. influx_value_abshum = float(s_currAbsHum)
  637. influx_json = [
  638. {
  639. 'measurement': influx_measurement,
  640. 'tags': {
  641. 'sensor': s_name
  642. },
  643. 'time': t_str,
  644. 'fields': {
  645. influx_fieldname_temperature: influx_value_temp,
  646. influx_fieldname_humidity: influx_value_hum
  647. }
  648. }
  649. ]
  650. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False):
  651. influx_json[0]["fields"][influx_fieldname_dewpoint] = influx_value_dewpoint
  652. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False):
  653. influx_json[0]["fields"][influx_fieldname_abshum] = influx_value_abshum
  654. try:
  655. if verbosemode:
  656. print("write to InfluxDB...")
  657. print(influx_json)
  658. influxclient[s_influxInstance].write_points(influx_json)
  659. if verbosemode: print("DONE!")
  660. except Exception as e:
  661. print("Error writing to InfluxDB")
  662. print(influx_json)
  663. print(e)
  664. else:
  665. if verbosemode:
  666. print("Error: invalid InfluxDB instance '" + s_influxInstance + "' configured for sensor '" + s_name + "'")
  667. else: # this is an unknown sensor
  668. if publishNow:
  669. if s_battNew:
  670. if config['mqtt'].getboolean('publish_unknown_new_sensors'):
  671. mqttc.publish(mqtt_topic_prefix+"/NewUnknownSensor/"+str(id)+"/temperature", str(s_currAvgTemp), qos=0, retain=False)
  672. mqttc.publish(mqtt_topic_prefix+"/NewUnknownSensor/"+str(id)+"/humidity", str(s_currAvgHum), qos=0, retain=False)
  673. mqttc.publish(mqtt_topic_prefix+"/NewUnknownSensor/"+str(id)+"/batteryNew", s_battNew_str, qos=0, retain=False)
  674. else:
  675. if config['mqtt'].getboolean('publish_unknown_sensors'):
  676. mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(id)+"/temperature", str(s_currAvgTemp), qos=0, retain=False)
  677. mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(id)+"/humidity", str(s_currAvgHum), qos=0, retain=False)
  678. mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(id)+"/battery", s_battState, qos=0, retain=False)
  679. if verbosemode: print()
  680. # outside sensors
  681. if (int(time.time()) - outSens_lastRun) > int(config['sensors'].get('publish_interval_outside', 60)):
  682. outSens_lastRun = int(time.time())
  683. sum_out_sensors_temp = 0
  684. sum_out_sensors_temp_min = 100
  685. sum_out_sensors_temp_max = -50
  686. sum_out_sensors_hum = 0
  687. sum_out_sensors_hum_min = 100
  688. sum_out_sensors_hum_max = -50
  689. count_used_out_sensors = 0
  690. # "declare" variables with invalid high values - will be overwritten later
  691. out_temp_publishvalue = 200
  692. out_hum_publishvalue = 200
  693. out_dewpoint = 200
  694. out_abshum = 200
  695. # median
  696. if(config['sensors'].getboolean('outside_sensors_use_median')):
  697. out_sensors_temp_median_values = []
  698. out_sensors_hum_median_values = []
  699. for id in sensors_outside_sensors:
  700. lupd = sensors_lastUpdate.get(id, None)
  701. tdiff = 0
  702. if lupd is not None:
  703. tdiff = int(time.time()) - lupd
  704. if lupd is not None and (tdiff < sensordata_maxage):
  705. tmpval_t = sensors_lastAvgValue_temp.get(id, None)
  706. tmpval_h = sensors_lastAvgValue_hum.get(id, None)
  707. if tmpval_t is not None and tmpval_h is not None:
  708. sum_out_sensors_temp = sum_out_sensors_temp + tmpval_t
  709. sum_out_sensors_hum = sum_out_sensors_hum + tmpval_h
  710. if tmpval_t < sum_out_sensors_temp_min:
  711. sum_out_sensors_temp_min = tmpval_t
  712. if tmpval_t < sum_out_sensors_hum_min:
  713. sum_out_sensors_hum_min = tmpval_h
  714. if tmpval_t > sum_out_sensors_temp_max:
  715. sum_out_sensors_temp_max = tmpval_t
  716. if tmpval_t > sum_out_sensors_hum_max:
  717. sum_out_sensors_hum_max = tmpval_h
  718. # median
  719. if(config['sensors'].getboolean('outside_sensors_use_median')):
  720. out_sensors_temp_median_values.append(tmpval_t)
  721. out_sensors_hum_median_values.append(tmpval_h)
  722. count_used_out_sensors += 1
  723. lacrosse_json = None
  724. if count_used_out_sensors > 0:
  725. out_temp_avg = round((sum_out_sensors_temp / count_used_out_sensors), 1)
  726. out_hum_avg = int(round((sum_out_sensors_hum / count_used_out_sensors), 0))
  727. if(config['sensors'].getboolean('outside_sensors_use_median')):
  728. # median
  729. out_temp_median = round(statistics.median(out_sensors_temp_median_values), 1)
  730. out_hum_median = int(round(statistics.median(out_sensors_hum_median_values), 0))
  731. out_temp_publishvalue = out_temp_median
  732. out_hum_publishvalue = out_hum_median
  733. else:
  734. out_temp_publishvalue = out_temp_avg
  735. out_hum_publishvalue = out_hum_avg
  736. # calc dewpoint
  737. if config['sensors'].getboolean('calculate_dewpoint', False):
  738. out_dewpoint = round(dewpoint(out_temp_avg, out_hum_avg), 1)
  739. # calc absolute humidity
  740. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  741. out_abshum = round(absoluteHumidity(out_hum_avg, out_temp_avg), 2)
  742. mqttc.publish(mqtt_topic_atemp, str(out_temp_publishvalue), qos=0, retain=True)
  743. mqttc.publish(mqtt_topic_ahum, str(out_hum_publishvalue), qos=0, retain=True)
  744. mqttc.publish(topic_prefix_outside_temphum + '/temperature', str(out_temp_publishvalue), qos=0, retain=True)
  745. mqttc.publish(topic_prefix_outside_temphum + '/humidity', str(out_hum_publishvalue), qos=0, retain=True)
  746. if config['sensors'].getboolean('calculate_dewpoint', False):
  747. mqttc.publish(topic_prefix_outside_temphum + '/dewpoint', str(out_dewpoint), qos=0, retain=True)
  748. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  749. mqttc.publish(topic_prefix_outside_temphum + '/absoluteHumidity', str(out_abshum), qos=0, retain=True)
  750. mqttc.publish(topic_prefix_outside_temphum + '/temp_average', str(out_temp_avg), qos=0, retain=True)
  751. mqttc.publish(topic_prefix_outside_temphum + '/hum_average', str(out_hum_avg), qos=0, retain=True)
  752. if(config['sensors'].getboolean('outside_sensors_use_median')):
  753. mqttc.publish(topic_prefix_outside_temphum + '/temp_median', str(out_temp_median), qos=0, retain=True)
  754. mqttc.publish(topic_prefix_outside_temphum + '/hum_median', str(out_hum_median), qos=0, retain=True)
  755. mqttc.publish(topic_prefix_outside_temphum + '/lastUpdate', strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=True)
  756. tmptext = str(out_temp_publishvalue) + "° " + str(out_hum_publishvalue) + "%"
  757. mqttc.publish(topic_prefix_outside_temphum + "/TempHumText", tmptext, qos=0, retain=False)
  758. lacrosse_json = "{\"temperature\":" + str(out_temp_publishvalue) + \
  759. ", \"humidity\":" + str(out_hum_publishvalue) + "\"" \
  760. ", \"usedSensors\":" + str(count_used_out_sensors)
  761. min = 100
  762. max = 100
  763. if count_used_out_sensors > 1:
  764. mqttc.publish(topic_prefix_outside_temphum + '/usedSensors', str(count_used_out_sensors), qos=0, retain=True)
  765. lacrosse_json = lacrosse_json + \
  766. ", \"temp_average\":" + str(out_temp_avg) + \
  767. ", \"hum_average\":" + str(out_hum_avg) + \
  768. ", \"temp_median\":" + str(out_temp_median) + \
  769. ", \"hum_median\":" + str(out_temp_median)
  770. if sum_out_sensors_temp_min < 100:
  771. mqttc.publish(topic_prefix_outside_temphum + '/temp_min', str(sum_out_sensors_temp_min), qos=0, retain=False)
  772. lacrosse_json = lacrosse_json + ", \"temp_min\":" + str(sum_out_sensors_temp_min)
  773. if sum_out_sensors_temp_max > -50:
  774. mqttc.publish(topic_prefix_outside_temphum + '/temp_max', str(sum_out_sensors_temp_max), qos=0, retain=False)
  775. lacrosse_json = lacrosse_json + ", \"temp_max\":" + str(sum_out_sensors_temp_max)
  776. if sum_out_sensors_hum_min < 100:
  777. mqttc.publish(topic_prefix_outside_temphum + '/hum_min', str(sum_out_sensors_hum_min), qos=0, retain=False)
  778. lacrosse_json = lacrosse_json + ", \"hum_min\":" + str(sum_out_sensors_hum_min)
  779. if sum_out_sensors_hum_max > -50:
  780. mqttc.publish(topic_prefix_outside_temphum + '/hum_max', str(sum_out_sensors_hum_max), qos=0, retain=False)
  781. lacrosse_json = lacrosse_json + ", \"hum_max\":" + str(sum_out_sensors_hum_max)
  782. if lacrosse_json is not None:
  783. if config['sensors'].getboolean('calculate_dewpoint', False):
  784. lacrosse_json = lacrosse_json + ", \"dewpoint\":" + str(out_dewpoint)
  785. if config['sensors'].getboolean('calculate_absolute_humidity', False):
  786. lacrosse_json = lacrosse_json + ", \"absoluteHumidity\":" + str(out_abshum)
  787. lacrosse_json = lacrosse_json + "}"
  788. mqttc.publish(topic_prefix_outside_temphum + "/json", lacrosse_json, qos=0, retain=False)
  789. # combined outside sensors to InfluxDB
  790. o_hasValidData = False
  791. if out_temp_publishvalue < 200 and out_hum_publishvalue < 200:
  792. o_hasValidData = True
  793. o_storeNow = False
  794. if (int(time.time()) - outSens_store_lastRun) > int(config['sensors'].get('store_interval_outside', 300)):
  795. if outSens_store_lastRun == 0:
  796. outSens_store_lastRun = int(time.time())
  797. else:
  798. o_storeNow = True
  799. outSens_store_lastRun = int(time.time())
  800. if o_storeNow and o_hasValidData:
  801. outSens_store_lastRun = int(time.time())
  802. o_influxInstance = config['sensors'].get('influxdb_instance_combined_outside_sensors')
  803. o_influxSensorName = config['sensors'].get('influxdb_sensorname_combined_outside_sensors', None)
  804. if o_influxInstance in influxdb_yaml and o_influxSensorName is not None:
  805. influx_measurement = influxdb_yaml[o_influxInstance].get('measurement', None)
  806. t_utc = datetime.utcnow()
  807. t_str = t_utc.isoformat() + 'Z'
  808. if influx_measurement is not None:
  809. influx_fieldnames = influxdb_yaml[o_influxInstance].get('fieldnames', None)
  810. if influx_fieldnames == None:
  811. influx_fieldname_temperature = influx_default_fieldname_temperature
  812. influx_fieldname_humidity = influx_default_fieldname_humidity
  813. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  814. influx_fieldname_abshum = influx_default_fieldname_abshum
  815. else:
  816. if influxdb_yaml[o_influxInstance]['fieldnames'].get('temperature', None) != None:
  817. influx_fieldname_temperature = influxdb_yaml[o_influxInstance]['fieldnames'].get('temperature')
  818. else:
  819. influx_fieldname_temperature = influx_default_fieldname_temperature
  820. if influxdb_yaml[o_influxInstance]['fieldnames'].get('humidity', None) != None:
  821. influx_fieldname_humidity = influxdb_yaml[o_influxInstance]['fieldnames'].get('humidity')
  822. else:
  823. influx_fieldname_humidity = influx_default_fieldname_humidity
  824. if influxdb_yaml[o_influxInstance]['fieldnames'].get('dewpoint', None) != None:
  825. influx_fieldname_dewpoint = influxdb_yaml[o_influxInstance]['fieldnames'].get('dewpoint')
  826. else:
  827. influx_fieldname_dewpoint = influx_default_fieldname_dewpoint
  828. if influxdb_yaml[o_influxInstance]['fieldnames'].get('abshum', None) != None:
  829. influx_fieldname_abshum = influxdb_yaml[o_influxInstance]['fieldnames'].get('abshum')
  830. else:
  831. influx_fieldname_abshum = influx_default_fieldname_abshum
  832. influx_datatypes = influxdb_yaml[o_influxInstance].get('datatypes', None)
  833. if influx_datatypes == None:
  834. influx_datatype_temperature = influx_default_datatype_temperature
  835. influx_datatype_humidity = influx_default_datatype_humidity
  836. else:
  837. if influxdb_yaml[o_influxInstance]['datatypes'].get('temperature', None) != None:
  838. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('temperature')
  839. if tmpdt == 'float' or tmpdt == 'int':
  840. influx_datatype_temperature = tmpdt
  841. else:
  842. influx_datatype_temperature = influx_default_datatype_temperature
  843. else:
  844. influx_datatype_temperature = influx_default_datatype_temperature
  845. if influxdb_yaml[o_influxInstance]['datatypes'].get('humidity', None) != None:
  846. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('humidity')
  847. if tmpdt == 'float' or tmpdt == 'int':
  848. influx_datatype_humidity = tmpdt
  849. else:
  850. influx_datatype_humidity = influx_default_datatype_humidity
  851. else:
  852. influx_datatype_humidity = influx_default_datatype_humidity
  853. if influxdb_yaml[o_influxInstance]['datatypes'].get('dewpoint', None) != None:
  854. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('dewpoint')
  855. if tmpdt == 'float' or tmpdt == 'int':
  856. influx_datatype_dewpoint = tmpdt
  857. else:
  858. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  859. else:
  860. influx_datatype_dewpoint = influx_default_datatype_dewpoint
  861. if influxdb_yaml[o_influxInstance]['datatypes'].get('abshum', None) != None:
  862. tmpdt = influxdb_yaml[o_influxInstance]['datatypes'].get('abshum')
  863. if tmpdt == 'float' or tmpdt == 'int':
  864. influx_datatype_abshum = tmpdt
  865. else:
  866. influx_datatype_abshum = influx_default_datatype_abshum
  867. else:
  868. influx_datatype_abshum = influx_default_datatype_abshum
  869. if influx_datatype_temperature == 'int':
  870. influx_value_temp = int(out_temp_publishvalue)
  871. else:
  872. influx_value_temp = float(out_temp_publishvalue)
  873. if influx_datatype_humidity == 'int':
  874. influx_value_hum = int(out_hum_publishvalue)
  875. else:
  876. influx_value_hum = float(out_hum_publishvalue)
  877. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False):
  878. if influx_datatype_dewpoint == 'int':
  879. influx_value_dewpoint = int(out_dewpoint)
  880. else:
  881. influx_value_dewpoint = float(out_dewpoint)
  882. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False):
  883. if influx_datatype_abshum == 'int':
  884. influx_value_abshum = int(out_abshum)
  885. else:
  886. influx_value_abshum = float(out_abshum)
  887. influx_json = [
  888. {
  889. 'measurement': influx_measurement,
  890. 'tags': {
  891. 'sensor': o_influxSensorName
  892. },
  893. 'time': t_str,
  894. 'fields': {
  895. influx_fieldname_temperature: influx_value_temp,
  896. influx_fieldname_humidity: influx_value_hum
  897. }
  898. }
  899. ]
  900. if config['sensors'].getboolean('calculate_dewpoint', False) and config['sensors'].getboolean('write_dewpoint_to_influxdb', False) and out_dewpoint < 200:
  901. influx_json[0]["fields"][influx_fieldname_dewpoint] = influx_value_dewpoint
  902. if config['sensors'].getboolean('calculate_absolute_humidity', False) and config['sensors'].getboolean('write_abshum_to_influxdb', False) and out_abshum < 200:
  903. influx_json[0]["fields"][influx_fieldname_abshum] = influx_value_abshum
  904. try:
  905. if verbosemode:
  906. print("write to InfluxDB...")
  907. print(influx_json)
  908. influxclient[o_influxInstance].write_points(influx_json)
  909. if verbosemode: print("DONE!")
  910. except Exception as e:
  911. print("Error writing to InfluxDB")
  912. print(influx_json)
  913. print(e)
  914. # handle outdated sensor values once a minute
  915. if (int(time.time()) - checkLastUpdateInterval_lastRun) > checkLastUpdateInterval:
  916. # exit program if nothing has been received from JeeLink for 5 min - will be restarted by systemd
  917. now = int(time.time())
  918. if (now - serialReceivedLastTime) > serialReceived_maxAge:
  919. print("Nothing received from JeeLink for " + str(now - serialReceivedLastTime) + "s - there is something wrong. Exiting program so that it will be restarted by systemd.")
  920. quit()
  921. checkLastUpdateInterval_lastRun = int(time.time())
  922. #print("check lastUpdate")
  923. for key in sensors_yaml:
  924. #print(key, '->', sensors_yaml[key])
  925. #print("Sensor name:", key)
  926. sensorId = sensors_yaml[key].get('LaCrosseID', None)
  927. if sensorId is not None:
  928. lupd = sensors_lastUpdate.get(sensorId, None)
  929. tdiff = 0
  930. if lupd is not None:
  931. tdiff = int(time.time()) - lupd
  932. if lupd is None or (tdiff > sensordata_maxage):
  933. mqttc.publish(mqtt_topic_prefix+"/"+ key +"/availability", "unavailable", qos=0, retain=False)
  934. notifystr = "received no data from sensor '" + key + "' with ID " + str(sensorId) + " for " + str(tdiff) + "s"
  935. mqttc.publish(mqtt_topic_prefix+"/" + mqtt_subtopic_notify, notifystr, qos=0, retain=False)
  936. except KeyboardInterrupt:
  937. print('\n')
  938. exit()