wyzeapy.services.bulb_service

  1#  Copyright (c) 2021. Mulliken, LLC - All Rights Reserved
  2#  You may use, distribute and modify this code under the terms
  3#  of the attached license. You should have received a copy of
  4#  the license with this file. If not, please write to:
  5#  katie@mulliken.net to receive a copy
  6import logging
  7import re
  8from typing import Any, Dict, Optional, List
  9
 10from .base_service import BaseService
 11from ..types import Device, PropertyIDs, DeviceTypes
 12from ..utils import create_pid_pair
 13
 14_LOGGER = logging.getLogger(__name__)
 15
 16
 17class Bulb(Device):
 18    _brightness: int = 0
 19    _color_temp: int = 1800
 20    _color: Optional[str]
 21    enr: str
 22
 23    on: bool = False
 24    cloud_fallback = False
 25
 26    def __init__(self, dictionary: Dict[Any, Any]):
 27        super().__init__(dictionary)
 28
 29        self.ip = self.device_params["ip"]
 30
 31        if (
 32            self.type is DeviceTypes.MESH_LIGHT
 33            or self.type is DeviceTypes.LIGHTSTRIP
 34        ):
 35            self._color = "000000"
 36
 37    @property
 38    def brightness(self) -> int:
 39        return self._brightness
 40
 41    @brightness.setter
 42    def brightness(self, value: int) -> None:
 43        assert value <= 100
 44        assert value >= 0
 45        self._brightness = value
 46
 47    @property
 48    def color_temp(self) -> int:
 49        return self._color_temp
 50
 51    @color_temp.setter
 52    def color_temp(self, value: int) -> None:
 53        self._color_temp = value
 54
 55    @property
 56    def color(self) -> Optional[str]:
 57        return self._color
 58
 59    @color.setter
 60    def color(self, value) -> None:
 61        assert re.match(r"^([A-Fa-f\d]{6}|[A-Fa-f\d]{3})$", value) is not None
 62        self._color = value
 63
 64
 65class BulbService(BaseService):
 66    async def update(self, bulb: Bulb) -> Bulb:
 67        # Get updated device_params
 68        async with BaseService._update_lock:
 69            bulb.device_params = await self.get_updated_params(bulb.mac)
 70
 71        device_info = await self._get_property_list(bulb)
 72        for property_id, value in device_info:
 73            if property_id == PropertyIDs.BRIGHTNESS:
 74                bulb.brightness = int(float(value))
 75            elif property_id == PropertyIDs.COLOR_TEMP:
 76                try:
 77                    bulb.color_temp = int(value)
 78                except ValueError:
 79                    bulb.color_temp = 2700
 80            elif property_id == PropertyIDs.ON:
 81                bulb.on = value == "1"
 82            elif property_id == PropertyIDs.AVAILABLE:
 83                bulb.available = value == "1"
 84            elif (
 85                property_id == PropertyIDs.COLOR
 86                and bulb.type in [DeviceTypes.LIGHTSTRIP, DeviceTypes.MESH_LIGHT]
 87            ):
 88                bulb.color = value
 89            elif property_id == PropertyIDs.COLOR_MODE:
 90                bulb.color_mode = value
 91            elif property_id == PropertyIDs.SUN_MATCH:
 92                bulb.sun_match = value == "1"
 93            elif property_id == PropertyIDs.LIGHTSTRIP_EFFECTS:
 94                bulb.effects = value
 95            elif property_id == PropertyIDs.LIGHTSTRIP_MUSIC_MODE:
 96                bulb.music_mode = value == "1"
 97
 98        return bulb
 99
100    async def get_bulbs(self) -> List[Bulb]:
101        if self._devices is None:
102            self._devices = await self.get_object_list()
103
104        bulbs = [
105            device
106            for device in self._devices
107            if device.type in [DeviceTypes.LIGHT,
108                               DeviceTypes.MESH_LIGHT,
109                               DeviceTypes.LIGHTSTRIP]
110        ]
111
112        return [Bulb(bulb.raw_dict) for bulb in bulbs]
113
114    async def turn_on(self, bulb: Bulb, local_control, options=None):
115        plist = [
116            create_pid_pair(PropertyIDs.ON, "1")
117        ]
118
119        if options is not None:
120            plist.extend(options)
121
122        if bulb.type is DeviceTypes.LIGHT:
123            await self._set_property_list(bulb, plist)
124
125        elif (
126            bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
127        ):
128            # Local Control
129            if local_control and not bulb.cloud_fallback:
130                await self._local_bulb_command(bulb, plist)
131
132            # Cloud Control
133            elif bulb.type is DeviceTypes.MESH_LIGHT:  # Sun match for mesh bulbs needs to be set on a different endpoint for some reason
134                for item in plist:
135                    if item["pid"] == PropertyIDs.SUN_MATCH.value:
136                        await self._set_property_list(bulb, [item])
137                        plist.remove(item)
138                await self._run_action_list(bulb, plist)
139            else:
140                await self._run_action_list(bulb, plist)  # Lightstrips
141
142    async def turn_off(self, bulb: Bulb, local_control):
143        plist = [
144            create_pid_pair(PropertyIDs.ON, "0")
145        ]
146
147        if bulb.type in [
148            DeviceTypes.LIGHT
149        ]:
150            await self._set_property_list(bulb, plist)
151        elif (
152            bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
153        ):
154            if local_control and not bulb.cloud_fallback:
155                await self._local_bulb_command(bulb, plist)
156            else:
157                await self._run_action_list(bulb, plist)
158
159    async def set_color_temp(self, bulb: Bulb, color_temp: int):
160        plist = [
161            create_pid_pair(PropertyIDs.COLOR_TEMP, str(color_temp))
162        ]
163
164        if bulb.type in [
165            DeviceTypes.LIGHT
166        ]:
167            await self._set_property_list(bulb, plist)
168        elif bulb.type in [
169            DeviceTypes.MESH_LIGHT
170        ]:
171            await self._local_bulb_command(bulb, plist)
172
173    async def set_color(self, bulb: Bulb, color: str, local_control):
174        plist = [
175            create_pid_pair(PropertyIDs.COLOR, str(color))
176        ]
177        if bulb.type in [
178            DeviceTypes.MESH_LIGHT
179        ]:
180            if local_control and not bulb.cloud_fallback:
181                await self._local_bulb_command(bulb, plist)
182            else:
183                await self._run_action_list(bulb, plist)
184
185    async def set_brightness(self, bulb: Device, brightness: int):
186        plist = [
187            create_pid_pair(PropertyIDs.BRIGHTNESS, str(brightness))
188        ]
189
190        if bulb.type in [
191            DeviceTypes.LIGHT
192        ]:
193            await self._set_property_list(bulb, plist)
194        if bulb.type in [
195            DeviceTypes.MESH_LIGHT
196        ]:
197            await self._local_bulb_command(bulb, plist)
198
199    async def music_mode_on(self, bulb: Device):
200        plist = [
201            create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "1")
202        ]
203
204        await self._run_action_list(bulb, plist)
205
206    async def music_mode_off(self, bulb: Device):
207        plist = [
208            create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "0")
209        ]
210
211        await self._run_action_list(bulb, plist)
class Bulb(wyzeapy.types.Device):
18class Bulb(Device):
19    _brightness: int = 0
20    _color_temp: int = 1800
21    _color: Optional[str]
22    enr: str
23
24    on: bool = False
25    cloud_fallback = False
26
27    def __init__(self, dictionary: Dict[Any, Any]):
28        super().__init__(dictionary)
29
30        self.ip = self.device_params["ip"]
31
32        if (
33            self.type is DeviceTypes.MESH_LIGHT
34            or self.type is DeviceTypes.LIGHTSTRIP
35        ):
36            self._color = "000000"
37
38    @property
39    def brightness(self) -> int:
40        return self._brightness
41
42    @brightness.setter
43    def brightness(self, value: int) -> None:
44        assert value <= 100
45        assert value >= 0
46        self._brightness = value
47
48    @property
49    def color_temp(self) -> int:
50        return self._color_temp
51
52    @color_temp.setter
53    def color_temp(self, value: int) -> None:
54        self._color_temp = value
55
56    @property
57    def color(self) -> Optional[str]:
58        return self._color
59
60    @color.setter
61    def color(self, value) -> None:
62        assert re.match(r"^([A-Fa-f\d]{6}|[A-Fa-f\d]{3})$", value) is not None
63        self._color = value
Bulb(dictionary: Dict[Any, Any])
27    def __init__(self, dictionary: Dict[Any, Any]):
28        super().__init__(dictionary)
29
30        self.ip = self.device_params["ip"]
31
32        if (
33            self.type is DeviceTypes.MESH_LIGHT
34            or self.type is DeviceTypes.LIGHTSTRIP
35        ):
36            self._color = "000000"
enr: str
on: bool = False
cloud_fallback = False
ip
brightness: int
38    @property
39    def brightness(self) -> int:
40        return self._brightness
color_temp: int
48    @property
49    def color_temp(self) -> int:
50        return self._color_temp
color: Optional[str]
56    @property
57    def color(self) -> Optional[str]:
58        return self._color
class BulbService(wyzeapy.services.base_service.BaseService):
 66class BulbService(BaseService):
 67    async def update(self, bulb: Bulb) -> Bulb:
 68        # Get updated device_params
 69        async with BaseService._update_lock:
 70            bulb.device_params = await self.get_updated_params(bulb.mac)
 71
 72        device_info = await self._get_property_list(bulb)
 73        for property_id, value in device_info:
 74            if property_id == PropertyIDs.BRIGHTNESS:
 75                bulb.brightness = int(float(value))
 76            elif property_id == PropertyIDs.COLOR_TEMP:
 77                try:
 78                    bulb.color_temp = int(value)
 79                except ValueError:
 80                    bulb.color_temp = 2700
 81            elif property_id == PropertyIDs.ON:
 82                bulb.on = value == "1"
 83            elif property_id == PropertyIDs.AVAILABLE:
 84                bulb.available = value == "1"
 85            elif (
 86                property_id == PropertyIDs.COLOR
 87                and bulb.type in [DeviceTypes.LIGHTSTRIP, DeviceTypes.MESH_LIGHT]
 88            ):
 89                bulb.color = value
 90            elif property_id == PropertyIDs.COLOR_MODE:
 91                bulb.color_mode = value
 92            elif property_id == PropertyIDs.SUN_MATCH:
 93                bulb.sun_match = value == "1"
 94            elif property_id == PropertyIDs.LIGHTSTRIP_EFFECTS:
 95                bulb.effects = value
 96            elif property_id == PropertyIDs.LIGHTSTRIP_MUSIC_MODE:
 97                bulb.music_mode = value == "1"
 98
 99        return bulb
100
101    async def get_bulbs(self) -> List[Bulb]:
102        if self._devices is None:
103            self._devices = await self.get_object_list()
104
105        bulbs = [
106            device
107            for device in self._devices
108            if device.type in [DeviceTypes.LIGHT,
109                               DeviceTypes.MESH_LIGHT,
110                               DeviceTypes.LIGHTSTRIP]
111        ]
112
113        return [Bulb(bulb.raw_dict) for bulb in bulbs]
114
115    async def turn_on(self, bulb: Bulb, local_control, options=None):
116        plist = [
117            create_pid_pair(PropertyIDs.ON, "1")
118        ]
119
120        if options is not None:
121            plist.extend(options)
122
123        if bulb.type is DeviceTypes.LIGHT:
124            await self._set_property_list(bulb, plist)
125
126        elif (
127            bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
128        ):
129            # Local Control
130            if local_control and not bulb.cloud_fallback:
131                await self._local_bulb_command(bulb, plist)
132
133            # Cloud Control
134            elif bulb.type is DeviceTypes.MESH_LIGHT:  # Sun match for mesh bulbs needs to be set on a different endpoint for some reason
135                for item in plist:
136                    if item["pid"] == PropertyIDs.SUN_MATCH.value:
137                        await self._set_property_list(bulb, [item])
138                        plist.remove(item)
139                await self._run_action_list(bulb, plist)
140            else:
141                await self._run_action_list(bulb, plist)  # Lightstrips
142
143    async def turn_off(self, bulb: Bulb, local_control):
144        plist = [
145            create_pid_pair(PropertyIDs.ON, "0")
146        ]
147
148        if bulb.type in [
149            DeviceTypes.LIGHT
150        ]:
151            await self._set_property_list(bulb, plist)
152        elif (
153            bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
154        ):
155            if local_control and not bulb.cloud_fallback:
156                await self._local_bulb_command(bulb, plist)
157            else:
158                await self._run_action_list(bulb, plist)
159
160    async def set_color_temp(self, bulb: Bulb, color_temp: int):
161        plist = [
162            create_pid_pair(PropertyIDs.COLOR_TEMP, str(color_temp))
163        ]
164
165        if bulb.type in [
166            DeviceTypes.LIGHT
167        ]:
168            await self._set_property_list(bulb, plist)
169        elif bulb.type in [
170            DeviceTypes.MESH_LIGHT
171        ]:
172            await self._local_bulb_command(bulb, plist)
173
174    async def set_color(self, bulb: Bulb, color: str, local_control):
175        plist = [
176            create_pid_pair(PropertyIDs.COLOR, str(color))
177        ]
178        if bulb.type in [
179            DeviceTypes.MESH_LIGHT
180        ]:
181            if local_control and not bulb.cloud_fallback:
182                await self._local_bulb_command(bulb, plist)
183            else:
184                await self._run_action_list(bulb, plist)
185
186    async def set_brightness(self, bulb: Device, brightness: int):
187        plist = [
188            create_pid_pair(PropertyIDs.BRIGHTNESS, str(brightness))
189        ]
190
191        if bulb.type in [
192            DeviceTypes.LIGHT
193        ]:
194            await self._set_property_list(bulb, plist)
195        if bulb.type in [
196            DeviceTypes.MESH_LIGHT
197        ]:
198            await self._local_bulb_command(bulb, plist)
199
200    async def music_mode_on(self, bulb: Device):
201        plist = [
202            create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "1")
203        ]
204
205        await self._run_action_list(bulb, plist)
206
207    async def music_mode_off(self, bulb: Device):
208        plist = [
209            create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "0")
210        ]
211
212        await self._run_action_list(bulb, plist)

Base service class for interacting with Wyze devices.

async def update( self, bulb: Bulb) -> Bulb:
67    async def update(self, bulb: Bulb) -> Bulb:
68        # Get updated device_params
69        async with BaseService._update_lock:
70            bulb.device_params = await self.get_updated_params(bulb.mac)
71
72        device_info = await self._get_property_list(bulb)
73        for property_id, value in device_info:
74            if property_id == PropertyIDs.BRIGHTNESS:
75                bulb.brightness = int(float(value))
76            elif property_id == PropertyIDs.COLOR_TEMP:
77                try:
78                    bulb.color_temp = int(value)
79                except ValueError:
80                    bulb.color_temp = 2700
81            elif property_id == PropertyIDs.ON:
82                bulb.on = value == "1"
83            elif property_id == PropertyIDs.AVAILABLE:
84                bulb.available = value == "1"
85            elif (
86                property_id == PropertyIDs.COLOR
87                and bulb.type in [DeviceTypes.LIGHTSTRIP, DeviceTypes.MESH_LIGHT]
88            ):
89                bulb.color = value
90            elif property_id == PropertyIDs.COLOR_MODE:
91                bulb.color_mode = value
92            elif property_id == PropertyIDs.SUN_MATCH:
93                bulb.sun_match = value == "1"
94            elif property_id == PropertyIDs.LIGHTSTRIP_EFFECTS:
95                bulb.effects = value
96            elif property_id == PropertyIDs.LIGHTSTRIP_MUSIC_MODE:
97                bulb.music_mode = value == "1"
98
99        return bulb
async def get_bulbs(self) -> List[Bulb]:
101    async def get_bulbs(self) -> List[Bulb]:
102        if self._devices is None:
103            self._devices = await self.get_object_list()
104
105        bulbs = [
106            device
107            for device in self._devices
108            if device.type in [DeviceTypes.LIGHT,
109                               DeviceTypes.MESH_LIGHT,
110                               DeviceTypes.LIGHTSTRIP]
111        ]
112
113        return [Bulb(bulb.raw_dict) for bulb in bulbs]
async def turn_on( self, bulb: Bulb, local_control, options=None):
115    async def turn_on(self, bulb: Bulb, local_control, options=None):
116        plist = [
117            create_pid_pair(PropertyIDs.ON, "1")
118        ]
119
120        if options is not None:
121            plist.extend(options)
122
123        if bulb.type is DeviceTypes.LIGHT:
124            await self._set_property_list(bulb, plist)
125
126        elif (
127            bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
128        ):
129            # Local Control
130            if local_control and not bulb.cloud_fallback:
131                await self._local_bulb_command(bulb, plist)
132
133            # Cloud Control
134            elif bulb.type is DeviceTypes.MESH_LIGHT:  # Sun match for mesh bulbs needs to be set on a different endpoint for some reason
135                for item in plist:
136                    if item["pid"] == PropertyIDs.SUN_MATCH.value:
137                        await self._set_property_list(bulb, [item])
138                        plist.remove(item)
139                await self._run_action_list(bulb, plist)
140            else:
141                await self._run_action_list(bulb, plist)  # Lightstrips
async def turn_off(self, bulb: Bulb, local_control):
143    async def turn_off(self, bulb: Bulb, local_control):
144        plist = [
145            create_pid_pair(PropertyIDs.ON, "0")
146        ]
147
148        if bulb.type in [
149            DeviceTypes.LIGHT
150        ]:
151            await self._set_property_list(bulb, plist)
152        elif (
153            bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
154        ):
155            if local_control and not bulb.cloud_fallback:
156                await self._local_bulb_command(bulb, plist)
157            else:
158                await self._run_action_list(bulb, plist)
async def set_color_temp(self, bulb: Bulb, color_temp: int):
160    async def set_color_temp(self, bulb: Bulb, color_temp: int):
161        plist = [
162            create_pid_pair(PropertyIDs.COLOR_TEMP, str(color_temp))
163        ]
164
165        if bulb.type in [
166            DeviceTypes.LIGHT
167        ]:
168            await self._set_property_list(bulb, plist)
169        elif bulb.type in [
170            DeviceTypes.MESH_LIGHT
171        ]:
172            await self._local_bulb_command(bulb, plist)
async def set_color( self, bulb: Bulb, color: str, local_control):
174    async def set_color(self, bulb: Bulb, color: str, local_control):
175        plist = [
176            create_pid_pair(PropertyIDs.COLOR, str(color))
177        ]
178        if bulb.type in [
179            DeviceTypes.MESH_LIGHT
180        ]:
181            if local_control and not bulb.cloud_fallback:
182                await self._local_bulb_command(bulb, plist)
183            else:
184                await self._run_action_list(bulb, plist)
async def set_brightness(self, bulb: wyzeapy.types.Device, brightness: int):
186    async def set_brightness(self, bulb: Device, brightness: int):
187        plist = [
188            create_pid_pair(PropertyIDs.BRIGHTNESS, str(brightness))
189        ]
190
191        if bulb.type in [
192            DeviceTypes.LIGHT
193        ]:
194            await self._set_property_list(bulb, plist)
195        if bulb.type in [
196            DeviceTypes.MESH_LIGHT
197        ]:
198            await self._local_bulb_command(bulb, plist)
async def music_mode_on(self, bulb: wyzeapy.types.Device):
200    async def music_mode_on(self, bulb: Device):
201        plist = [
202            create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "1")
203        ]
204
205        await self._run_action_list(bulb, plist)
async def music_mode_off(self, bulb: wyzeapy.types.Device):
207    async def music_mode_off(self, bulb: Device):
208        plist = [
209            create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "0")
210        ]
211
212        await self._run_action_list(bulb, plist)