0
|
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()
|