#!/usr/bin/python3 -u # -*- coding: utf-8 -*- # import serial import time from time import localtime, strftime import os import sys import paho.mqtt.client as mqtt #import json #import math #import numpy as np #import httplib serialport = '/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AL01MYTF-if00-port0' serialbaud = 57600 mqtt_server = "mqtt.lan" mqtt_port = 1883 mqtt_user = "" mqtt_password = "" mqtt_topic_prefix = "LaCrosse" mqtt_topic_atemp = "wetter/atemp" mqtt_topic_ahum = "wetter/ahum" mqtt_topic_atemphum_lastUpdate = "wetter/atemphum_lastUpdate" mqtt_topic_domoticz_in = "domoticz/in" verbosemode = False sensors_conf_file = "/home/pi/jeelink_sensors.csv" sensordata_maxage = 300 minUpdateInterval = 60 aTempHumPublishInterval = 60 override_updateinterval_on_change = False atemp_sensor_idx = 94 atemp_sensor_idx_2 = 113 if len(sys.argv) > 1 and str(sys.argv[1]) == "-v": verbosemode = True def touch(fname, times=None): with open(fname, 'a'): os.utime(fname, times) def on_connect(client, userdata, flags, rc): if verbosemode: print("MQTT connected with result code " + str(rc) + "\n") #client.subscribe("wetter/atemp") def on_disconnect(client, userdata, rc): if rc != 0: print("Unexpected MQTT disconnection. Will auto-reconnect\n") #def on_message(client, userdata, msg): # #print(msg.topic + " " + str(msg.payload)) # global atemp # atemp = msg.payload # dont edit below #starting values only.. atemp = 61 ahum = 101 atemp1 = 61 ahum1 = 101 atemp2 = 61 ahum2 = 101 atemp_last = 61 ahum_last = 101 checkLastUpdateInterval = 60 checkLastUpdateInterval_lastRun = 0 sensors = {} sensors_idx = {} sensors_lastTemp = {} sensors_lastHum = {} sensors_lastUpdate = {} sensors_unavailable = {} if verbosemode: print("JeeLink2MQTT by Flo Kra") print("=======================================================================") print("loading sensors assignment: ") with open(sensors_conf_file, "r") as sensorscsv: for line in sensorscsv: if line.find('ID,DomoticzIdx,Name') == -1: # csv file header filtern line = line.strip('\r') line = line.strip('\n') parts = line.split(',') sensorId = parts[0] domoticzIdx = parts[1] sensorName = parts[2] sensors[str(sensorId)] = str(sensorName) sensors_idx[str(sensorId)] = str(domoticzIdx) sensors_lastUpdate[str(sensorId)] = 0 sensors_unavailable[str(sensorId)] = 1 #will be overwritten when first value is received if verbosemode: idhex = "{0:x}".format(int(sensorId)) print("Sensor " + sensorId + " = 0x" + str(idhex) + ", Idx = " + str(domoticzIdx) + ", Name = '" + sensorName + "'") if verbosemode: print("\n") mqttc = mqtt.Client() mqttc.on_connect = on_connect mqttc.on_disconnect = on_disconnect ##mqttc.on_message = on_message if mqtt_user != "" and mqtt_password != "": mqttc.username_pw_set(mqtt_user, mqtt_password) mqttc.connect(mqtt_server, mqtt_port, 60) mqttc.loop_start() #mqttc.loop_forever() ser = serial.Serial(port=serialport, baudrate = serialbaud, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1) #sensors = {'4':'Arbeitszimmer','16':'AussenGarten','60':'AussenParkplatz','50':'Bad','39':'Balkon','55':'Kueche','40':'Schlafzimmer'} #sensors_idx = {'4':'1','16':'94','60':'113','50':'4','39':'88','55':'6','40':'3'} #if verbosemode: # print(sensors) # print(sensors_idx) checkLastUpdateInterval_lastRun = time.time() # first check after 1 min try: while True: msg_was_sent = 0 #clear serial buffer to remove junk and noise ser.flushInput() #read buffer until cr/lf serLine = ser.readline().strip() # catch exception on invalid char coming in: UnicodeDecodeError: 'ascii' codec can't decode byte 0xf4 in position 6: ordinal not in range(128) try: serLine = serLine.decode('ascii') except: serLine = "" if(serLine): if serLine.find('OK 9') != -1: if verbosemode: print(serLine + " = LaCrosse sensor") # uns interessieren nur reinkommende Zeilen die mit "OK 9 " beginnen # 0 1 2 3 4 5 6 # OK 9 ID XXX XXX XXX XXX # | | | | | | | # | | | | | | --- Humidity incl. WeakBatteryFlag # | | | | | |------ Temp * 10 + 1000 LSB # | | | | |---------- Temp * 10 + 1000 MSB # | | | |-------------- Sensor type (1 or 2) +128 if NewBatteryFlag # | | |----------------- Sensor ID # | |------------------- fix "9" # |---------------------- fix "OK" serLineParts = serLine.split(' ') #addr = serLineParts[2] #addr = "{0:x}".format(int(serLineParts[2])) #addr = hex((int(serLineParts[2]))) addr = int(serLineParts[2]) addrhex = "{0:x}".format(int(serLineParts[2])) lastUpdate = sensors_lastUpdate.get(str(addr), None) lastTemp = sensors_lastTemp.get(str(addr), None) lastHum = sensors_lastHum.get(str(addr), None) currentsensor_idx = sensors_idx.get(str(addr),None) currentsensor_name = sensors.get(str(addr), None) if int(serLineParts[3]) >= 128: batt_new = 1 type = int(serLineParts[3]) - 128 else: batt_new = 0 type = int(serLineParts[3]) temp = (int(serLineParts[4])*256 + int(serLineParts[5]) - 1000)/10.0 if int(serLineParts[6]) >= 128: batt_low = 1 hum = int(serLineParts[6]) - 128 else: batt_low = 0 hum = int(serLineParts[6]) if hum > 100: hum = 100 if batt_low == 0: batterystate = "ok" else: batterystate = "low" senddata = False if currentsensor_idx is not None: if override_updateinterval_on_change: if lastTemp != temp or lastHum != hum: senddata = True if verbosemode: print("override interval (value changed): " + str(temp) + " != " + str(lastTemp) + " " + str(hum) + " != " + str(lastHum)) if lastUpdate is not None: timediff = int(time.time()) - lastUpdate if timediff >= minUpdateInterval: senddata = True elif sensors_unavailable[str(addr)] == 1: senddata = True else: senddata = True sensors_unavailable[str(addr)] = 0 #print(sensors_unavailable) if currentsensor_name is None: if batt_new == 1: fname = '/home/pi/logs/jeelink_unknown_new_sensor_' + str(addr) else: fname = '/home/pi/logs/jeelink_unknown_sensor_' + str(addr) if not os.path.isfile(fname): try: touch(fname) except: # guat dann hoit ned... pass temp = (int(serLineParts[4])*256 + int(serLineParts[5]) - 1000)/10.0 if verbosemode: print("unknown sensor ID " + str(addr)) mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(addr)+"/temperature", str(temp), qos=0, retain=False) mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(addr)+"/humidity", str(hum), qos=0, retain=False) mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(addr)+"/battNew", str(batt_new), qos=0, retain=False) if verbosemode: print("addr: " + str(addr) + " = 0x" + str(addrhex) + " batt_new: " + str(batt_new) + " type: " + str(type) + " batt_low: " + str(batt_low) + " temp: " + str(temp) + " hum: " + str(hum) + " Name: " + str(currentsensor_name)) if senddata: sensors_lastUpdate[str(addr)] = int(time.time()) sensors_lastTemp[str(addr)] = temp sensors_lastHum[str(addr)] = hum isAtemp = False if int(currentsensor_idx) == atemp_sensor_idx: atemp1 = temp ahum1 = hum isAtemp = True elif int(currentsensor_idx) == atemp_sensor_idx_2: atemp2 = temp ahum2 = hum isAtemp = True if isAtemp: if atemp1 <= atemp2: atemp = atemp1 ahum = ahum1 else: atemp = atemp2 ahum = ahum2 if atemp < 61 and ahum < 101: if atemp != atemp_last or ahum != ahum_last or ((time.time() - atemphum_lastUpdate) > aTempHumPublishInterval): atemphum_lastUpdate = time.time() atemp_last = atemp ahum_last = ahum mqttc.publish(mqtt_topic_atemp, str(atemp), qos=0, retain=True) mqttc.publish(mqtt_topic_ahum, str(ahum), qos=0, retain=True) mqttc.publish(mqtt_topic_atemphum_lastUpdate, strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=False) domoticz_json = "{\"idx\":" + str(currentsensor_idx) + ",\"nvalue\":0,\"svalue\":\"" + str(temp) + ";" + str(hum) + ";1\"}" #if verbosemode: # print(domoticz_json) mqttc.publish(mqtt_topic_domoticz_in, domoticz_json, qos=0, retain=False) mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/temperature", str(temp), qos=0, retain=False) mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/humidity", str(hum), qos=0, retain=False) mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/battery", str(batterystate), qos=0, retain=False) mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/lastUpdate", strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=0, retain=False) mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/availability", "available", qos=0, retain=False) lacrosse_json = "{\"temperature\":" + str(temp) + ", \"humidity\":" + str(hum) + ", \"battery\":\"" + str(batterystate) + "\"}" mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/json", lacrosse_json, qos=0, retain=False) tmptext = str(temp) + "° " + str(hum) + "%" mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/TempHumText", tmptext, qos=0, retain=False) if verbosemode: print("MQTT published") try: touch("/tmp/jeelink2mqtt_running") except: # guat dann ned... pass else: if verbosemode: if currentsensor_name is None: print("MQTT published") else: print("MQTT publishing surpressed (interval not expired)") if verbosemode: print("\n") # handle outdated sensor values once a minute if (time.time() - checkLastUpdateInterval_lastRun) > checkLastUpdateInterval: checkLastUpdateInterval_lastRun = time.time() #print("check lastUpdate") for key in sensors_lastUpdate: #print(key, '->', sensors_lastUpdate[key], '->', sensors[key]) if (time.time() - sensors_lastUpdate[key]) > sensordata_maxage: if verbosemode: print(sensors[key], ' outd ->') sensors_unavailable[key] = 1 mqttc.publish(mqtt_topic_prefix+"/"+str(sensors[key])+"/availability", "unavailable", qos=0, retain=False) except KeyboardInterrupt: print('\n') exit()