123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- #!/usr/bin/python3 -u
- # -*- coding: utf-8 -*-
- #
- import serial
- #from serial import Serial
- #from serial 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
- mqtt_server = "mqtt.lan"
- mqtt_port = 1883
- mqtt_user = "script"
- mqtt_password = "rlAzqusqfbAy"
- sensordata_maxage = 300
- verbosemode = False
- 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
-
- minUpdateInterval = 60
- mqtt_topic_prefix = "LaCrosse"
- override_updateinterval_on_change = False
- atemp_sensor_idx = 94
- atemp_sensor_idx_2 = 113
- atemp = 61 #starting values only..
- 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("/home/pi/jeelink_sensors.csv", "r") as sensorscsv:
- for line in sensorscsv:
- if line.find('ID,DomoticzIdx,Name') == -1:
- # nur Zeilen die nicht der header sind sind interessant
- 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
- 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='/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AL01MYTF-if00-port0',
- baudrate = 57600,
- 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=2, retain=False)
- mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(addr)+"/humidity", str(hum), qos=2, retain=False)
- mqttc.publish(mqtt_topic_prefix+"/UnknownSensor/"+str(addr)+"/battNew", str(batt_new), qos=2, 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:
- atemp_last = atemp
- ahum_last = ahum
- mqttc.publish("wetter/atemp", str(atemp), qos=2, retain=True)
- mqttc.publish("wetter/ahum", str(ahum), qos=2, retain=True)
-
- domoticz_json = "{\"idx\":" + str(currentsensor_idx) + ",\"nvalue\":0,\"svalue\":\"" + str(temp) + ";" + str(hum) + ";1\"}"
- #if verbosemode:
- # print(domoticz_json)
- mqttc.publish("domoticz/in", domoticz_json, qos=2, retain=False)
-
- mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/temperature", str(temp), qos=2, retain=False)
- mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/humidity", str(hum), qos=2, retain=False)
- mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/battery", str(batterystate), qos=2, retain=False)
- mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/lastUpdate", strftime("%Y-%m-%d %H:%M:%S", localtime()), qos=2, retain=False)
- mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/availability", "available", qos=2, 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=2, 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=2, retain=False)
- except KeyboardInterrupt:
- print('\n')
- exit()
|