#!/usr/bin/env python3 # Contact Bosh-Siemens Home Connect devices # and connect their messages to the mqtt server import json import sys import time import ssl from threading import Thread import click import click_config_file import paho.mqtt.client as mqtt from HCDevice import HCDevice from HCSocket import HCSocket, now @click.command() @click.option("-d", "--devices_file", default="config/devices.json") @click.option("-h", "--mqtt_host", default="localhost") @click.option("-p", "--mqtt_prefix", default="homeconnect/") @click.option("--mqtt_port", default=1883, type=int) @click.option("--mqtt_username") @click.option("--mqtt_password") @click.option("--mqtt_ssl", is_flag=True) @click.option("--mqtt_cafile") @click.option("--mqtt_certfile") @click.option("--mqtt_keyfile") @click.option("--mqtt_clientname", default="hcpy") @click_config_file.configuration_option() def hc2mqtt(devices_file: str, mqtt_host: str, mqtt_prefix: str, mqtt_port: int, mqtt_username: str, mqtt_password: str, mqtt_ssl: bool, mqtt_cafile: str, mqtt_certfile: str, mqtt_keyfile: str, mqtt_clientname: str): click.echo(f"Hello {devices_file=} {mqtt_host=} {mqtt_prefix=} {mqtt_port=} {mqtt_username=} {mqtt_password=} " f"{mqtt_ssl=} {mqtt_cafile=} {mqtt_certfile=} {mqtt_keyfile=} {mqtt_clientname=}") with open(devices_file, "r") as f: devices = json.load(f) client = mqtt.Client(mqtt_clientname) if mqtt_username and mqtt_password: client.username_pw_set(mqtt_username, mqtt_password) if mqtt_ssl: if mqtt_cafile and mqtt_certfile and mqtt_keyfile: client.tls_set(ca_certs=mqtt_cafile, certfile=mqtt_certfile, keyfile=mqtt_keyfile, cert_reqs=ssl.CERT_REQUIRED) else: client.tls_set(cert_reqs=ssl.CERT_NONE) client.connect(host=mqtt_host, port=mqtt_port, keepalive=70) for device in devices: mqtt_topic = mqtt_prefix + device["name"] print(now(), f"topic: {mqtt_topic}") thread = Thread(target=client_connect, args=(client, device, mqtt_topic)) thread.start() client.loop_forever() # Map their value names to easier state names topics = { "InternalError": "Error", "FatalErrorOccured": "Error", } global dev dev = {} def client_connect(client, device, mqtt_topic): def on_message(client, userdata, msg): print(msg.topic) mqtt_state = msg.payload.decode() mqtt_topic = msg.topic.split('/') print(now(), f"received mqtt message {mqtt_state}") try: msg = json.loads(mqtt_state) if 'uid' in msg: dev[mqtt_topic[-2]].get("/ro/values", 1, "POST", msg) else: raise Exception(f"Payload {msg} is not correctly formatted") except Exception as e: print("ERROR", e, file=sys.stderr) host = device["host"] device_topics = topics for value in device["features"]: if "access" in device["features"][value] and "read" in device["features"][value]['access'].lower(): name = device["features"][value]['name'].split(".") device_topics[name[-1]] = name[-1] device_topics[value] = name[-1] #sometimes the returned key is a digit, making translation possible state = {} for topic in device_topics: if not topic.isdigit(): #We only want the named topics state[device_topics[topic]] = None mqtt_set_topic = mqtt_topic + "/set" print(now(), device["name"], f"set topic: {mqtt_set_topic}") client.subscribe(mqtt_set_topic) client.on_message = on_message while True: try: print(now(), device["name"], f"connecting to {host}") ws = HCSocket(host, device["key"], device.get("iv",None)) dev[device["name"]] = HCDevice(ws, device.get("features", None), device["name"]) #ws.debug = True ws.reconnect() while True: msg = dev[device["name"]].recv() if msg is None: break if len(msg) > 0: print(now(), device["name"], msg) update = False for topic in device_topics: value = msg.get(topic, None) if value is None: continue # new_topic = topics[topic] # if new_topic == "remaining": # state["remainingseconds"] = value # value = "%d:%02d" % (value / 60 / 60, (value / 60) % 60) new_topic = device_topics[topic] state[new_topic] = value update = True if not update: continue msg = json.dumps(state) print(now(), device["name"], f"publish to {mqtt_topic} with {msg}") client.publish(mqtt_topic + "/state", msg) except Exception as e: print("ERROR", host, e, file=sys.stderr) time.sleep(5) if __name__ == "__main__": hc2mqtt()