diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a490b61 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 9cc4bb8..d36f403 100644 --- a/README.md +++ b/README.md @@ -71,22 +71,7 @@ library. Example message published to `homeconnect/dishwasher`: -``` -{ - "state": "Run", - "door": "Closed", - "remaining": "2:49", - "power": true, - "lowwaterpressure": false, - "aquastop": false, - "error": false, - "remainingseconds": 10140 -} -``` -
-Full state information - ``` { 'AllowBackendConnection': False, @@ -154,7 +139,6 @@ Example message published to `homeconnect/dishwasher`: ```
- ### Clothes washer ![laptop in a clothes washer](images/clotheswasher.jpg) @@ -167,21 +151,7 @@ binary data over the websocket (type 0x82). Example message published to `homeconnect/washer`: -``` -{ - "state": "Ready", - "door": "Closed", - "remaining": "3:48", - "power": true, - "lowwaterpressure": false, - "aquastop": false, - "error": false, - "remainingseconds": 13680 -} -``` -
-Full state information ``` { @@ -252,11 +222,9 @@ Example message published to `homeconnect/washer`: ![Image of the coffee machine from the Siemens website](images/coffee.jpg) -The coffee machine needs a better mapping to MQTT messages. +Example message published to `homeconnect/coffeemaker`:
-Full state information - ``` { 'LastSelectedBeverage': 8217, @@ -400,3 +368,20 @@ Synchronize with time server, `false` is disabled ## FRIDA tools Moved to [`README-frida.md`](README-frida.md) + +## Home assistant + +For integration with Home Assistant, the following MQTT sensor can be used to create a read only sensor + +```yaml +- unique_id: "coffee_machine" + name: "Coffee Machine" + state_topic: "homeconnect/coffeemaker/state" + value_template: "{{ value_json.PowerState }}" + json_attributes_topic: "homeconnect/coffeemaker/state" + json_attributes_template: "{{ value_json | tojson }}" +``` + +## Notes +- Sometimes when the device is off, there is the error `ERROR [ip] [Errno 113] No route to host` +- There is a lot more information available, like the status of a program that is currently active. This needs to be integrated if possible. For now only the values that relate to the `config.json` are published \ No newline at end of file diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..62f0807 --- /dev/null +++ b/changelog.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## 2023.9.12.1 +### Added +- Ability to configure MQTT clientname + +### Changed +- There was a default set of values being published. Now the device publishes what is present as access read, or readWrite in the `config.json` + +### Fixed +- MQTT was not always published to the correct topic diff --git a/hc2mqtt b/hc2mqtt index f8ba704..6a6f357 100755 --- a/hc2mqtt +++ b/hc2mqtt @@ -12,7 +12,6 @@ import paho.mqtt.client as mqtt from HCDevice import HCDevice from HCSocket import HCSocket, now - @click.command() @click.argument("config_file") @click.option("-h", "--mqtt_host", default="localhost") @@ -24,15 +23,16 @@ from HCSocket import HCSocket, now @click.option("--mqtt_cafile") @click.option("--mqtt_certfile") @click.option("--mqtt_keyfile") +@click.option("--mqtt_clientname") def hc2mqtt(config_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_password: str, mqtt_ssl: bool, mqtt_cafile: str, mqtt_certfile: str, mqtt_keyfile: str, mqtt_clientname: str): click.echo(f"Hello {config_file=} {mqtt_host=} {mqtt_prefix=} {mqtt_port=} {mqtt_username=} {mqtt_password=} " - f"{mqtt_ssl=} {mqtt_cafile=} {mqtt_certfile=} {mqtt_keyfile=}") + f"{mqtt_ssl=} {mqtt_cafile=} {mqtt_certfile=} {mqtt_keyfile=} {mqtt_clientname=}") with open(config_file, "r") as f: devices = json.load(f) - client = mqtt.Client() + client = mqtt.Client(mqtt_clientname) if mqtt_username and mqtt_password: client.username_pw_set(mqtt_username, mqtt_password) @@ -55,36 +55,40 @@ def hc2mqtt(config_file: str, mqtt_host: str, mqtt_prefix: str, mqtt_port: int, # Map their value names to easier state names topics = { - "OperationState": "state", - "DoorState": "door", - "RemainingProgramTime": "remaining", - "PowerState": "power", - "LowWaterPressure": "lowwaterpressure", - "AquaStopOccured": "aquastop", - "InternalError": "error", - "FatalErrorOccured": "error", + "InternalError": "Error", + "FatalErrorOccured": "Error", } +global dev +dev = {} def client_connect(client, device, mqtt_topic): def on_message(client, userdata, msg): - global dev + 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.get("/ro/values",1,"POST",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) - global dev 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 topics: - state[topics[topic]] = None + 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}") @@ -93,36 +97,32 @@ def client_connect(client, device, mqtt_topic): while True: try: + print(now(), device["name"], f"connecting to {host}") ws = HCSocket(host, device["key"], device.get("iv",None)) - dev = HCDevice(ws, device.get("features", None), device["name"]) + dev[device["name"]] = HCDevice(ws, device.get("features", None), device["name"]) #ws.debug = True ws.reconnect() while True: - msg = dev.recv() + msg = dev[device["name"]].recv() if msg is None: break if len(msg) > 0: print(now(), device["name"], msg) update = False - for topic in topics: + for topic in device_topics: value = msg.get(topic, None) if value is None: continue - # Convert "On" to True, "Off" to False - if value == "On": - value = True - elif value == "Off": - value = False - - new_topic = topics[topic] - if new_topic == "remaining": - state["remainingseconds"] = value - value = "%d:%02d" % (value / 60 / 60, (value / 60) % 60) + # 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