jeelink2mqtt.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #!/usr/bin/python -u
  2. # -*- coding: utf-8 -*-
  3. #
  4. import serial
  5. import time
  6. import os
  7. import paho.mqtt.client as mqtt
  8. #import json
  9. #import math
  10. #import numpy as np
  11. #import httplib
  12. mqtt_server = "mqtt.lan"
  13. mqtt_port = 1883
  14. mqtt_user = "sagi"
  15. mqtt_password = "dirsichernet!"
  16. verbosemode = False
  17. def touch(fname, times=None):
  18. with open(fname, 'a'):
  19. os.utime(fname, times)
  20. def on_connect(client, userdata, flags, rc):
  21. print("MQTT connected with result code " + str(rc))
  22. #client.subscribe("wetter/atemp")
  23. def on_disconnect(client, userdata, rc):
  24. if rc != 0:
  25. print "Unexpected MQTT disconnection. Will auto-reconnect"
  26. #def on_message(client, userdata, msg):
  27. # #print(msg.topic + " " + str(msg.payload))
  28. # global atemp
  29. # atemp = msg.payload
  30. minUpdateInterval = 60
  31. mqtt_topic_prefix = "LaCrosse"
  32. override_updateinterval_on_change = False
  33. atemp_sensor_idx = 94
  34. sensors = {}
  35. sensors_idx = {}
  36. with open("/home/pi/jeelink_sensors.csv", "r") as sensorscsv:
  37. for line in sensorscsv:
  38. if line.find('ID,DomoticzIdx,Name') == -1:
  39. # nur Zeilen die nicht der header sind sind interessant
  40. line = line.strip('\r')
  41. line = line.strip('\n')
  42. parts = line.split(',')
  43. sensorId = parts[0]
  44. domoticzIdx = parts[1]
  45. sensorName = parts[2]
  46. sensors[str(sensorId)] = str(sensorName)
  47. sensors_idx[str(sensorId)] = str(domoticzIdx)
  48. mqttc = mqtt.Client()
  49. mqttc.on_connect = on_connect
  50. mqttc.on_disconnect = on_disconnect
  51. ##mqttc.on_message = on_message
  52. mqttc.username_pw_set(mqtt_user, mqtt_password)
  53. mqttc.connect(mqtt_server, mqtt_port, 60)
  54. mqttc.loop_start()
  55. #mqttc.loop_forever()
  56. ser = serial.Serial(port='/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AL01MYTF-if00-port0',
  57. baudrate = 57600,
  58. parity=serial.PARITY_NONE,
  59. stopbits=serial.STOPBITS_ONE,
  60. bytesize=serial.EIGHTBITS,
  61. timeout=1)
  62. #sensors = {'4':'Arbeitszimmer','16':'AussenGarten','60':'AussenParkplatz','50':'Bad','39':'Balkon','55':'Kueche','40':'Schlafzimmer'}
  63. #sensors_idx = {'4':'1','16':'94','60':'113','50':'4','39':'88','55':'6','40':'3'}
  64. if verbosemode:
  65. print sensors
  66. print sensors_idx
  67. sensors_lastTemp = {}
  68. sensors_lastHum = {}
  69. sensors_lastUpdate = {}
  70. try:
  71. while True:
  72. msg_was_sent = 0
  73. #clear serial buffer to remove junk and noise
  74. ser.flushInput()
  75. #read buffer until cr/lf
  76. serLine = ser.readline()
  77. if(serLine):
  78. #print (repr(serLine)) #Echo the serial buffer bytes up to the CRLF back to screen
  79. serLine = serLine.strip('\r')
  80. serLine = serLine.strip('\n')
  81. if verbosemode:
  82. print serLine
  83. if serLine.find('OK 9') != -1:
  84. # uns interessieren nur reinkommende Zeilen die mit "OK 9 " beginnen
  85. # 0 1 2 3 4 5 6
  86. # OK 9 ID XXX XXX XXX XXX
  87. # | | | | | | |
  88. # | | | | | | --- Humidity incl. WeakBatteryFlag
  89. # | | | | | |------ Temp * 10 + 1000 LSB
  90. # | | | | |---------- Temp * 10 + 1000 MSB
  91. # | | | |-------------- Sensor type (1 or 2) +128 if NewBatteryFlag
  92. # | | |----------------- Sensor ID
  93. # | |------------------- fix "9"
  94. # |---------------------- fix "OK"
  95. bytes = serLine.split(' ')
  96. #addr = bytes[2]
  97. #addr = "{0:x}".format(int(bytes[2]))
  98. #addr = hex((int(bytes[2])))
  99. addr = int(bytes[2])
  100. addrhex = "{0:x}".format(int(bytes[2]))
  101. currentsensor_name = sensors.get(str(addr), None)
  102. #if currentsensor_name == 0:
  103. if currentsensor_name is None:
  104. fname = '/home/pi/logs/jeelink_unknown_sensor_' + str(addr)
  105. try:
  106. touch(fname)
  107. except:
  108. # guat dann hoit ned...
  109. pass
  110. if verbosemode:
  111. print "unknown sensor ID " + str(addr)
  112. temp = (int(bytes[4])*256 + int(bytes[5]) - 1000)/10.0
  113. print "Temp: " + str(temp)
  114. lastUpdate = sensors_lastUpdate.get(str(addr), None)
  115. lastTemp = sensors_lastTemp.get(str(addr), None)
  116. lastHum = sensors_lastHum.get(str(addr), None)
  117. currentsensor_idx = sensors_idx.get(str(addr),None)
  118. senddata = False
  119. if currentsensor_idx is not None:
  120. if override_updateinterval_on_change:
  121. if lastTemp != str(temp) or lastHum != str(hum):
  122. senddata = True
  123. #print "hier! " + str(temp) + " != " + str(lastTemp) + " " + str(hum) + " != " + str(lastHum)
  124. if lastUpdate is not None:
  125. timediff = int(time.time()) - lastUpdate
  126. if timediff >= minUpdateInterval:
  127. senddata = True
  128. #print "do!"
  129. else:
  130. senddata = True
  131. if senddata:
  132. if int(bytes[3]) >= 128:
  133. batt_new = 1
  134. type = int(bytes[3]) - 128
  135. else:
  136. batt_new = 0
  137. type = int(bytes[3])
  138. temp = (int(bytes[4])*256 + int(bytes[5]) - 1000)/10.0
  139. if int(bytes[6]) >= 128:
  140. batt_low = 1
  141. hum = int(bytes[6]) - 128
  142. else:
  143. batt_low = 0
  144. hum = int(bytes[6])
  145. if hum > 100:
  146. hum = 100
  147. if batt_low == 0:
  148. batterystate = "ok"
  149. else:
  150. batterystate = "low"
  151. sensors_lastUpdate[str(addr)] = int(time.time())
  152. sensors_lastTemp[str(addr)] = str(temp)
  153. sensors_lastHum[str(addr)] = str(hum)
  154. if int(currentsensor_idx) == atemp_sensor_idx:
  155. mqttc.publish("wetter/atemp", str(temp), qos=2, retain=True)
  156. mqttc.publish("wetter/ahum", str(hum), qos=2, retain=True)
  157. domoticz_json = "{\"idx\":" + str(currentsensor_idx) + ",\"nvalue\":0,\"svalue\":\"" + str(temp) + ";" + str(hum) + ";1\"}"
  158. if verbosemode:
  159. print domoticz_json
  160. mqttc.publish("domoticz/in", domoticz_json, qos=2, retain=False)
  161. mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/temperature", str(temp), qos=2, retain=True)
  162. mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/humidity", str(hum), qos=2, retain=True)
  163. mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/battery", str(batterystate), qos=2, retain=True)
  164. lacrosse_json = "{\"temperature\":" + str(temp) + ", \"humidity\":" + str(hum) + ", \"battery\":\"" + str(batterystate) + "\"}"
  165. mqttc.publish(mqtt_topic_prefix+"/"+str(currentsensor_name)+"/json", lacrosse_json, qos=2, retain=True)
  166. if verbosemode:
  167. 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)
  168. try:
  169. touch("/tmp/jeelink2mqtt_running")
  170. except:
  171. # guat dann ned...
  172. pass
  173. except KeyboardInterrupt, e:
  174. print('\n')