Mercurial > code > home > repos > light-bridge
annotate light.py @ 29:35affd4d37d4 default tip
add 1st ikea color light
author | drewp@bigasterisk.com |
---|---|
date | Sat, 14 Dec 2024 22:36:29 -0800 |
parents | fb2e91f230f4 |
children |
rev | line source |
---|---|
2 | 1 import asyncio |
2 import logging | |
13
1c865af058e7
start make* funcs and add links to light addresses
drewp@bigasterisk.com
parents:
12
diff
changeset
|
3 from dataclasses import dataclass |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
4 from typing import Callable |
2 | 5 |
5
7eeda7f4f9cd
spell it to_dict, for compat with DataClassJsonMixin
drewp@bigasterisk.com
parents:
4
diff
changeset
|
6 from color import Color |
21 | 7 from color_convert import DeviceColor, brightnessConv, ikeaWhiteConv, oneWhiteConv, relayConv, twoWhitesConv, zbConv |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
8 from mqtt_io import MqttIo |
29 | 9 from protocols import ShellyGen1WebTransport, SonoffRelayTransport, TasmotaWebTransport, Transport, WledControl, WledTransport, ZigbeeTransport, espColorMessage, zbBrightnessMessage, zbColorMessage, zbRelayMessage, zbWhiteSpectrumMessage |
5
7eeda7f4f9cd
spell it to_dict, for compat with DataClassJsonMixin
drewp@bigasterisk.com
parents:
4
diff
changeset
|
10 |
20 | 11 log = logging.getLogger('lite') |
2 | 12 |
13 | |
14 @dataclass | |
15 class Light: | |
16 name: str | |
17 | 17 transport: Transport |
18 convertColor: Callable[[Color], DeviceColor] | |
9 | 19 |
20 requestingColor: Color = Color.fromHex('#000000') | |
21 requestingDeviceColor: DeviceColor = DeviceColor() | |
22 | |
23 emittingColor: Color = Color.fromHex('#000000') | |
24 online: bool | None = None | |
25 latencyMs: float | None = None | |
26 | |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
27 notifyChanged: Callable | None = None |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
28 |
9 | 29 def __post_init__(self): |
17 | 30 self.requestingDeviceColor = self.convertColor(self.requestingColor) |
2 | 31 |
5
7eeda7f4f9cd
spell it to_dict, for compat with DataClassJsonMixin
drewp@bigasterisk.com
parents:
4
diff
changeset
|
32 def to_dict(self): |
9 | 33 d = { |
34 'name': self.name, | |
17 | 35 'address': self.transport.linked(), |
9 | 36 'requestingColor': self.requestingColor.hex(), |
37 'requestingDeviceColor': self.requestingDeviceColor.summary(), | |
38 'emittingColor': self.emittingColor.hex(), | |
39 'online': self.online, | |
40 'latencyMs': self.latencyMs, | |
2 | 41 } |
42 | |
9 | 43 return {'light': d} |
44 | |
45 async def setColor(self, c: Color): | |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
46 if c == self.requestingColor: |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
47 return |
9 | 48 self.requestingColor = c |
20 | 49 dc = self.convertColor(self.requestingColor) |
50 | |
51 log.info(f'setColor from {self.requestingColor} to {c} = {dc.summary()=}') | |
52 if dc != self.requestingDeviceColor: | |
53 self.requestingDeviceColor = dc | |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
54 |
20 | 55 if self.notifyChanged: |
56 self.notifyChanged() | |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
57 |
20 | 58 # waits for the relevant round-trip |
59 log.info(f'transport send {self.requestingDeviceColor.summary()}') | |
60 await self.transport.send(self.requestingDeviceColor) | |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
61 |
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
62 self.emittingColor = self.requestingColor |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
63 if self.notifyChanged: |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
64 self.notifyChanged() |
9 | 65 |
2 | 66 |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
67 def makeZbBar(mqtt: MqttIo, name: str, ieee: str) -> Light: |
17 | 68 return Light(name=name, convertColor=zbConv, transport=ZigbeeTransport(mqtt, name, ieee)) |
13
1c865af058e7
start make* funcs and add links to light addresses
drewp@bigasterisk.com
parents:
12
diff
changeset
|
69 |
1c865af058e7
start make* funcs and add links to light addresses
drewp@bigasterisk.com
parents:
12
diff
changeset
|
70 |
20 | 71 def makeTasmota(name: str, hostname: str) -> Light: |
72 return Light(name=name, convertColor=twoWhitesConv, transport=TasmotaWebTransport(hostname)) | |
73 | |
74 | |
29 | 75 def makeShellyRGBWW(name: str, hostname: str) -> Light: |
20 | 76 return Light(name=name, convertColor=oneWhiteConv, transport=ShellyGen1WebTransport(hostname)) |
77 | |
78 | |
79 def makeSonoffRelay(mqtt: MqttIo, name: str, topic: str) -> Light: | |
80 return Light(name=name, convertColor=relayConv, transport=SonoffRelayTransport(mqtt, topic)) | |
81 | |
82 | |
21 | 83 def makeZbIkeaWhiteTemp(mqtt: MqttIo, name: str, ieee: str) -> Light: |
84 return Light(name=name, convertColor=ikeaWhiteConv, transport=ZigbeeTransport(mqtt, name, ieee, msg=zbWhiteSpectrumMessage)) | |
85 | |
86 | |
29 | 87 def makeZbIkeaRGBWW(mqtt: MqttIo, name: str, ieee: str) -> Light: |
88 return Light(name=name, convertColor=zbConv, transport=ZigbeeTransport(mqtt, name, ieee, msg=zbColorMessage)) | |
89 | |
90 | |
21 | 91 def makeZbBrightness(mqtt: MqttIo, name: str, ieee: str) -> Light: |
92 return Light(name=name, convertColor=brightnessConv, transport=ZigbeeTransport(mqtt, name, ieee, msg=zbBrightnessMessage)) | |
93 | |
94 | |
95 def makeZbRelay(mqtt: MqttIo, name: str, ieee: str) -> Light: | |
96 return Light(name=name, convertColor=relayConv, transport=ZigbeeTransport(mqtt, name, ieee, msg=zbRelayMessage)) | |
97 | |
98 | |
99 def makeEspBrightness(mqtt: MqttIo, name: str, topicPrefix: str) -> Light: | |
100 return Light(name=name, | |
101 convertColor=brightnessConv, | |
102 transport=ZigbeeTransport(mqtt, name, '', topic=lambda *arg: topicPrefix + '/command', msg=zbBrightnessMessage)) | |
103 | |
104 | |
23 | 105 def makeEspRgbw(mqtt: MqttIo, name: str, topicPrefix: str) -> Light: |
106 return Light(name=name, | |
107 convertColor=oneWhiteConv, | |
108 transport=ZigbeeTransport(mqtt, name, '', topic=lambda *arg: topicPrefix + '/command', msg=espColorMessage)) | |
109 | |
110 | |
26
33b3eb24506e
wled (single bulb) support. more lights
drewp@bigasterisk.com
parents:
25
diff
changeset
|
111 def makeWledSingleBulb(name: str, hostname: str) -> Light: |
28 | 112 return Light(name=name, convertColor=twoWhitesConv, transport=WledTransport(hostname, WledControl.wholeStringColor)) |
26
33b3eb24506e
wled (single bulb) support. more lights
drewp@bigasterisk.com
parents:
25
diff
changeset
|
113 |
28 | 114 def makeWledStringBrightness(name: str, hostname: str) -> Light: |
115 return Light(name=name, convertColor=brightnessConv, transport=WledTransport(hostname, WledControl.brightness)) | |
26
33b3eb24506e
wled (single bulb) support. more lights
drewp@bigasterisk.com
parents:
25
diff
changeset
|
116 |
2 | 117 class Lights: |
118 _d: dict[str, Light] = {} | |
119 | |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
120 def __init__(self, mqtt: MqttIo): |
22 | 121 # todo: combine mqtt, aiohttp session, and pigpiod client into some |
122 # Transports object | |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
123 self.mqtt = mqtt |
17 | 124 |
14
e3dbd04dab96
add mqtt; talk to first light (no throttling)
drewp@bigasterisk.com
parents:
13
diff
changeset
|
125 self.add(makeZbBar(mqtt, 'do-bar', '0xa4c13844948d2da4')) |
20 | 126 self.add(makeTasmota('do-lamp', 'tasmota-9E2AB7-2743')) |
29 | 127 self.add(makeTasmota('li-high-shelf', 'li-high-shelf')) # bulb labeled 'L3'. athom. broken wifi. |
21 | 128 self.add(makeTasmota('tr-door', 'light-tr-door')) |
28 | 129 |
26
33b3eb24506e
wled (single bulb) support. more lights
drewp@bigasterisk.com
parents:
25
diff
changeset
|
130 self.add(makeWledSingleBulb('ft-hanging', 'light-ft-ceil')) |
28 | 131 self.add(makeWledSingleBulb('tr-ball', 'light-tr-ball')) |
132 self.add(makeWledStringBrightness('hr-string', 'light-hr-string')) | |
133 self.add(makeWledStringBrightness('tr-string', 'light-tr-string')) | |
26
33b3eb24506e
wled (single bulb) support. more lights
drewp@bigasterisk.com
parents:
25
diff
changeset
|
134 |
29 | 135 self.add(makeShellyRGBWW('ki-ceiling', 'shellyrgbw2-e868e7f34c35')) |
136 self.add(makeShellyRGBWW('ki-counter', 'shellyrgbw2-e868e7f34cb2')) | |
20 | 137 |
21 | 138 self.add(makeZbIkeaWhiteTemp(mqtt, 'br-floor', '0x000b57fffedabd20')) |
139 self.add(makeZbIkeaWhiteTemp(mqtt, 'br-wall', '0x14b457fffe2dab6e')) | |
140 self.add(makeZbIkeaWhiteTemp(mqtt, 'en', '0x000b57fffe988959')) | |
141 self.add(makeZbIkeaWhiteTemp(mqtt, 'py', '0x000b57fffeaf42cd')) | |
142 self.add(makeZbIkeaWhiteTemp(mqtt, 'rr-lamp', '0x000b57fffe32e5a5')) | |
26
33b3eb24506e
wled (single bulb) support. more lights
drewp@bigasterisk.com
parents:
25
diff
changeset
|
143 self.add(makeZbIkeaWhiteTemp(mqtt, 'ft-ceiling', '0xd0cf5efffe28abcf')) |
29 | 144 self.add(makeZbIkeaWhiteTemp(mqtt, 'di-ceiling', '0x000b57fffe8c0ad4')) |
145 self.add(makeZbIkeaRGBWW(mqtt, 'li-high-shelf4', '0xd0cf5efffe158e49')) | |
20 | 146 |
21 | 147 self.add(makeZbBrightness(mqtt, 'go-high', '0x847127fffebb3efa')) |
148 | |
149 self.add(makeZbRelay(mqtt, 'ws-hanging', '0xd0cf5efffe720b46')) | |
20 | 150 |
151 self.add(makeSonoffRelay(mqtt, 'li-lamp0', 'sonoff_0')) | |
152 self.add(makeSonoffRelay(mqtt, 'li-lamp1', 'sonoff_1')) | |
153 self.add(makeSonoffRelay(mqtt, 'li-lamp2', 'sonoff_2')) | |
154 self.add(makeSonoffRelay(mqtt, 'li-lamp3', 'sonoff_3')) | |
155 self.add(makeSonoffRelay(mqtt, 'li-lamp4', 'sonoff_4')) | |
6 | 156 |
21 | 157 self.add(makeEspBrightness(mqtt, 'ws-high0', 'workshop/light/high0')) |
158 self.add(makeEspBrightness(mqtt, 'ws-high1', 'workshop/light/high1')) | |
159 self.add(makeEspBrightness(mqtt, 'ws-high2', 'workshop/light/high2')) | |
160 self.add(makeEspBrightness(mqtt, 'ws-high3', 'workshop/light/high3')) | |
161 self.add(makeEspBrightness(mqtt, 'ws-kid', 'workshop/light/kid')) | |
162 self.add(makeEspBrightness(mqtt, 'ws-sewing', 'workshop/light/sewing')) | |
163 | |
23 | 164 self.add(makeEspRgbw(mqtt, 'br-headboard', 'bed/light/headboard')) |
165 | |
21 | 166 # ft-ceil |
167 # li-toys | |
168 # sh-top | |
169 # light-sh | |
170 # ga-hanging | |
171 | |
172 # wled: | |
173 # string-tr | |
174 # string-hr | |
175 # light-tr-ball | |
176 # wled-ft-hanging | |
177 | |
2 | 178 def add(self, d: Light): |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
179 d.notifyChanged = self.notifyChanged |
2 | 180 self._d[d.name] = d |
181 | |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
182 self.notifyChanged() |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
183 |
25 | 184 def allNames(self) -> list[str]: |
185 return list(self._d.keys()) | |
186 | |
2 | 187 def byName(self, name: str) -> Light: |
188 return self._d[name] | |
189 | |
12 | 190 def to_dict(self): |
191 return {'lights': [d.to_dict() for d in sorted(self._d.values(), key=lambda r: r.name)]} | |
192 | |
193 # the following is bad. Get a better events lib. | |
194 _changeSleepTask: asyncio.Task | None = None | |
195 | |
2 | 196 async def changes(self): # yields None on any data change |
197 while True: | |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
198 log.info('Lights has a change') |
2 | 199 yield None |
11
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
200 self._changeSleepTask = asyncio.create_task(self._sleep()) |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
201 try: |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
202 await self._changeSleepTask |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
203 except asyncio.CancelledError: |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
204 pass |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
205 self._changeSleepTask = None |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
206 |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
207 async def _sleep(self): |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
208 await asyncio.sleep(100) |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
209 |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
210 def notifyChanged(self): |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
211 log.info('Lights.notifyChanged()') |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
212 if self._changeSleepTask is not None: |
028ed39aa78f
more ts types; attempted multiplayer but the change events are managed wrong
drewp@bigasterisk.com
parents:
9
diff
changeset
|
213 self._changeSleepTask.cancel() |