comparison light_bridge.py @ 0:5a77696c6dab

start
author drewp@bigasterisk.com
date Sun, 28 Jan 2024 15:32:18 -0800
parents
children 42a494b8ee1a
comparison
equal deleted inserted replaced
-1:000000000000 0:5a77696c6dab
1 """
2 replaces a lot of mqtt_to_rdf and rdf_to_mqtt
3 """
4 import asyncio
5 from dataclasses import dataclass
6 from functools import partial
7 import json
8 import logging
9
10 import background_loop
11 from patchablegraph import PatchableGraph
12 from patchablegraph.handler import GraphEvents, StaticGraph
13 from rdflib import Namespace
14 from starlette.applications import Starlette
15 from starlette.requests import Request
16 from starlette.responses import JSONResponse
17 from starlette.routing import Route
18 from starlette_exporter import PrometheusMiddleware, handle_metrics
19 from sse_starlette.sse import EventSourceResponse
20
21 EX = Namespace('http://example.com/')
22
23 logging.basicConfig(level=logging.INFO)
24 log = logging.getLogger()
25
26
27 class Color(str):
28
29 def to_js(self):
30 return self
31
32
33 @dataclass
34 class Light:
35 name: str
36 address: str
37 online: bool
38 colorRequest: Color
39 colorMessage: dict
40 colorCurrent: Color
41 latencyMs: float
42
43 def to_js(self):
44 return {
45 'light': {
46 'name': self.name,
47 'address': self.address,
48 'online': self.online,
49 'colorRequest': self.colorRequest.to_js(),
50 'colorMessage': self.colorMessage,
51 'colorCurrent': self.colorCurrent.to_js(),
52 'latencyMs': self.latencyMs,
53 }
54 }
55
56
57 class Lights:
58 _d: dict[str, Light] = {}
59
60 def add(self, d: Light):
61 self._d[d.name] = d
62
63 def byName(self, name: str) -> Light:
64 return self._d[name]
65
66 async def changes(self): # yields None on any data change
67 while True:
68 yield None
69 await asyncio.sleep(1)
70
71 def to_js(self):
72 return {'lights': [d.to_js() for d in sorted(self._d.values(), key=lambda r: r.name)]}
73
74
75 async def output(lights: Lights, request: Request) -> JSONResponse:
76 light = lights.byName(request.query_params['light'])
77 return JSONResponse(light.to_js())
78
79
80 async def table(lights: Lights, req: Request) -> EventSourceResponse:
81
82 async def g():
83 async for ping in lights.changes():
84 yield json.dumps(lights.to_js())
85 await asyncio.sleep(1)
86
87 return EventSourceResponse(g())
88
89
90 def main():
91 lights = Lights()
92 lights.add(Light('do-desk', 'topic1', True, Color('#ff0000'), {'r': 255}, Color('#000000'), 100))
93 lights.add(Light('do-desk2', 'topic2', True, Color('#ff00ff'), {'r': 255}, Color('#000000'), 200))
94 graph = PatchableGraph()
95 app = Starlette(debug=True,
96 routes=[
97 Route('/api/output', partial(output, lights), methods=['PUT']),
98 Route('/api/table', partial(table, lights)),
99 Route('/api/graph', StaticGraph(graph)),
100 Route('/api/graph/events', GraphEvents(graph)),
101 ])
102
103 app.add_middleware(PrometheusMiddleware, app_name='light-bridge')
104 app.add_route("/metrics", handle_metrics)
105 # app.state.loop = background_loop.loop_forever(lambda first_run=False: update(graph), 1)
106
107 return app
108
109
110 app = main()