#!/usr/bin/env python3 from __future__ import absolute_import, print_function import sys import argparse import socket import logging from datetime import datetime import can from can import Bus, BusState, Logger import paho.mqtt.client as mqtt base_topic="house" coil_topic = "coil" light_topic = "light" switch_topic = "switch" coil_map = { # tag, node, address, affinity # tag: (L)ight, (E)G, (O)G, (F)lur, (S)chlaf, (A)ussen 'EZ': [ "LE", 0x03, 0x01 ], 'K': [ "LE", 0x03, 0x02 ], 'WZ1': [ "LE", 0x02, 0x01 ], 'WZ2': [ "LE", 0x02, 0x02 ], 'TER': [ "LEA", 0x03, 0x03 ], 'FE': [ "LFE", 0x03, 0x07 ], 'TRE': [ "LFE", 0x02, 0x03 ], 'FO': [ "LFO", 0x01, 0x03 ], 'TRO': [ "LFO", 0x01, 0x07 ], 'KI1': [ "LSO", 0x01, 0x06 ], 'KI2': [ "LSO", 0x01, 0x04 ], 'SCH': [ "LSO", 0x01, 0x08 ], 'B1': [ "LO", 0x01, 0x01 ], 'B2': [ "LO", 0x01, 0x02 ], 'ROL_EZ_G_DOWN': [ "", 0x03, 0x05, -1], 'ROL_EZ_G_UP': [ "", 0x03, 0x04, -1], 'ROL_EZ_N_DOWN': [ "", 0x03, 0x06, -2], 'ROL_EZ_N_UP': [ "", 0x03, 0x08, -2], # 'EG1': [ 0, 0x02, 0x09 ], # 'EG2': [ 0, 0x03, 0x09 ], # 'OG': [ 0, 0x01, 0x09 ], 'ALL': [ "", 0xFF, 0x09 ], } cmd_map = { 'off': 0, '0': 0, 'on': 1, '1': 1, 'value': 2, 'toggle': 3 } def decon(msg_id): msg_prio=msg_id>>26 msg_type=(msg_id>>24) & 0x03 msg_dst=(msg_id>>16) & 0xFF msg_src=(msg_id>>8) & 0xFF msg_cmd=msg_id & 0xFF # 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)) return [msg_prio, msg_type, msg_dst, msg_src, msg_cmd] def con(msg_dst,msg_cmd,msg_prio=3,msg_type=0,msg_src=11, ): msg_id=(msg_prio<<26) + \ ((msg_type&0x03)<<24) +\ ((msg_dst&0xFF)<<16) +\ ((msg_src&0xFF)<<8) +\ (msg_cmd&0xFF) print("Constructed ID: "+hex(msg_id)) return msg_id def coil_action(coil, payload, bus, tag=0 ): try: cmd=cmd_map[payload] except BaseException as e: logging.error("Error finding command {%s}: %s" % (payload, e)) print("Coil: ",coil,"cmd:", payload) try: addr = coil_map[coil] print("Addr:", addr) if cmd == 2: print("TBD") # msg_data.append(value) print("cmd", cmd,"length: ", len(addr)) if tag is not 0: if tag not in addr[0]: print("not tagged with: ",tag) return if cmd is not 0 and len(addr) is 4: print("Applying affinity") for key, value in coil_map.items(): print("checking: ", key) if len(value) is 4: print("value_2 is", value[3], "Addr: ", addr[3]) if value[3] is addr[3]: msg_id=con(msg_dst=value[1],msg_cmd=0) msg_data=[value[2]] m = can.Message(arbitration_id=msg_id, data=msg_data, extended_id=True) try: bus.send(m) print("data sent to CAN",m) except BaseException as e: logging.error("Error sending can message {%s}: %s" % (m, e)) msg_id=con(msg_dst=addr[1],msg_cmd=cmd) msg_data=[addr[2]] print(msg_id) m = can.Message(arbitration_id=msg_id, data=msg_data, extended_id=True) try: bus.send(m) print("data sent to CAN",m) except BaseException as e: logging.error("Error sending can message {%s}: %s" % (m, e)) except BaseException as e: logging.error("Error finding coil %s" % (coil)) def on_connect(mcp_mqtt, userdata, flags, rc): print("Connected with result code "+str(rc)) # Subscribing in on_connect() means that if we lose the connection and # reconnect then subscriptions will be renewed. mcp_mqtt.subscribe(base_topic+"/#", 0) print("Subscribed to: "+base_topic+"/#") def on_message_coil(mcp_mqtt, bus, msg): print("data Received topic: ", msg.topic) m_decode=str(msg.payload.decode("utf-8","ignore")) print("data Received",m_decode) sub=msg.topic[len(base_topic)+len(coil_topic)+2:] coil_action(sub, m_decode, bus) def on_message_light(mcp_mqtt, bus, msg): print("data Received topic: ", msg.topic) m_decode=str(msg.payload.decode("utf-8","ignore")) print("data Received",m_decode) sub=msg.topic[len(base_topic)+len(light_topic)+2:] coil_action(sub, m_decode, bus, "L") def on_message(mcp_mqtt, obj, msg): # This callback will be called for messages that we receive that do not # match any patterns defined in topic specific callbacks, i.e. in this case print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload)) def main(): verbosity = 2 logging_level_name = ['critical', 'error', 'warning', 'info', 'debug', 'subdebug'][min(5, verbosity)] can.set_logging_level(logging_level_name) can_filters = [] config = {"can_filters": can_filters, "single_handle": True} config["interface"] = "socketcan" config["bitrate"] = 125000 bus = Bus("can1", **config) print('Connected to {}: {}'.format(bus.__class__.__name__, bus.channel_info)) print('Can Logger (Started on {})\n'.format(datetime.now())) mcp_mqtt = mqtt.Client() mcp_mqtt.on_connect = on_connect # mcp_mqtt.on_message = on_message mcp_mqtt.user_data_set(bus) mcp_mqtt.loop_start() mcp_mqtt.message_callback_add(base_topic+"/"+coil_topic+"/+", on_message_coil) mcp_mqtt.message_callback_add(base_topic+"/"+light_topic+"/+", on_message_light) mcp_mqtt.on_message = on_message mcp_mqtt.connect("mcp", 1883, 60) mcp_mqtt.loop_start() #mcp_mqtt.loop_forever() try: while True: msg = bus.recv(1) if msg is not None: de=decon(msg.arbitration_id) 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]) } if de[2]==0 and de[4] == 6: print("received state: ", hex(de[3]), hex(msg.data[0]), hex(msg.data[1])) for key in coil_map: address=coil_map[key] if address[1] == de[3] and address[2] == msg.data[0]+1: print("coil/"+key+" changed to "+str(msg.data[1])) mcp_mqtt.publish(base_topic+"/"+coil_topic+"/"+key+"/state", msg.data[1] , retain=1) if "L" in address[0]: print("light/"+key+" changed to "+str(msg.data[1])) mcp_mqtt.publish(base_topic+"/"+light_topic+"/"+key+"/state", msg.data[1] , retain=1) elif de[2]==0 and de[4] == 5: print("received state: ", de[3], msg.data[0], bin(msg.data[1])) mcp_mqtt.publish(base_topic+"/"+switch_topic+"/"+str(de[3])+"-"+str(msg.data[0]), bin(msg.data[1]) , retain=1) except KeyboardInterrupt: pass finally: bus.shutdown() if __name__ == "__main__": main()