You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
6.8KB

  1. #!/usr/bin/env python3
  2. from __future__ import absolute_import, print_function
  3. import sys
  4. import argparse
  5. import socket
  6. import logging
  7. from datetime import datetime
  8. import can
  9. from can import Bus, BusState, Logger
  10. import paho.mqtt.client as mqtt
  11. base_topic="house"
  12. coil_topic = "coil"
  13. light_topic = "light"
  14. switch_topic = "switch"
  15. coil_map = {
  16. # tag, node, address, affinity
  17. # tag: (L)ight, (E)G, (O)G, (F)lur, (S)chlaf, (A)ussen
  18. 'EZ': [ "LE", 0x03, 0x01 ],
  19. 'K': [ "LE", 0x03, 0x02 ],
  20. 'WZ1': [ "LE", 0x02, 0x01 ],
  21. 'WZ2': [ "LE", 0x02, 0x02 ],
  22. 'TER': [ "LEA", 0x03, 0x03 ],
  23. 'FE': [ "LFE", 0x03, 0x07 ],
  24. 'TRE': [ "LFE", 0x02, 0x03 ],
  25. 'FO': [ "LFO", 0x01, 0x03 ],
  26. 'TRO': [ "LFO", 0x01, 0x07 ],
  27. 'KI1': [ "LSO", 0x01, 0x06 ],
  28. 'KI2': [ "LSO", 0x01, 0x04 ],
  29. 'SCH': [ "LSO", 0x01, 0x08 ],
  30. 'B1': [ "LO", 0x01, 0x01 ],
  31. 'B2': [ "LO", 0x01, 0x02 ],
  32. 'ROL_EZ_G_DOWN': [ "", 0x03, 0x05, -1],
  33. 'ROL_EZ_G_UP': [ "", 0x03, 0x04, -1],
  34. 'ROL_EZ_N_DOWN': [ "", 0x03, 0x06, -2],
  35. 'ROL_EZ_N_UP': [ "", 0x03, 0x08, -2],
  36. # 'EG1': [ 0, 0x02, 0x09 ],
  37. # 'EG2': [ 0, 0x03, 0x09 ],
  38. # 'OG': [ 0, 0x01, 0x09 ],
  39. 'ALL': [ "", 0xFF, 0x09 ],
  40. }
  41. cmd_map = {
  42. 'off': 0,
  43. '0': 0,
  44. 'on': 1,
  45. '1': 1,
  46. 'value': 2,
  47. 'toggle': 3
  48. }
  49. def decon(msg_id):
  50. msg_prio=msg_id>>26
  51. msg_type=(msg_id>>24) & 0x03
  52. msg_dst=(msg_id>>16) & 0xFF
  53. msg_src=(msg_id>>8) & 0xFF
  54. msg_cmd=msg_id & 0xFF
  55. # print("msg_id: ", hex(msg_id), "prio: ", hex(msg_prio),"type: ", hex(msg_type)," dst: ", hex(msg_dst)," src: ", hex(msg_src)," cmd: ",hex(msg_cmd))
  56. return [msg_prio, msg_type, msg_dst, msg_src, msg_cmd]
  57. def con(msg_dst,msg_cmd,msg_prio=3,msg_type=0,msg_src=11, ):
  58. msg_id=(msg_prio<<26) + \
  59. ((msg_type&0x03)<<24) +\
  60. ((msg_dst&0xFF)<<16) +\
  61. ((msg_src&0xFF)<<8) +\
  62. (msg_cmd&0xFF)
  63. print("Constructed ID: "+hex(msg_id))
  64. return msg_id
  65. def coil_action(coil, payload, bus, tag=0 ):
  66. try:
  67. cmd=cmd_map[payload]
  68. except BaseException as e:
  69. logging.error("Error finding command {%s}: %s" % (payload, e))
  70. print("Coil: ",coil,"cmd:", payload)
  71. try:
  72. addr = coil_map[coil]
  73. print("Addr:", addr)
  74. if cmd == 2:
  75. print("TBD")
  76. # msg_data.append(value)
  77. print("cmd", cmd,"length: ", len(addr))
  78. if tag is not 0:
  79. if tag not in addr[0]:
  80. print("not tagged with: ",tag)
  81. return
  82. if cmd is not 0 and len(addr) is 4:
  83. print("Applying affinity")
  84. for key, value in coil_map.items():
  85. print("checking: ", key)
  86. if len(value) is 4:
  87. print("value_2 is", value[3], "Addr: ", addr[3])
  88. if value[3] is addr[3]:
  89. msg_id=con(msg_dst=value[1],msg_cmd=0)
  90. msg_data=[value[2]]
  91. m = can.Message(arbitration_id=msg_id,
  92. data=msg_data,
  93. extended_id=True)
  94. try:
  95. bus.send(m)
  96. print("data sent to CAN",m)
  97. except BaseException as e:
  98. logging.error("Error sending can message {%s}: %s" % (m, e))
  99. msg_id=con(msg_dst=addr[1],msg_cmd=cmd)
  100. msg_data=[addr[2]]
  101. print(msg_id)
  102. m = can.Message(arbitration_id=msg_id,
  103. data=msg_data,
  104. extended_id=True)
  105. try:
  106. bus.send(m)
  107. print("data sent to CAN",m)
  108. except BaseException as e:
  109. logging.error("Error sending can message {%s}: %s" % (m, e))
  110. except BaseException as e:
  111. logging.error("Error finding coil %s" % (coil))
  112. def on_connect(mcp_mqtt, userdata, flags, rc):
  113. print("Connected with result code "+str(rc))
  114. # Subscribing in on_connect() means that if we lose the connection and
  115. # reconnect then subscriptions will be renewed.
  116. mcp_mqtt.subscribe(base_topic+"/#", 0)
  117. print("Subscribed to: "+base_topic+"/#")
  118. def on_message_coil(mcp_mqtt, bus, msg):
  119. print("data Received topic: ", msg.topic)
  120. m_decode=str(msg.payload.decode("utf-8","ignore"))
  121. print("data Received",m_decode)
  122. sub=msg.topic[len(base_topic)+len(coil_topic)+2:]
  123. coil_action(sub, m_decode, bus)
  124. def on_message_light(mcp_mqtt, bus, msg):
  125. print("data Received topic: ", msg.topic)
  126. m_decode=str(msg.payload.decode("utf-8","ignore"))
  127. print("data Received",m_decode)
  128. sub=msg.topic[len(base_topic)+len(light_topic)+2:]
  129. coil_action(sub, m_decode, bus, "L")
  130. def on_message(mcp_mqtt, obj, msg):
  131. # This callback will be called for messages that we receive that do not
  132. # match any patterns defined in topic specific callbacks, i.e. in this case
  133. print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
  134. def main():
  135. verbosity = 2
  136. logging_level_name = ['critical', 'error', 'warning', 'info', 'debug', 'subdebug'][min(5, verbosity)]
  137. can.set_logging_level(logging_level_name)
  138. can_filters = []
  139. config = {"can_filters": can_filters, "single_handle": True}
  140. config["interface"] = "socketcan"
  141. config["bitrate"] = 125000
  142. bus = Bus("can1", **config)
  143. print('Connected to {}: {}'.format(bus.__class__.__name__, bus.channel_info))
  144. print('Can Logger (Started on {})\n'.format(datetime.now()))
  145. mcp_mqtt = mqtt.Client()
  146. mcp_mqtt.on_connect = on_connect
  147. # mcp_mqtt.on_message = on_message
  148. mcp_mqtt.user_data_set(bus)
  149. mcp_mqtt.loop_start()
  150. mcp_mqtt.message_callback_add(base_topic+"/"+coil_topic+"/+", on_message_coil)
  151. mcp_mqtt.message_callback_add(base_topic+"/"+light_topic+"/+", on_message_light)
  152. mcp_mqtt.on_message = on_message
  153. mcp_mqtt.connect("mcp", 1883, 60)
  154. mcp_mqtt.loop_start()
  155. #mcp_mqtt.loop_forever()
  156. try:
  157. while True:
  158. msg = bus.recv(1)
  159. if msg is not None:
  160. de=decon(msg.arbitration_id)
  161. m= { "prio": hex(de[0]), "type": hex(de[1]), "dst": hex(de[2]), "src": hex(de[3]), "cmd": hex(de[4]), "action": hex(msg.data[0]) }
  162. if de[2]==0 and de[4] == 6:
  163. print("received state: ", hex(de[3]), hex(msg.data[0]), hex(msg.data[1]))
  164. for key in coil_map:
  165. address=coil_map[key]
  166. if address[1] == de[3] and address[2] == msg.data[0]+1:
  167. print("coil/"+key+" changed to "+str(msg.data[1]))
  168. mcp_mqtt.publish(base_topic+"/"+coil_topic+"/"+key+"/state", msg.data[1] , retain=1)
  169. if "L" in address[0]:
  170. print("light/"+key+" changed to "+str(msg.data[1]))
  171. mcp_mqtt.publish(base_topic+"/"+light_topic+"/"+key+"/state", msg.data[1] , retain=1)
  172. elif de[2]==0 and de[4] == 5:
  173. print("received state: ", de[3], msg.data[0], bin(msg.data[1]))
  174. mcp_mqtt.publish(base_topic+"/"+switch_topic+"/"+str(de[3])+"-"+str(msg.data[0]), bin(msg.data[1]) , retain=1)
  175. except KeyboardInterrupt:
  176. pass
  177. finally:
  178. bus.shutdown()
  179. if __name__ == "__main__":
  180. main()