changeset 20:24a574108365

more protocols; bugs in setColor
author drewp@bigasterisk.com
date Mon, 29 Jan 2024 11:52:43 -0800
parents 4e350dcdc4fe
children b8201490c731
files color_convert.py light.py protocols.py
diffstat 3 files changed, 118 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/color_convert.py	Mon Jan 29 11:50:28 2024 -0800
+++ b/color_convert.py	Mon Jan 29 11:52:43 2024 -0800
@@ -15,6 +15,8 @@
     w: float = 0
     x: float = 0
     y: float = 0
+    cw: float = 0
+    ww: float = 0
     brightness: float = 0
 
     def summary(self) -> dict:
@@ -23,3 +25,12 @@
 # fix this to send what z2m likes
 def zbConv(c: Color) -> DeviceColor:
     return DeviceColor(r=c.r, g=c.g, b=c.b, brightness=max(c.r, c.g, c.b))
+
+def oneWhiteConv(c: Color) -> DeviceColor:
+    return DeviceColor(r=c.r, g=c.g, b=c.b, w=max(c.r, c.g, c.b))
+
+def twoWhitesConv(c: Color) -> DeviceColor:
+    return DeviceColor(r=c.r, g=c.g, b=c.b, cw=max(c.r, c.g, c.b))
+
+def relayConv(c: Color) -> DeviceColor:
+    return DeviceColor(brightness=1 if (max(c.r, c.g, c.b) > 0) else 0)
\ No newline at end of file
--- a/light.py	Mon Jan 29 11:50:28 2024 -0800
+++ b/light.py	Mon Jan 29 11:52:43 2024 -0800
@@ -4,11 +4,11 @@
 from typing import Callable
 
 from color import Color
-from color_convert import DeviceColor, zbConv
+from color_convert import DeviceColor, oneWhiteConv, relayConv, twoWhitesConv, zbConv
 from mqtt_io import MqttIo
-from protocols import Transport, ZigbeeTransport
+from protocols import ShellyGen1WebTransport, SonoffRelayTransport, TasmotaWebTransport, Transport, ZigbeeTransport
 
-log = logging.getLogger('light')
+log = logging.getLogger('lite')
 
 
 @dataclass
@@ -43,18 +43,21 @@
         return {'light': d}
 
     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.convertColor(self.requestingColor)
+        dc = self.convertColor(self.requestingColor)
+
+        log.info(f'setColor from {self.requestingColor} to {c} = {dc.summary()=}')
+        if dc != self.requestingDeviceColor:
+            self.requestingDeviceColor = dc
 
-        if self.notifyChanged:
-            self.notifyChanged()
+            if self.notifyChanged:
+                self.notifyChanged()
 
-        # waits for the relevant round-trip
-        log.info(f'transport send {self.requestingDeviceColor.summary()}')
-        await self.transport.send(self.requestingDeviceColor)
+            # waits for the relevant round-trip
+            log.info(f'transport send {self.requestingDeviceColor.summary()}')
+            await self.transport.send(self.requestingDeviceColor)
 
         self.emittingColor = self.requestingColor
         if self.notifyChanged:
@@ -65,6 +68,18 @@
     return Light(name=name, convertColor=zbConv, transport=ZigbeeTransport(mqtt, name, ieee))
 
 
+def makeTasmota(name: str, hostname: str) -> Light:
+    return Light(name=name, convertColor=twoWhitesConv, transport=TasmotaWebTransport(hostname))
+
+
+def makeShellyRGW2(name: str, hostname: str) -> Light:
+    return Light(name=name, convertColor=oneWhiteConv, transport=ShellyGen1WebTransport(hostname))
+
+
+def makeSonoffRelay(mqtt: MqttIo, name: str, topic: str) -> Light:
+    return Light(name=name, convertColor=relayConv, transport=SonoffRelayTransport(mqtt, topic))
+
+
 class Lights:
     _d: dict[str, Light] = {}
 
@@ -72,6 +87,34 @@
         self.mqtt = mqtt
 
         self.add(makeZbBar(mqtt, 'do-bar', '0xa4c13844948d2da4'))
+        self.add(makeTasmota('do-lamp', 'tasmota-9E2AB7-2743'))
+        self.add(makeShellyRGW2('ki-ceiling', 'shellyrgbw2-e868e7f34c35'))
+        self.add(makeShellyRGW2('ki-counter', 'shellyrgbw2-e868e7f34cb2'))
+        # br-floor	online=online | metric=1 (graph)	lqi=93	bright=12
+        # br-wall	online=online | metric=1 (graph)	lqi=30	bright=0
+
+        # en	online=online | metric=1 (graph)	lqi=36	bright=216
+        # ft-ceil	online=online | metric=1 (graph)	lqi=96	bright=252
+        # go-high	online=online | metric=1 (graph)	lqi=93	bright=253
+        # li-toys	online=online | metric=1 (graph)	lqi=96	bright=28
+        # py	online=online | metric=1 (graph)	lqi=93	bright=216
+        # rr-lamp	online=offline | metric=0 (graph)	lqi=...	bright=...
+        # sh-top	online=online | metric=1 (graph)	lqi=93	bright=254
+        # ws-hanging	online=online | metric=1 (graph)	lqi=39	bright=...
+        # light-li-high-shelf	uptimesec=2232334	wifi=-65	dim=100 color=0000002FD0
+        # light-sh	uptimesec=...	wifi=...	dim=... color=...
+        # light-tr-wall	uptimesec=5082251	wifi=-38	dim=34 color=5700000000
+
+        # wled-ft-hanging	offline		#FFA000 bright=128
+        # string-tr	online		#8CFF82 bright=128
+        # light-tr-ball	online		#808A03 bright=12
+        # string-hr	online		#C9FFDC bright=131
+
+        self.add(makeSonoffRelay(mqtt, 'li-lamp0', 'sonoff_0'))
+        self.add(makeSonoffRelay(mqtt, 'li-lamp1', 'sonoff_1'))
+        self.add(makeSonoffRelay(mqtt, 'li-lamp2', 'sonoff_2'))
+        self.add(makeSonoffRelay(mqtt, 'li-lamp3', 'sonoff_3'))
+        self.add(makeSonoffRelay(mqtt, 'li-lamp4', 'sonoff_4'))
 
     def add(self, d: Light):
         d.notifyChanged = self.notifyChanged
--- a/protocols.py	Mon Jan 29 11:50:28 2024 -0800
+++ b/protocols.py	Mon Jan 29 11:52:43 2024 -0800
@@ -1,7 +1,10 @@
+import logging
 import json
+import aiohttp
 from color_convert import DeviceColor
 from mqtt_io import MqttIo
 
+log = logging.getLogger('prot')
 
 class Transport:
 
@@ -27,7 +30,57 @@
         self.ieee = ieee
 
     def linked(self):
-        return {'url': f'https://bigasterisk.com/zigbee/console/#/device/{self.ieee}/info', 'label': 'do-bar'}
+        return {'url': f'https://bigasterisk.com/zigbee/console/#/device/{self.ieee}/info', 'label': self.name}
 
     async def send(self, dc: DeviceColor):
         await self.mqtt.publish(f'zigbee/{self.name}/set', json.dumps(zigbeeHexMessage(dc)))
+
+
+class SonoffRelayTransport(Transport):
+
+    def __init__(self, mqtt: MqttIo, name: str):
+        self.mqtt = mqtt
+        self.name = name
+
+    def linked(self):
+        return {'label': self.name}
+
+    async def send(self, dc: DeviceColor):
+        topic = f'{self.name}/switch/sonoff_basic_relay/command'
+        msg = 'ON' if dc.brightness else 'OFF'
+        log.info(f'sonoff {topic=} {msg=}')
+        await self.mqtt.publish(topic, msg)
+
+
+class _WebTransport(Transport):
+
+    def __init__(self, hostname: str):
+        self.hostname = hostname
+        self._session = aiohttp.ClientSession()
+
+    def linked(self):
+        return {'url': f'http://{self.hostname}/', 'label': self.hostname}
+
+
+class TasmotaWebTransport(_WebTransport):
+
+    async def send(self, dc: DeviceColor):
+        cmnd = 'Color ' + ','.join(str(int(x * 255)) for x in (dc.r, dc.g, dc.b, dc.cw, dc.ww))
+        async with self._session.get(f'http://{self.hostname}/cm', params={'cmnd': cmnd}) as resp:
+            await resp.text()
+            # {"POWER":"ON","Dimmer":21,"Color":"3636363600","HSBColor":"0,0,21","White":21,"CT":153,"Channel":[21,21,21,21,0]}
+
+
+class ShellyGen1WebTransport(_WebTransport):
+
+    async def send(self, dc: DeviceColor):
+        # also see https://shelly-api-docs.shelly.cloud/gen1/#shelly-rgbw2-color-status for metrics
+        async with self._session.get(f'http://{self.hostname}/light/0',
+                                     params={
+                                         'red': int(dc.r * 255),
+                                         'green': int(dc.g * 255),
+                                         'blue': int(dc.b * 255),
+                                         'white': int(dc.w * 255),
+                                     }) as resp:
+            await resp.text()
+            # {..."mode":"color","red":255,"green":242,"blue":0,"white":255,"gain":59,"effect":0,"transition":0,"power":18.00,"overpower":false}