--- /dev/null
+#!/usr/bin/python3
+
+import argparse
+from datetime import datetime
+import json
+import paho.mqtt.client as mqtt
+import random
+import sys
+import time
+
+
+class Thermostat:
+ connected = False
+ ident = ""
+ in_topic = ""
+ out_topic = ""
+ # mqc = mqtt client
+
+
+def on_connect(mqc, therm, flags, rc):
+ print(f'# Connect: rc={rc}')
+ if rc == 0:
+ therm.connected = True
+ mqc.subscribe(therm.in_topic, 0)
+ mqc.subscribe(therm.out_topic, 0)
+
+
+def on_disconnect(mqc, therm, rc):
+ print(f'# Disconnect: rc={rc}')
+ therm.connected = False
+
+
+def on_message(mqc, therm, msg):
+ t = datetime.now().isoformat()
+ print(f'@ {t} {msg.topic}')
+ print('\t' + msg.payload.hex(' '))
+
+ m = msg.payload
+ if len(m) > 6 and m[0] in (0x10, 0x30) and m[1] == 0x0a:
+ print(f'\t\t{m[:6].hex(" ")}')
+ i = 6
+ while i + 4 <= len(m):
+ ln = 256*m[i+2] + m[i+3]
+ if 4 + ln > len(m):
+ break
+ mj = m[i]
+ mn = m[i+1]
+ val = m[i+4:i+4+ln]
+ print(f'\t\t\t{mj:02x} {mn:02x} = {val.hex(" ")}')
+ if mj == 0x30 and mn == 0x0b and ln == 5:
+ t_desired = val[0] / 2
+ t_actual = val[1] + (val[2] & 0x0f) / 10
+ if val[2] & 0x10 != 0:
+ t_actual = -t_actual
+ if val[2] & 0x40 != 0:
+ t_actual = -999
+ analog = val[2] & 0x20 != 0
+ t_manual = val[3] / 2
+ t_auto = val[4] / 2
+ print(f'\t\t\t\t-> desired={t_desired} actual={t_actual} analog={analog} manual={t_manual} auto={t_auto}')
+ i += 4 + ln
+ if i != len(m):
+ print('\t\t!!! Partial Decode !!!')
+ else:
+ print('\t\t!!! Unknown Packet !!!')
+
+
+def on_subscribe(mqc, therm, mid, granted_qos):
+ print(f'# Subscribed: mid={mid}, granted={granted_qos}')
+
+
+def on_log(mqc, therm, level, string):
+ print(f'# Log[{level}]: {string}')
+
+
+def gen_client_id():
+ return 'APP_' + "".join(["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTUVWXYZ1234567890"[random.randrange(60)] for _ in range(18)])
+
+
+def read_device_config(config_file, device_name):
+ with open(config_file) as f:
+ js = json.load(f)
+ assert isinstance(js, list)
+ for cf in js:
+ assert isinstance(cf, dict)
+ if cf['name'] == device_name:
+ return cf
+ print('Device not found in configuration file', file=sys.stderr)
+ sys.exit(1)
+
+
+def init_therm(config):
+ therm = Thermostat()
+ therm.ident = str(config['uniqueIdentifier']).rjust(6, '0')
+ therm.in_topic = f'TSWIFI/I/{therm.ident}'
+ therm.out_topic = f'TSWIFI/O/{therm.ident}'
+
+ mqc = mqtt.Client(transport='websockets', client_id=gen_client_id(), userdata=therm)
+ mqc.on_message = on_message
+ mqc.on_connect = on_connect
+ mqc.on_disconnect = on_disconnect
+ mqc.on_subscribe = on_subscribe
+ mqc.on_log = on_log
+
+ mqc.ws_set_options("/wss")
+ mqc.tls_set_context()
+ mqc.username_pw_set(f'TSWIFI_{therm.ident}', config['mqttPass'])
+ mqc.connect("mqtt.elbock.cz", 8081, 60)
+
+ mqc.loop_start()
+
+ therm.mqc = mqc
+ return therm
+
+
+parser = argparse.ArgumentParser(description='A daemon watching Elektrobock thermostats')
+parser.add_argument('--config', required=True, type=str, help='Configuration file from the web app (devices.json)')
+parser.add_argument('--device', required=True, type=str, help='Device name as in the configuration file')
+
+args = parser.parse_args()
+
+config = read_device_config(args.config, args.device)
+therm = init_therm(config)
+
+time.sleep(1)
+while True:
+ if therm.connected:
+ print('Brum: Connected')
+ # Request message sent by the web interface, but with a random message ID
+ request = b'\x10\x0a' + random.randbytes(2) + b'\x00\x24\x20\x01\x00\x00\x20\x02\x00\x00\x20\x03\x00\x00\x20\x06\x00\x00\x20\x07\x00\x00\x20\x05\x00\x00\x20\x09\x00\x00\x20\x0b\x00\x00\x20\x0c\x00\x00'
+ therm.mqc.publish('TSWIFI/I/024299', request)
+ time.sleep(60)
+ else:
+ print('Brum: Not connected')
+ time.sleep(5)
+++ /dev/null
-#!/usr/bin/python3
-
-import argparse
-from datetime import datetime
-import paho.mqtt.client as mqtt
-import random
-import time
-
-
-connected = False
-
-
-def on_connect(mqc, userdata, flags, rc):
- print(f'# Connect: rc={rc}')
- if rc == 0:
- global connected
- connected = True
-
-
-def on_disconnect(mqc, userdata, rc):
- print(f'# Disconnect: rc={rc}')
- global connected
- connected = False
-
-
-def on_message(mqc, userdata, msg):
- t = datetime.now().isoformat()
- print(f'@ {t} {msg.topic}')
- print('\t' + msg.payload.hex(' '))
-
- m = msg.payload
- if len(m) > 6 and m[0] in (0x10, 0x30) and m[1] == 0x0a:
- print(f'\t\t{m[:6].hex(" ")}')
- i = 6
- while i + 4 <= len(m):
- ln = 256*m[i+2] + m[i+3]
- if 4 + ln > len(m):
- break
- mj = m[i]
- mn = m[i+1]
- val = m[i+4:i+4+ln]
- print(f'\t\t\t{mj:02x} {mn:02x} = {val.hex(" ")}')
- if mj == 0x30 and mn == 0x0b and ln == 5:
- t_desired = val[0] / 2
- t_actual = val[1] + (val[2] & 0x0f) / 10
- if val[2] & 0x10 != 0:
- t_actual = -t_actual
- if val[2] & 0x40 != 0:
- t_actual = -999
- analog = val[2] & 0x20 != 0
- t_manual = val[3] / 2
- t_auto = val[4] / 2
- print(f'\t\t\t\t-> desired={t_desired} actual={t_actual} analog={analog} manual={t_manual} auto={t_auto}')
- i += 4 + ln
- if i != len(m):
- print('\t\t!!! Partial Decode !!!')
- else:
- print('\t\t!!! Unknown Packet !!!')
-
-
-def on_subscribe(mqc, userdata, mid, granted_qos):
- print(f'# Subscribed: mid={mid}, granted={granted_qos}')
-
-
-def on_log(mqc, userdata, level, string):
- print(f'# Log[{level}]: {string}')
-
-
-def gen_client_id():
- return 'APP_' + "".join(["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTUVWXYZ1234567890"[random.randrange(60)] for _ in range(18)])
-
-
-mqc = mqtt.Client(transport='websockets', client_id=gen_client_id())
-mqc.on_message = on_message
-mqc.on_connect = on_connect
-mqc.on_disconnect = on_disconnect
-mqc.on_subscribe = on_subscribe
-mqc.on_log = on_log
-
-mqc.ws_set_options("/wss")
-mqc.tls_set_context()
-mqc.username_pw_set('TSWIFI_024299', 'iqjYBfrZGe')
-mqc.connect("mqtt.elbock.cz", 8081, 60)
-
-# FIXME: Move to connect callback
-mqc.subscribe("TSWIFI/I/024299", 0)
-mqc.subscribe("TSWIFI/O/024299", 0)
-
-mqc.loop_start()
-
-while True:
- if connected:
- print('Brum: Connected')
- # Request message sent by the web interface, but with a random message ID
- request = b'\x10\x0a' + random.randbytes(2) + b'\x00\x24\x20\x01\x00\x00\x20\x02\x00\x00\x20\x03\x00\x00\x20\x06\x00\x00\x20\x07\x00\x00\x20\x05\x00\x00\x20\x09\x00\x00\x20\x0b\x00\x00\x20\x0c\x00\x00'
- mqc.publish('TSWIFI/I/024299', request)
- time.sleep(60)
- else:
- print('Brum: Not connected')
- time.sleep(5)