2023.9.12.1
This commit is contained in:
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"githubPullRequests.ignoredPullRequestBranches": [
|
||||||
|
"main"
|
||||||
|
]
|
||||||
|
}
|
||||||
51
README.md
51
README.md
@@ -71,22 +71,7 @@ library.
|
|||||||
|
|
||||||
Example message published to `homeconnect/dishwasher`:
|
Example message published to `homeconnect/dishwasher`:
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"state": "Run",
|
|
||||||
"door": "Closed",
|
|
||||||
"remaining": "2:49",
|
|
||||||
"power": true,
|
|
||||||
"lowwaterpressure": false,
|
|
||||||
"aquastop": false,
|
|
||||||
"error": false,
|
|
||||||
"remainingseconds": 10140
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Full state information</summary>
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
'AllowBackendConnection': False,
|
'AllowBackendConnection': False,
|
||||||
@@ -154,7 +139,6 @@ Example message published to `homeconnect/dishwasher`:
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
### Clothes washer
|
### Clothes washer
|
||||||
|
|
||||||

|

|
||||||
@@ -167,21 +151,7 @@ binary data over the websocket (type 0x82).
|
|||||||
|
|
||||||
Example message published to `homeconnect/washer`:
|
Example message published to `homeconnect/washer`:
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"state": "Ready",
|
|
||||||
"door": "Closed",
|
|
||||||
"remaining": "3:48",
|
|
||||||
"power": true,
|
|
||||||
"lowwaterpressure": false,
|
|
||||||
"aquastop": false,
|
|
||||||
"error": false,
|
|
||||||
"remainingseconds": 13680
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Full state information</summary>
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
@@ -252,11 +222,9 @@ Example message published to `homeconnect/washer`:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
The coffee machine needs a better mapping to MQTT messages.
|
Example message published to `homeconnect/coffeemaker`:
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Full state information</summary>
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
'LastSelectedBeverage': 8217,
|
'LastSelectedBeverage': 8217,
|
||||||
@@ -400,3 +368,20 @@ Synchronize with time server, `false` is disabled
|
|||||||
## FRIDA tools
|
## FRIDA tools
|
||||||
|
|
||||||
Moved to [`README-frida.md`](README-frida.md)
|
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
|
||||||
13
changelog.md
Normal file
13
changelog.md
Normal file
@@ -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
|
||||||
60
hc2mqtt
60
hc2mqtt
@@ -12,7 +12,6 @@ import paho.mqtt.client as mqtt
|
|||||||
from HCDevice import HCDevice
|
from HCDevice import HCDevice
|
||||||
from HCSocket import HCSocket, now
|
from HCSocket import HCSocket, now
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.argument("config_file")
|
@click.argument("config_file")
|
||||||
@click.option("-h", "--mqtt_host", default="localhost")
|
@click.option("-h", "--mqtt_host", default="localhost")
|
||||||
@@ -24,15 +23,16 @@ from HCSocket import HCSocket, now
|
|||||||
@click.option("--mqtt_cafile")
|
@click.option("--mqtt_cafile")
|
||||||
@click.option("--mqtt_certfile")
|
@click.option("--mqtt_certfile")
|
||||||
@click.option("--mqtt_keyfile")
|
@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,
|
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=} "
|
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:
|
with open(config_file, "r") as f:
|
||||||
devices = json.load(f)
|
devices = json.load(f)
|
||||||
|
|
||||||
client = mqtt.Client()
|
client = mqtt.Client(mqtt_clientname)
|
||||||
|
|
||||||
if mqtt_username and mqtt_password:
|
if mqtt_username and mqtt_password:
|
||||||
client.username_pw_set(mqtt_username, 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
|
# Map their value names to easier state names
|
||||||
topics = {
|
topics = {
|
||||||
"OperationState": "state",
|
"InternalError": "Error",
|
||||||
"DoorState": "door",
|
"FatalErrorOccured": "Error",
|
||||||
"RemainingProgramTime": "remaining",
|
|
||||||
"PowerState": "power",
|
|
||||||
"LowWaterPressure": "lowwaterpressure",
|
|
||||||
"AquaStopOccured": "aquastop",
|
|
||||||
"InternalError": "error",
|
|
||||||
"FatalErrorOccured": "error",
|
|
||||||
}
|
}
|
||||||
|
global dev
|
||||||
|
dev = {}
|
||||||
|
|
||||||
def client_connect(client, device, mqtt_topic):
|
def client_connect(client, device, mqtt_topic):
|
||||||
def on_message(client, userdata, msg):
|
def on_message(client, userdata, msg):
|
||||||
global dev
|
print(msg.topic)
|
||||||
mqtt_state = msg.payload.decode()
|
mqtt_state = msg.payload.decode()
|
||||||
|
mqtt_topic = msg.topic.split('/')
|
||||||
print(now(),f"received mqtt message {mqtt_state}")
|
print(now(),f"received mqtt message {mqtt_state}")
|
||||||
try:
|
try:
|
||||||
msg = json.loads(mqtt_state)
|
msg = json.loads(mqtt_state)
|
||||||
if 'uid' in msg:
|
if 'uid' in msg:
|
||||||
dev.get("/ro/values",1,"POST",msg)
|
dev[mqtt_topic[-2]].get("/ro/values",1,"POST",msg)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Payload {msg} is not correctly formatted")
|
raise Exception(f"Payload {msg} is not correctly formatted")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("ERROR", e, file=sys.stderr)
|
print("ERROR", e, file=sys.stderr)
|
||||||
|
|
||||||
global dev
|
|
||||||
host = device["host"]
|
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 = {}
|
state = {}
|
||||||
for topic in topics:
|
for topic in device_topics:
|
||||||
state[topics[topic]] = None
|
if not topic.isdigit(): #We only want the named topics
|
||||||
|
state[device_topics[topic]] = None
|
||||||
|
|
||||||
mqtt_set_topic = mqtt_topic + "/set"
|
mqtt_set_topic = mqtt_topic + "/set"
|
||||||
print(now(), device["name"], f"set topic: {mqtt_set_topic}")
|
print(now(), device["name"], f"set topic: {mqtt_set_topic}")
|
||||||
@@ -93,36 +97,32 @@ def client_connect(client, device, mqtt_topic):
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
print(now(), device["name"], f"connecting to {host}")
|
||||||
ws = HCSocket(host, device["key"], device.get("iv",None))
|
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.debug = True
|
||||||
ws.reconnect()
|
ws.reconnect()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
msg = dev.recv()
|
msg = dev[device["name"]].recv()
|
||||||
if msg is None:
|
if msg is None:
|
||||||
break
|
break
|
||||||
if len(msg) > 0:
|
if len(msg) > 0:
|
||||||
print(now(), device["name"], msg)
|
print(now(), device["name"], msg)
|
||||||
|
|
||||||
update = False
|
update = False
|
||||||
for topic in topics:
|
for topic in device_topics:
|
||||||
value = msg.get(topic, None)
|
value = msg.get(topic, None)
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Convert "On" to True, "Off" to False
|
# new_topic = topics[topic]
|
||||||
if value == "On":
|
# if new_topic == "remaining":
|
||||||
value = True
|
# state["remainingseconds"] = value
|
||||||
elif value == "Off":
|
# value = "%d:%02d" % (value / 60 / 60, (value / 60) % 60)
|
||||||
value = False
|
|
||||||
|
|
||||||
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
|
state[new_topic] = value
|
||||||
update = True
|
update = True
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user