ioext.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #!/usr/bin/python3 -u
  2. #
  3. import serial
  4. from time import sleep
  5. import os
  6. import sys
  7. import paho.mqtt.client as mqtt
  8. import json
  9. # --- CONFIGURATION ---
  10. # timeout in s to end script when nothing is received (will be restarted by systemd then)
  11. # must be higher than getStatusInterval
  12. quitOnNoReceiveTimeout = 75
  13. # status file - on ramdrive (/tmp on RasPi by default)
  14. # is "touched" every time a serial message comes in so that working communication can be monitored easily
  15. statusFile = '/tmp/ioext_running'
  16. # serial port config
  17. serialPort = '/dev/serial/by-id/usb-1a86_USB_Serial-if00-port0'
  18. serialBaud = 57600
  19. serialTimeout = 1 # should be 1 as above interval/timeout relies on it
  20. # MQTT config
  21. mqtt_server = "mqtt.lan"
  22. mqtt_port = 1883
  23. mqtt_user = "user"
  24. mqtt_password = "password"
  25. mqtt_base_topic = "T5/HomeSvrIOExt"
  26. mqtt_topic_tuerkontakt = "T5/Wohnungstuer/Tuerkontakt"
  27. mqtt_topic_pir1 = "T5/VZ/PIR1"
  28. mqtt_topic_pir2 = "T5/VZ/PIR2"
  29. mqtt_topic_out_temp = "T5/Abstr/Sensors/temp"
  30. mqtt_topic_out_hum = "T5/Abstr/Sensors/hum"
  31. # --- END CONFIGURATION ---
  32. # --- GLOBAL VARS ---
  33. noReceiveCount = 0 # increase everytime the serial readline times out (1s timeout)
  34. #getStatusCount = 0 # used as heartbeat, get status every 30s
  35. verbose = False
  36. debug = False
  37. quiet = True
  38. lastState_tk = None
  39. lastState_pir1 = None
  40. lastState_pir2 = None
  41. # --- END GLOBAL VARS ---
  42. if len(sys.argv) >= 2:
  43. if sys.argv[1] == "-q":
  44. verbose = False
  45. debug = False
  46. quiet = True
  47. #print("VERBOSE=ON")
  48. elif sys.argv[1] == "-v":
  49. verbose = True
  50. debug = False
  51. quiet = False
  52. #print("VERBOSE=ON")
  53. elif sys.argv[1] == "-d":
  54. verbose = True
  55. quiet = False
  56. debug = True
  57. #print("DEBUG=ON")
  58. def on_connect(client, userdata, flags, rc):
  59. if not quiet:
  60. print("MQTT connected with result code " + str(rc))
  61. #client.subscribe(mqtt_topic_cmd)
  62. #def on_message(client, userdata, msg):
  63. # if msg.topic == mqtt_topic_cmd:
  64. # payload = msg.payload.decode('ascii')
  65. # if payload == "status" or payload == "get conf" or payload == "clear" or payload == "open door" or payload.startswith("set prof ") or payload.startswith("set conf "):
  66. # if verbose:
  67. # print("serCmd: " + payload)
  68. # serCmd = payload + '\n'
  69. # ser.write(serCmd.encode('ascii'))
  70. # elif verbose:
  71. # print("unknown command:", payload)
  72. def touch(fname, times=None):
  73. with open(fname, 'a'):
  74. os.utime(fname, times)
  75. mqttc = mqtt.Client()
  76. mqttc.on_connect = on_connect
  77. #mqttc.on_disconnect = on_disconnect
  78. #mqttc.on_message = on_message
  79. mqttc.username_pw_set(mqtt_user, mqtt_password)
  80. mqttc.connect(mqtt_server, mqtt_port, 60)
  81. mqttc.loop_start()
  82. ser = serial.Serial(port=serialPort,
  83. baudrate = serialBaud,
  84. parity=serial.PARITY_NONE,
  85. stopbits=serial.STOPBITS_ONE,
  86. bytesize=serial.EIGHTBITS,
  87. timeout=serialTimeout)
  88. try:
  89. while True:
  90. ##ser.flushInput() ## attention truncates incoming strings if there is little time between them
  91. #read buffer until cr/lf
  92. serLine = ser.readline().strip()
  93. # catch exception on invalid char coming in: UnicodeDecodeError: 'ascii' codec can't decode byte 0xf4 in position 6: ordinal not in range(128)
  94. try:
  95. serLine = serLine.decode('ascii')
  96. except:
  97. serLine = ""
  98. # if there came something in...
  99. if(serLine):
  100. noReceiveCount = 0
  101. touch(statusFile)
  102. #serLine = serLine.strip('\'')
  103. #serLine = serLine.strip('\r')
  104. #serLine = serLine.strip('\n')
  105. if verbose:
  106. print ('RX: ' + repr(serLine)) #Echo the serial buffer bytes up to the CRLF back to screen
  107. mqttc.publish(mqtt_base_topic + "/RX", str(serLine), qos=0, retain=False)
  108. # Tuerkontakt
  109. if serLine.startswith('P2='):
  110. newState = None
  111. if serLine == "P2=L":
  112. newState = "OFF"
  113. elif serLine == "P2=H":
  114. newState = "ON"
  115. if newState is not None and lastState_tk != newState:
  116. lastState_tk = newState
  117. mqttc.publish(mqtt_topic_tuerkontakt, newState, qos=0, retain=False)
  118. # PIR #1
  119. if serLine.startswith('P3='):
  120. newState = None
  121. if serLine == "P3=L":
  122. newState = "OFF"
  123. elif serLine == "P3=H":
  124. newState = "ON"
  125. if newState is not None and lastState_pir1 != newState:
  126. lastState_pir1 = newState
  127. mqttc.publish(mqtt_topic_pir1, newState, qos=0, retain=False)
  128. # PIR #2
  129. if serLine.startswith('P4='):
  130. newState = None
  131. if serLine == "P4=L":
  132. newState = "OFF"
  133. elif serLine == "P4=H":
  134. newState = "ON"
  135. if newState is not None and lastState_pir2 != newState:
  136. lastState_pir2 = newState
  137. mqttc.publish(mqtt_topic_pir2, newState, qos=0, retain=False)
  138. # DHT TH sensor
  139. # {"T":26.60,"H":36}
  140. if serLine.startswith('{"T":'):
  141. th = json.loads(serLine)
  142. t = round(float(th["T"]), 1)
  143. h = int(th["H"])
  144. if t >= -20 and t <= 50:
  145. mqttc.publish(mqtt_topic_out_temp, str(t), qos=0, retain=False)
  146. if h >= 0 and h <= 100:
  147. mqttc.publish(mqtt_topic_out_hum, str(h), qos=0, retain=False)
  148. ## publish MQTT messages
  149. #if serLine.startswith('EVENT_'):
  150. # mqttc.publish(mqtt_topic_event, serLine, qos=0, retain=False)
  151. # #os.system(os.path.dirname(os.path.realpath(__file__))+'/event_top5_klingel.py')
  152. #
  153. #elif serLine.startswith('OK'):
  154. # mqttc.publish(mqtt_topic_cmdresponse, serLine, qos=0, retain=False)
  155. #
  156. #elif serLine.startswith('{"'):
  157. # mqttc.publish(mqtt_topic_cmdresponse, serLine, qos=0, retain=False)
  158. # #os.system(os.path.dirname(os.path.realpath(__file__))+'/event_top5_klingel.py')
  159. # nothing came in this time...
  160. else:
  161. noReceiveCount += 1
  162. if debug:
  163. print("noReceiveCount=" + str(noReceiveCount))
  164. # quit script if nothing has been received for some time - will be restarted by systemd
  165. if noReceiveCount >= quitOnNoReceiveTimeout:
  166. quit()
  167. ## get status every [getStatusInterval] seconds
  168. #getStatusCount += 1
  169. #if getStatusCount >= getStatusInterval:
  170. # getStatusCount = 0
  171. # serCmd = 'status\n'
  172. # ser.write(serCmd.encode('ascii'))
  173. #if debug:
  174. # print("getStatusCount=" + str(getStatusCount))
  175. except KeyboardInterrupt:
  176. print('\n')