Mercurial > code > home > repos > homeauto
annotate service/rdf_to_mqtt/rdf_to_mqtt.py @ 1732:3f4b447d65f5
port to starlette/asyncio
author | drewp@bigasterisk.com |
---|---|
date | Mon, 10 Jul 2023 17:37:58 -0700 |
parents | 80b01d548b9c |
children | 09df2b4b886f |
rev | line source |
---|---|
597
e1ee6661329a
adjust kitchen PWM freqs. add comments and proposed contents of n3 configs
drewp@bigasterisk.com
parents:
586
diff
changeset
|
1 """ |
e1ee6661329a
adjust kitchen PWM freqs. add comments and proposed contents of n3 configs
drewp@bigasterisk.com
parents:
586
diff
changeset
|
2 We get output statements that are like light9's deviceAttrs (:dev1 :color "#ff0000"), |
e1ee6661329a
adjust kitchen PWM freqs. add comments and proposed contents of n3 configs
drewp@bigasterisk.com
parents:
586
diff
changeset
|
3 convert those to outputAttrs (:dev1 :red 255; :green 0; :blue 0) and post them to mqtt. |
e1ee6661329a
adjust kitchen PWM freqs. add comments and proposed contents of n3 configs
drewp@bigasterisk.com
parents:
586
diff
changeset
|
4 |
e1ee6661329a
adjust kitchen PWM freqs. add comments and proposed contents of n3 configs
drewp@bigasterisk.com
parents:
586
diff
changeset
|
5 This is like light9/bin/collector. |
e1ee6661329a
adjust kitchen PWM freqs. add comments and proposed contents of n3 configs
drewp@bigasterisk.com
parents:
586
diff
changeset
|
6 """ |
1732 | 7 import asyncio |
581
30022797642e
mqtt_graph_bridge to new build rules and to py3
drewp@bigasterisk.com
parents:
460
diff
changeset
|
8 import json |
1732 | 9 import os |
10 import time | |
777 | 11 |
1732 | 12 from prometheus_client import Counter, Gauge, Summary |
777 | 13 from rdflib import Namespace |
1732 | 14 from starlette_exporter import PrometheusMiddleware, handle_metrics |
15 from starlette.applications import Starlette | |
16 from starlette.requests import Request | |
17 from starlette.responses import PlainTextResponse | |
18 from starlette.routing import Route | |
19 from starlette.staticfiles import StaticFiles | |
20 import aiomqtt | |
21 | |
1703 | 22 from devs import devs |
694
925bc4137c93
extract rdfStatementsFromRequest for sharing with other tools
drewp@bigasterisk.com
parents:
597
diff
changeset
|
23 import rdf_over_http |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
24 |
1732 | 25 # from victorialogger import log |
26 import logging | |
27 | |
28 logging.basicConfig(level=logging.DEBUG) | |
29 log = logging.getLogger(__name__) | |
30 | |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
31 ROOM = Namespace('http://projects.bigasterisk.com/room/') |
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
32 |
1732 | 33 PUT_REQUESTS = Summary('put_requests', 'calls') |
34 STATEMENT = Summary('on_statement', 'calls') | |
35 MQTT_PUBLISH = Summary('mqtt_publish', 'calls') | |
36 | |
37 mqtt: aiomqtt.Client | None = None | |
762 | 38 |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
39 |
1732 | 40 class OutputPage: |
778
acf58b83022f
rdf_to_mqtt sylvania bulbs and code cleanup and k8s updates
drewp@bigasterisk.com
parents:
777
diff
changeset
|
41 |
1732 | 42 async def put(self, request: Request) -> PlainTextResponse: |
43 with PUT_REQUESTS.time(): | |
44 for stmt in rdf_over_http.rdfStatementsFromRequest(request.query_params, await request.body(), request.headers): | |
45 await self._onStatement(stmt) | |
46 return PlainTextResponse("ok") | |
694
925bc4137c93
extract rdfStatementsFromRequest for sharing with other tools
drewp@bigasterisk.com
parents:
597
diff
changeset
|
47 |
1732 | 48 @STATEMENT.time() |
49 async def _onStatement(self, stmt): | |
694
925bc4137c93
extract rdfStatementsFromRequest for sharing with other tools
drewp@bigasterisk.com
parents:
597
diff
changeset
|
50 log.info(f'incoming statement: {stmt}') |
392
79d041273e26
mqtt has two devices now. various older cleanups.
drewp@bigasterisk.com
parents:
378
diff
changeset
|
51 ignored = True |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
52 for dev, attrs in devs.items(): |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
53 if stmt[0] == ROOM['frontWindow']: |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
54 ignored = ignored and self._publishFrontScreenText(stmt) |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
55 if stmt[0:2] == (dev, ROOM['brightness']): |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
56 log.info(f'brightness request: {stmt}') |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
57 brightness = stmt[2].toPython() |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
58 |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
59 if attrs.get('values', '') == 'binary': |
1732 | 60 await self._publishOnOff(attrs, brightness) |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
61 else: |
1732 | 62 await self._publishRgbw(attrs, brightness) |
392
79d041273e26
mqtt has two devices now. various older cleanups.
drewp@bigasterisk.com
parents:
378
diff
changeset
|
63 ignored = False |
761 | 64 if stmt[0:2] == (dev, ROOM['inputSelector']): |
1732 | 65 choice = stmt[2].toPython() |
66 await self._publish(topic=attrs['root'], message=f'input_{choice}') | |
761 | 67 ignored = False |
68 if stmt[0:2] == (dev, ROOM['volumeChange']): | |
69 delta = int(stmt[2].toPython()) | |
70 which = 'up' if delta > 0 else 'down' | |
1732 | 71 await self._publish(topic=f'theater_blaster/ir_out/volume_{which}', message=json.dumps({'timed': abs(delta)})) |
761 | 72 ignored = False |
776 | 73 if stmt[0:2] == (dev, ROOM['color']): |
1732 | 74 msg = self._onColor(stmt[2].toPython(), attrs) |
75 await self._publish(topic=attrs['root'], message=json.dumps(msg)) | |
777 | 76 ignored = False |
778
acf58b83022f
rdf_to_mqtt sylvania bulbs and code cleanup and k8s updates
drewp@bigasterisk.com
parents:
777
diff
changeset
|
77 |
392
79d041273e26
mqtt has two devices now. various older cleanups.
drewp@bigasterisk.com
parents:
378
diff
changeset
|
78 if ignored: |
79d041273e26
mqtt has two devices now. various older cleanups.
drewp@bigasterisk.com
parents:
378
diff
changeset
|
79 log.warn("ignoring %s", stmt) |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
80 |
1732 | 81 def _onColor(self, h, attrs): |
82 if isinstance(h, bytes): | |
83 h = h.decode('utf8') | |
84 msg = {} | |
85 if h.endswith('K'): # accept "0.7*2200K" (brightness 0.7) | |
86 # see https://www.zigbee2mqtt.io/information/mqtt_topics_and_message_structure.html#zigbee2mqttfriendly_nameset | |
87 bright, kelvin = map(float, h[:-1].split('*')) | |
88 msg['state'] = 'ON' | |
89 msg["color_temp"] = round(1000000 / kelvin, 2) | |
90 msg['brightness'] = int(bright * 255) # 1..20 look about the same | |
91 else: | |
92 r, g, b = int(h[1:3], 16), int(h[3:5], 16), int(h[5:7], 16) | |
93 msg = { | |
94 'state': 'ON' if r or g or b else 'OFF', | |
95 'color': { | |
96 'r': r, | |
97 'g': g, | |
98 'b': b | |
99 }, | |
100 'brightness': max(r, g, b), | |
101 } | |
102 | |
103 if attrs.get('hasWhite', False): | |
104 msg['white_value'] = max(r, g, b) | |
105 msg.update(attrs.get('defaults', {})) | |
106 return msg | |
107 | |
108 async def _publishOnOff(self, attrs, brightness): | |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
109 msg = 'OFF' |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
110 if brightness > 0: |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
111 msg = 'ON' |
1732 | 112 await self._publish(topic=attrs['root'], message=msg) |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
113 |
1732 | 114 async def _publishRgbw(self, attrs, brightness): |
777 | 115 for chan, scale in [('w1', 1), ('r', 1), ('g', .8), ('b', .8)]: |
1732 | 116 await self._publish(topic=f"{attrs['root']}/light/kit_{chan}/command", messageJson={'state': 'ON', 'brightness': int(brightness * 255)}) |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
117 |
1732 | 118 async def _publishFrontScreenText(self, stmt): |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
119 ignored = True |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
120 for line in ['line1', 'line2', 'line3', 'line4']: |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
121 if stmt[1] == ROOM[line]: |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
122 ignored = False |
1732 | 123 assert mqtt is not None |
124 await mqtt.publish('frontwindow/%s' % line, stmt[2].toPython()) | |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
125 return ignored |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
126 |
1732 | 127 @MQTT_PUBLISH.time() |
128 async def _publish(self, topic: str, messageJson: object = None, message: str | None = None): | |
765
82bea37aeb92
fix theater input selector string assembling
drewp@bigasterisk.com
parents:
764
diff
changeset
|
129 log.debug(f'mqtt.publish {topic} {message} {messageJson}') |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
130 if messageJson is not None: |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
131 message = json.dumps(messageJson) |
1732 | 132 assert mqtt is not None |
133 await mqtt.publish(topic, message) | |
696
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
134 |
c52b172c0824
add publish to ON/OFF messages. split up the main statement handler
drewp@bigasterisk.com
parents:
694
diff
changeset
|
135 |
1732 | 136 def main(): |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
137 |
1732 | 138 async def start2(): |
139 global mqtt | |
140 async with aiomqtt.Client(os.environ.get('MOSQUITTO', "mosquitto-ext"), 1883, client_id="rdf_to_mqtt-%s" % time.time(), keepalive=6) as mqtt: | |
141 log.info(f'connected to mqtt {mqtt}') | |
142 while True: | |
143 await asyncio.sleep(5) | |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
144 |
1732 | 145 def start(): |
146 asyncio.create_task(start2()) | |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
147 |
1732 | 148 log.info('make app') |
149 app = Starlette(debug=True, | |
150 on_startup=[start], | |
151 routes=[ | |
152 Route('/', StaticFiles(directory='.', html=True)), | |
153 Route("/output", OutputPage().put, methods=["PUT"]), | |
154 ]) | |
155 app.add_middleware(PrometheusMiddleware, app_name='environment') | |
156 app.add_route("/metrics", handle_metrics) | |
157 log.info('return app') | |
158 return app | |
373
2158e7ad19b1
receive oneshot updates from reasoning; emit commands on MQTT to control H801 wifi dimmer
drewp@bigasterisk.com
parents:
diff
changeset
|
159 |
1732 | 160 |
161 app = main() |