forked from osresearch/hcpy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
hc2mqtt
executable file
·142 lines (115 loc) · 4.19 KB
/
hc2mqtt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python3
# Contact Bosh-Siemens Home Connect devices
# and connect their messages to the mqtt server
import json
import sys
import time
from threading import Thread
import click
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")
@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")
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_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=} {mqtt_clientname=}")
with open(config_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()