Mercurial > code > home > repos > light-bridge
changeset 11:028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
author | drewp@bigasterisk.com |
---|---|
date | Sun, 28 Jan 2024 17:31:06 -0800 |
parents | 140633abfa2a |
children | 7cc004eafb82 |
files | light.py light_bridge.py src/main.ts |
diffstat | 3 files changed, 74 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/light.py Sun Jan 28 16:53:35 2024 -0800 +++ b/light.py Sun Jan 28 17:31:06 2024 -0800 @@ -1,6 +1,7 @@ import asyncio import logging from dataclasses import dataclass, field +from typing import Callable from color import Color @@ -33,6 +34,8 @@ online: bool | None = None latencyMs: float | None = None + notifyChanged: Callable | None = None + def __post_init__(self): self.requestingDeviceColor = self.deviceColor(self.requestingColor) @@ -54,27 +57,50 @@ return DeviceColor(r=c.r, g=c.g, b=c.b) async def setColor(self, c: Color): + log.info(f'setColor from {self.requestingColor} to {c}') + if c == self.requestingColor: + return self.requestingColor = c self.requestingDeviceColor = self.deviceColor(self.requestingColor) + if self.notifyChanged: + self.notifyChanged() class Lights: _d: dict[str, Light] = {} + _changeSleepTask: asyncio.Task | None = None def __init__(self): self.add(Light('do-desk', 'topic1')) self.add(Light('do-desk2', 'topic2')) def add(self, d: Light): + d.notifyChanged = self.notifyChanged self._d[d.name] = d + self.notifyChanged() + def byName(self, name: str) -> Light: return self._d[name] async def changes(self): # yields None on any data change while True: + log.info('Lights has a change') yield None - await asyncio.sleep(1) # todo + self._changeSleepTask = asyncio.create_task(self._sleep()) + try: + await self._changeSleepTask + except asyncio.CancelledError: + pass + self._changeSleepTask = None + + async def _sleep(self): + await asyncio.sleep(100) + + def notifyChanged(self): + log.info('Lights.notifyChanged()') + if self._changeSleepTask is not None: + self._changeSleepTask.cancel() def to_dict(self): return {'lights': [d.to_dict() for d in sorted(self._d.values(), key=lambda r: r.name)]}
--- a/light_bridge.py Sun Jan 28 16:53:35 2024 -0800 +++ b/light_bridge.py Sun Jan 28 17:31:06 2024 -0800 @@ -1,6 +1,7 @@ """ replaces a lot of mqtt_to_rdf and rdf_to_mqtt """ +import asyncio import json import logging import time @@ -31,9 +32,15 @@ async def table(lights: Lights, req: Request) -> EventSourceResponse: + def updateMessage(): + return json.dumps({'now': time.time()} | lights.to_dict()) + async def g(): - async for ping in lights.changes(): - yield json.dumps({'now': time.time()} | lights.to_dict()) + yield updateMessage() + async for ping in lights.changes(): # broken if there's more than one caller! + log.info('send table event') + yield updateMessage() + await asyncio.sleep(.5) # slow down the inf loop return EventSourceResponse(g())
--- a/src/main.ts Sun Jan 28 16:53:35 2024 -0800 +++ b/src/main.ts Sun Jan 28 17:31:06 2024 -0800 @@ -1,6 +1,16 @@ import { LitElement, css, html } from "lit"; import { customElement, state } from "lit/decorators.js"; +interface Light { + name: string; + address: string; + requestingColor: string; + requestingDeviceColor: object; + emittingColor: string; + online: boolean; + latencyMs: number; +} + @customElement("lb-page") export class LbPage extends LitElement { static styles = [ @@ -39,7 +49,8 @@ ]; // bug https://github.com/lit/lit/issues/4305 - @((state as any)()) lights: object[] = []; + @((state as any)()) lights: Light[] = []; + @((state as any)()) lightByName: Map<string, Light> = new Map(); @((state as any)()) reportTime: Date = new Date(0); connectedCallback(): void { @@ -47,10 +58,18 @@ const es = new EventSource("api/table"); es.onmessage = (ev) => { const body = JSON.parse(ev.data); - this.lights = body.lights; + this.lights = body.lights.map((wrappedLight: any) => wrappedLight.light as Light); + this.rebuildLightByName(); this.reportTime = new Date(body.now * 1000); }; } + private rebuildLightByName() { + this.lightByName = new Map(); + this.lights.forEach((light: any) => { + this.lightByName.set(light.name, light); + }); + } + render() { return html` <h1>Light-bridge</h1> @@ -66,18 +85,18 @@ <th class="col-group-3">latency</th> </tr> ${this.lights.map( - (d: any) => html` + (d: Light) => html` <tr> - <td class="col-group-1">${d.light.name}</td> - <td class="col-group-1"><code>${d.light.address}</code></td> + <td class="col-group-1">${d.name}</td> + <td class="col-group-1"><code>${d.address}</code></td> <td class="col-group-2"> - <code>${d.light.requestingColor}</code> - <input type="color" @input=${this.onRequestingColor.bind(this, d.light.name)} .value="${d.light.requestingColor}" /> + <code>${d.requestingColor}</code> + <input type="color" @input=${this.onRequestingColor.bind(this, d.name)} .value="${d.requestingColor}" /> </td> - <td class="col-group-2"><code>${JSON.stringify(d.light.requestingDeviceColor)}</code></td> - <td class="col-group-3">${d.light.emittingColor} <span class="color" style="background: ${d.light.emittingColor}"></span></td> - <td class="col-group-3">${d.light.online ? "✔" : ""}</td> - <td class="col-group-3">${d.light.latencyMs} ms</td> + <td class="col-group-2"><code>${JSON.stringify(d.requestingDeviceColor)}</code></td> + <td class="col-group-3">${d.emittingColor} <span class="color" style="background: ${d.emittingColor}"></span></td> + <td class="col-group-3">${d.online ? "✔" : ""}</td> + <td class="col-group-3">${d.latencyMs} ms</td> </tr> ` )} @@ -90,10 +109,17 @@ <bigast-loginbar></bigast-loginbar> `; } - onRequestingColor(lightName: string, ev: InputEvent) { + async onRequestingColor(lightName: string, ev: InputEvent) { + const currentRequest = this.lightByName.get(lightName)!.requestingColor; const value = (ev.target as HTMLInputElement).value; + console.log("LbPage ~ onRequestingColor ~ value === currentRequest:", value, currentRequest); + if (value === currentRequest) { + return; + } const url = new URL("api/output", location as any); url.searchParams.append("light", lightName); fetch(url, { method: "PUT", body: value }); // todo: only run one fetch at a time, per light + + // sometime after this, the SSE table will send us back the change we made (probably) } }