summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/relay_mqtt_http_proxy.md20
-rw-r--r--src/home/mqtt/relay.py8
-rw-r--r--src/relay_mqtt_http_proxy.py68
3 files changed, 93 insertions, 3 deletions
diff --git a/doc/relay_mqtt_http_proxy.md b/doc/relay_mqtt_http_proxy.md
new file mode 100644
index 0000000..dfd0c90
--- /dev/null
+++ b/doc/relay_mqtt_http_proxy.md
@@ -0,0 +1,20 @@
+## config example
+
+```
+[mqtt]
+host = "mqtt.solarmon.ru"
+port = 8883
+
+client_id = "relay_mqtt_util"
+username = ""
+password = ""
+
+[relay]
+devices = ['id1', 'id2']
+
+[server]
+listen = "0.0.0.0:8821"
+
+[logging]
+verbose = false
+``` \ No newline at end of file
diff --git a/src/home/mqtt/relay.py b/src/home/mqtt/relay.py
index 0c6084f..53d43e4 100644
--- a/src/home/mqtt/relay.py
+++ b/src/home/mqtt/relay.py
@@ -86,11 +86,13 @@ class MQTTRelay(MQTTBase):
except Exception as e:
self._logger.exception(str(e))
- def set_power(self, device_id, enable: bool):
+ def set_power(self, device_id, enable: bool, secret=None):
device = next(d for d in self._devices if d.id == device_id)
- assert device.secret is not None, 'device secret not specified'
+ secret = secret if secret else device.secret
+
+ assert secret is not None, 'device secret not specified'
- payload = PowerPayload(secret=device.secret,
+ payload = PowerPayload(secret=secret,
state=enable)
self._client.publish(f'hk/{device.id}/relay/power',
payload=payload.pack(),
diff --git a/src/relay_mqtt_http_proxy.py b/src/relay_mqtt_http_proxy.py
new file mode 100644
index 0000000..ab8a138
--- /dev/null
+++ b/src/relay_mqtt_http_proxy.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+from home import http
+from home.config import config
+from home.mqtt import MQTTRelay, MQTTRelayDevice, MQTTRelayState
+from home.mqtt.payload import MQTTPayload
+from home.mqtt.payload.relay import InitialStatPayload, StatPayload
+from typing import Optional
+from home.util import parse_addr
+
+mqtt_relay: Optional[MQTTRelay] = None
+relay_states: dict[str, MQTTRelayState] = {}
+
+
+def on_mqtt_message(device_id, message: MQTTPayload):
+ if isinstance(message, InitialStatPayload) or isinstance(message, StatPayload):
+ kwargs = dict(rssi=message.rssi, enabled=message.flags.state)
+ if device_id not in relay_states:
+ relay_states[device_id] = MQTTRelayState()
+ relay_states[device_id].update(**kwargs)
+
+
+class RelayMqttHttpProxy(http.HTTPServer):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.get('/relay/{id}/on', self.relay_on)
+ self.get('/relay/{id}/off', self.relay_off)
+ self.get('/relay/{id}/toggle', self.relay_toggle)
+
+ async def _relay_on_off(self,
+ enable: Optional[bool],
+ req: http.Request):
+ device_id = req.match_info['id']
+ device_secret = req.query['secret']
+
+ if enable is None:
+ if device_id in relay_states and relay_states[device_id].ever_updated:
+ cur_state = relay_states[device_id].enabled
+ else:
+ cur_state = False
+ enable = not cur_state
+
+ mqtt_relay.set_power(device_id, enable, device_secret)
+ return self.ok()
+
+ async def relay_on(self, req: http.Request):
+ return await self._relay_on_off(True, req)
+
+ async def relay_off(self, req: http.Request):
+ return await self._relay_on_off(False, req)
+
+ async def relay_toggle(self, req: http.Request):
+ return await self._relay_on_off(None, req)
+
+
+if __name__ == '__main__':
+ config.load('relay_mqtt_http_proxy')
+
+ mqtt_relay = MQTTRelay(devices=[MQTTRelayDevice(id=device_id) for device_id in config.get('relay.devices')])
+ mqtt_relay.configure_tls()
+ mqtt_relay.set_message_callback(on_mqtt_message)
+ mqtt_relay.connect_and_loop(loop_forever=False)
+
+ proxy = RelayMqttHttpProxy(parse_addr(config.get('server.listen')))
+ try:
+ proxy.run()
+ except KeyboardInterrupt:
+ mqtt_relay.disconnect()
+