├── README.md └── mqtt-mysensors /README.md: -------------------------------------------------------------------------------- 1 | # mqtt-mysensors 2 | Provide a MQTT service using a mysensors serial gateway version 1.4 3 | -------------------------------------------------------------------------------- /mqtt-mysensors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # mqtt-mysensors by Theo Arends 4 | # 5 | # Provides MySensors MQTT service using a mysensors serial gateway version 1.4 6 | # 7 | # Execute: mqtt-mysensors & 8 | # 9 | # Needs paho.mqtt.client 10 | # - git clone http://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.python.git 11 | # - python setup.py install 12 | # Needs pyserial 2.7 13 | # - apt-get install python-pip 14 | # - pip install pyserial 15 | # 16 | # Topic = /// 17 | # - mysensors/4/0/V_LIGHT ON 18 | # 19 | # **** Start of user configuration values 20 | 21 | serial_port = "/dev/ttyUSB0" # MySensors serial gateway port 22 | serial_bps = 115200 # MySensors serial gateway baud rate 23 | 24 | broker = "sidnas2" # MQTT broker ip address or name 25 | broker_port = 1883 # MQTT broker port 26 | 27 | topic_prefix = "mysensors" # MQTT mysensors topic prefix 28 | 29 | M_INFO = 0 # Print messages: 0 - none, 1 - Startup, 2 - Serial, 3 - All 30 | 31 | # **** End of user configuration values 32 | 33 | import paho.mqtt.client as mqtt 34 | import serial 35 | import time 36 | 37 | # mysensor_command 38 | C_PRESENTATION = '0' 39 | C_SET = '1' 40 | C_REQ = '2' 41 | C_INTERNAL = '3' 42 | C_STREAM = '4' 43 | 44 | # mysensor_data 45 | V_codes = ['TEMP', 'HUM', 'LIGHT', 'DIMMER', 'PRESSURE', 'FORECAST', 'RAIN', 'RAINRATE', 'WIND', 46 | 'GUST', 'DIRECTON', 'UV', 'WEIGHT', 'DISTANCE', 'IMPEDANCE', 'ARMED', 'TRIPPED', 'WATT', 'KWH', 47 | 'SCENE_ON', 'SCENE_OFF', 'HEATER', 'HEATER_SW', 'LIGHT_LEVEL', 'VAR1', 'VAR2', 'VAR3', 'VAR4', 48 | 'VAR5', 'UP', 'DOWN', 'STOP', 'IR_SEND', 'IR_RECEIVE', 'FLOW', 'VOLUME', 'LOCK_STATUS', 'DUST_LEVEL', 49 | 'VOLTAGE', 'CURRENT'] 50 | 51 | # mysensor_internal 52 | I_codes = ['BATTERY_LEVEL', 'TIME', 'VERSION', 'ID_REQUEST', 'ID_RESPONSE', 'INCLUSION_MODE', 'CONFIG', 53 | 'FIND_PARENT', 'FIND_PARENT_RESPONSE', 'LOG_MESSAGE', 'CHILDREN', 'SKETCH_NAME', 'SKETCH_VERSION', 54 | 'REBOOT', 'GATEWAY_READY'] 55 | 56 | # mysensor_sensor 57 | S_codes = ['DOOR', 'MOTION', 'SMOKE', 'LIGHT', 'DIMMER', 'COVER', 'TEMP', 'HUM', 'BARO', 'WIND', 58 | 'RAIN', 'UV', 'WEIGHT', 'POWER', 'HEATER', 'DISTANCE', 'LIGHT_LEVEL', 'ARDUINO_NODE', 59 | 'ARDUINO_REPEATER_NODE', 'LOCK', 'IR', 'WATER', 'AIR_QUALITY', 'CUSTOM', 'DUST', 'SCENE_CONTROLLER'] 60 | 61 | M_NACK = '0' 62 | M_ACK = '1' 63 | 64 | B_codes = ['OFF', 'ON'] 65 | 66 | mypublish = "" 67 | 68 | # The callback for when the client receives a CONNACK response from the server. 69 | def on_connect(client, userdata, flags, rc): 70 | if M_INFO > 0: 71 | print("\nMQTT-MySensors service connected with result code "+str(rc)) 72 | 73 | # Subscribing in on_connect() means that if we lose the connection and 74 | # reconnect then subscriptions will be renewed. 75 | client.subscribe(topic_prefix+"/#") 76 | 77 | # The callback for when a PUBLISH message is received from the server. 78 | def on_message(client, userdata, msg): 79 | 80 | if M_INFO > 2: 81 | print(" Last published |"+mypublish+"|") 82 | print("MQTT subscribed |"+msg.topic+"|"+msg.payload+"|") 83 | 84 | mysubscribe = msg.topic+"/"+msg.payload 85 | if mypublish != mysubscribe: 86 | parts = msg.topic.split("/") # mysensors/4/0/V_LIGHT/ON 87 | 88 | try: 89 | if (len(parts) > 3) and (int(parts[1]) < 256) and (int(parts[2]) < 256): 90 | command = C_PRESENTATION 91 | idx = "0" 92 | mysensor_data = parts[3].upper() 93 | msg_payload = msg.payload.rstrip() 94 | payload = "" 95 | if mysensor_data[0] == 'I': 96 | command = C_INTERNAL 97 | if len(msg_payload) != 0: 98 | payload = ";" + msg_payload 99 | if mysensor_data[2:22] in I_codes: 100 | idx = str(I_codes.index(mysensor_data[2:22])) 101 | else: 102 | if len(msg_payload) == 0: 103 | command = C_REQ 104 | else: 105 | command = C_SET 106 | payload = ";" + msg_payload 107 | if mysensor_data == 'V_LIGHT': 108 | if msg_payload.upper() in B_codes: 109 | payload = ";"+str(B_codes.index(msg_payload.upper())) 110 | if mysensor_data[2:22] in V_codes: 111 | idx = str(V_codes.index(mysensor_data[2:22])) 112 | myserial = parts[1]+";"+parts[2]+";"+command+";0;"+idx+payload 113 | 114 | if M_INFO > 1: 115 | print(" Serial out |"+myserial+"|") 116 | 117 | ser.write(myserial+"\n") 118 | except: 119 | pass 120 | 121 | if M_INFO > 0: 122 | print("MQTT mysensors serial gateway service.") 123 | 124 | while True: 125 | client = mqtt.Client() 126 | client.on_connect = on_connect 127 | client.on_message = on_message 128 | 129 | rc = 1 130 | while rc == 1: 131 | try: 132 | client.connect(broker, broker_port) 133 | rc = 0 134 | except: 135 | 136 | if M_INFO > 0: 137 | print("Warning: No broker found. Retry in one minute.") 138 | 139 | time.sleep(60) 140 | pass 141 | 142 | client.publish(topic_prefix+"/0/0/I_LOG_MESSAGE", "Connection with broker established.") 143 | 144 | sf = 1 145 | while sf == 1: 146 | try: 147 | ser = serial.Serial(port=serial_port, baudrate=serial_bps) 148 | sf = 0 149 | except: 150 | 151 | if M_INFO > 0: 152 | print("Warning: No serial port found. Retry in one minute.") 153 | 154 | client.publish(topic_prefix+"/0/0/I_LOG_MESSAGE", "No serial Gateway found but will retry in one minute.") 155 | time.sleep(60) 156 | pass 157 | 158 | # client.publish(topic_prefix+"/0/0/I_GATEWAY_READY", "MQTT Gateway startup complete.") 159 | while rc == 0: 160 | rv = '' 161 | 162 | try: 163 | while ser.inWaiting() > 0: 164 | ch = ser.read(1) 165 | rv += ch 166 | if ch=='\n': 167 | rv = rv.rstrip() 168 | 169 | if M_INFO > 1: 170 | print(" Serial in |"+rv+"|") 171 | 172 | parts = rv.split(";") # 2;0;1;0;0;25.0 173 | if len(parts) > 4: 174 | mysensor_data = '' 175 | payload = parts[5] 176 | if parts[2] == C_PRESENTATION: 177 | if int(parts[4]) < len(S_codes): 178 | mysensor_data = "S_"+S_codes[int(parts[4])] 179 | else: 180 | mysensor_data = "S_UNKNOWN" 181 | elif parts[2] == C_INTERNAL: 182 | if int(parts[4]) < len(I_codes): 183 | mysensor_data = "I_"+I_codes[int(parts[4])] 184 | else: 185 | mysensor_data = "I_UNKNOWN" 186 | else: 187 | if int(parts[4]) < len(V_codes): 188 | mysensor_data = "V_"+V_codes[int(parts[4])] 189 | else: 190 | mysensor_data = "V_UNKNOWN" 191 | if mysensor_data == "V_LIGHT": 192 | if int(parts[5]) < len(B_codes): 193 | payload = B_codes[int(parts[5])] # ON / OFF 194 | mytopic = topic_prefix+"/"+parts[0]+"/"+parts[1]+"/"+mysensor_data 195 | mypublish = mytopic+"/"+payload 196 | 197 | if M_INFO > 2: 198 | print(" New published |"+mypublish+"|") 199 | print(" MQTT published |"+mytopic+"|"+payload+"|") 200 | 201 | client.publish(mytopic, payload) 202 | rv = '' 203 | rc = client.loop() 204 | except: 205 | rc = 1 206 | pass 207 | 208 | ser.close() 209 | print("Warning: Communication error - Restarting.") 210 | --------------------------------------------------------------------------------