jeelinklog.py 62 KB

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