phue2 0.0.1.dev279__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phue2/__init__.py +37 -0
- phue2/__main__.py +408 -0
- phue2/_internal/__init__.py +1 -0
- phue2/_internal/console.py +175 -0
- phue2/bridge.py +1246 -0
- phue2/exceptions.py +22 -0
- phue2/group.py +118 -0
- phue2/light.py +301 -0
- phue2/scene.py +58 -0
- phue2/sensor.py +307 -0
- phue2-0.0.1.dev279.dist-info/METADATA +188 -0
- phue2-0.0.1.dev279.dist-info/RECORD +15 -0
- phue2-0.0.1.dev279.dist-info/WHEEL +4 -0
- phue2-0.0.1.dev279.dist-info/entry_points.txt +2 -0
- phue2-0.0.1.dev279.dist-info/licenses/LICENSE +21 -0
phue2/exceptions.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Exceptions for the phue_modern library."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PhueException(Exception):
|
|
5
|
+
"""Base exception for all Philips Hue related errors."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, id: int, message: str):
|
|
8
|
+
self.id = id
|
|
9
|
+
self.message = message
|
|
10
|
+
super().__init__(f"Error {id}: {message}")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PhueRegistrationException(PhueException):
|
|
14
|
+
"""Exception raised when registration with the bridge fails."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PhueRequestTimeout(PhueException):
|
|
20
|
+
"""Exception raised when a request to the bridge times out."""
|
|
21
|
+
|
|
22
|
+
pass
|
phue2/group.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Group classes for controlling groups of Philips Hue lights."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
from phue2.light import Light
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger("phue_modern")
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from phue2.bridge import Bridge
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Group(Light):
|
|
15
|
+
"""A group of Hue lights, tracked as a group on the bridge
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
|
|
19
|
+
>>> b = Bridge()
|
|
20
|
+
>>> g1 = Group(b, 1)
|
|
21
|
+
>>> g1.hue = 50000 # all lights in that group turn blue
|
|
22
|
+
>>> g1.on = False # all will turn off
|
|
23
|
+
|
|
24
|
+
>>> g2 = Group(b, 'Kitchen') # you can also look up groups by name
|
|
25
|
+
>>> # will raise a LookupError if the name doesn't match
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, bridge: "Bridge", group_id: int | str):
|
|
29
|
+
Light.__init__(self, bridge, 0) # Light ID will be overridden
|
|
30
|
+
self.light_id = None # not relevant for a group
|
|
31
|
+
self._any_on: bool | None = None
|
|
32
|
+
self._all_on: bool | None = None
|
|
33
|
+
self.group_id: int
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
self.group_id = int(group_id)
|
|
37
|
+
except ValueError:
|
|
38
|
+
name = str(group_id)
|
|
39
|
+
groups = bridge.get_group()
|
|
40
|
+
for idnumber, info in groups.items():
|
|
41
|
+
if info["name"] == name:
|
|
42
|
+
self.group_id = int(idnumber)
|
|
43
|
+
break
|
|
44
|
+
else:
|
|
45
|
+
raise LookupError("Could not find a group by that name.")
|
|
46
|
+
|
|
47
|
+
# Wrapper functions for get/set through the bridge, adding support for
|
|
48
|
+
# remembering the transitiontime parameter if the user has set it
|
|
49
|
+
def _get(self, *args: Any, **kwargs: Any) -> Any:
|
|
50
|
+
return self.bridge.get_group(self.group_id, *args, **kwargs)
|
|
51
|
+
|
|
52
|
+
def _set(self, *args: Any, **kwargs: Any) -> Any:
|
|
53
|
+
# let's get basic group functionality working first before adding
|
|
54
|
+
# transition time...
|
|
55
|
+
if self.transitiontime is not None:
|
|
56
|
+
kwargs["transitiontime"] = self.transitiontime
|
|
57
|
+
logger.debug(
|
|
58
|
+
f"Setting with transitiontime = {self.transitiontime} ds = {float(self.transitiontime) / 10} s"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if (args[0] == "on" and args[1] is False) or (
|
|
62
|
+
kwargs.get("on", True) is False
|
|
63
|
+
):
|
|
64
|
+
self._reset_bri_after_on = True
|
|
65
|
+
return self.bridge.set_group(self.group_id, *args, **kwargs)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def name(self) -> str:
|
|
69
|
+
"""Get or set the name of the light group [string]"""
|
|
70
|
+
return self._get("name")
|
|
71
|
+
|
|
72
|
+
@name.setter
|
|
73
|
+
def name(self, value: str) -> None:
|
|
74
|
+
old_name = self.name
|
|
75
|
+
self._name = value
|
|
76
|
+
logger.debug(f"Renaming light group from '{old_name}' to '{value}'")
|
|
77
|
+
self._set("name", self._name)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def any_on(self) -> bool:
|
|
81
|
+
"""If true at least one light in the group is on"""
|
|
82
|
+
self._any_on = self._get("any_on")
|
|
83
|
+
return self._any_on or False
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def all_on(self) -> bool:
|
|
87
|
+
"""If true all lights in the group are on"""
|
|
88
|
+
self._all_on = self._get("all_on")
|
|
89
|
+
return self._all_on or False
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def lights(self) -> list[Light]:
|
|
93
|
+
"""Return a list of all lights in this group"""
|
|
94
|
+
return [Light(self.bridge, int(light_id)) for light_id in self._get("lights")]
|
|
95
|
+
|
|
96
|
+
@lights.setter
|
|
97
|
+
def lights(self, value: list[int]) -> None:
|
|
98
|
+
"""Change the lights that are in this group"""
|
|
99
|
+
logger.debug(f"Setting lights in group {self.group_id} to {str(value)}")
|
|
100
|
+
self._set("lights", value)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class AllLights(Group):
|
|
104
|
+
"""All the Hue lights connected to your bridge
|
|
105
|
+
|
|
106
|
+
This makes use of the semi-documented feature that
|
|
107
|
+
"Group 0" of lights appears to be a group automatically
|
|
108
|
+
consisting of all lights. This is not returned by
|
|
109
|
+
listing the groups, but is accessible if you explicitly
|
|
110
|
+
ask for group 0.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, bridge: "Bridge | None" = None):
|
|
114
|
+
if bridge is None:
|
|
115
|
+
from .bridge import Bridge
|
|
116
|
+
|
|
117
|
+
bridge = Bridge()
|
|
118
|
+
Group.__init__(self, bridge, 0)
|
phue2/light.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""Light class for controlling Philips Hue lights."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from phue2.bridge import Bridge
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger("phue_modern")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Light:
|
|
13
|
+
"""Hue Light object
|
|
14
|
+
|
|
15
|
+
Light settings can be accessed or set via the properties of this object.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, bridge: "Bridge", light_id: int):
|
|
19
|
+
self.bridge = bridge
|
|
20
|
+
self.light_id = light_id
|
|
21
|
+
|
|
22
|
+
self._name: str | None = None
|
|
23
|
+
self._on: bool | None = None
|
|
24
|
+
self._brightness: int | None = None
|
|
25
|
+
self._colormode: str | None = None
|
|
26
|
+
self._hue: int | None = None
|
|
27
|
+
self._saturation: int | None = None
|
|
28
|
+
self._xy: tuple[float, float] | None = None
|
|
29
|
+
self._colortemp: int | None = None
|
|
30
|
+
self._effect: str | None = None
|
|
31
|
+
self._alert: str | None = None
|
|
32
|
+
self.transitiontime: int | None = None # default
|
|
33
|
+
self._reset_bri_after_on: bool | None = None
|
|
34
|
+
self._reachable: bool | None = None
|
|
35
|
+
self._type: str | None = None
|
|
36
|
+
|
|
37
|
+
def __repr__(self) -> str:
|
|
38
|
+
# like default python repr function, but add light name
|
|
39
|
+
return f'<{self.__class__.__module__}.{self.__class__.__name__} object "{self.name}" at {hex(id(self))}>'
|
|
40
|
+
|
|
41
|
+
# Wrapper functions for get/set through the bridge, adding support for
|
|
42
|
+
# remembering the transitiontime parameter if the user has set it
|
|
43
|
+
def _get(self, *args: Any, **kwargs: Any) -> Any:
|
|
44
|
+
return self.bridge.get_light(self.light_id, *args, **kwargs)
|
|
45
|
+
|
|
46
|
+
def _set(self, *args: Any, **kwargs: Any) -> Any:
|
|
47
|
+
if self.transitiontime is not None:
|
|
48
|
+
kwargs["transitiontime"] = self.transitiontime
|
|
49
|
+
logger.debug(
|
|
50
|
+
f"Setting with transitiontime = {self.transitiontime} ds = {float(self.transitiontime) / 10} s"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if (args[0] == "on" and args[1] is False) or (
|
|
54
|
+
kwargs.get("on", True) is False
|
|
55
|
+
):
|
|
56
|
+
self._reset_bri_after_on = True
|
|
57
|
+
return self.bridge.set_light(self.light_id, *args, **kwargs)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def name(self) -> str:
|
|
61
|
+
"""Get or set the name of the light [string]"""
|
|
62
|
+
fetched_name = self._get("name")
|
|
63
|
+
assert isinstance(fetched_name, str), (
|
|
64
|
+
f"Expected str for name, got {type(fetched_name)}"
|
|
65
|
+
)
|
|
66
|
+
self._name = fetched_name
|
|
67
|
+
return self._name
|
|
68
|
+
|
|
69
|
+
@name.setter
|
|
70
|
+
def name(self, value: str) -> None:
|
|
71
|
+
old_name = self.name
|
|
72
|
+
self._name = value
|
|
73
|
+
self._set("name", self._name)
|
|
74
|
+
|
|
75
|
+
logger.debug(f"Renaming light from '{old_name}' to '{value}'")
|
|
76
|
+
|
|
77
|
+
self.bridge.lights_by_name[self.name] = self
|
|
78
|
+
del self.bridge.lights_by_name[old_name]
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def on(self) -> bool:
|
|
82
|
+
"""Get or set the state of the light [True|False]"""
|
|
83
|
+
self._on = self._get("on")
|
|
84
|
+
assert self._on is not None, "Failed to get 'on' state from bridge."
|
|
85
|
+
assert isinstance(self._on, bool), (
|
|
86
|
+
f"Expected bool for 'on' state, got {type(self._on)}"
|
|
87
|
+
)
|
|
88
|
+
return self._on
|
|
89
|
+
|
|
90
|
+
@on.setter
|
|
91
|
+
def on(self, value: bool) -> None:
|
|
92
|
+
# Some added code here to work around known bug where
|
|
93
|
+
# turning off with transitiontime set makes it restart on brightness = 1
|
|
94
|
+
if self._on and value is False:
|
|
95
|
+
self._reset_bri_after_on = self.transitiontime is not None
|
|
96
|
+
if self._reset_bri_after_on:
|
|
97
|
+
logger.warning(
|
|
98
|
+
"Turned off light with transitiontime specified, brightness will be reset on power on"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
self._set("on", value)
|
|
102
|
+
|
|
103
|
+
# work around bug by resetting brightness after a power on
|
|
104
|
+
if self._on is False and value is True:
|
|
105
|
+
if isinstance(self._reset_bri_after_on, bool) and self._reset_bri_after_on:
|
|
106
|
+
logger.warning(
|
|
107
|
+
"Light was turned off with transitiontime specified, brightness needs to be reset now."
|
|
108
|
+
)
|
|
109
|
+
if self._brightness is not None:
|
|
110
|
+
self.brightness = self._brightness
|
|
111
|
+
else:
|
|
112
|
+
logger.warning(
|
|
113
|
+
"Cannot reset brightness, initial brightness value not available."
|
|
114
|
+
)
|
|
115
|
+
self._reset_bri_after_on = False
|
|
116
|
+
|
|
117
|
+
self._on = value
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def colormode(self) -> str:
|
|
121
|
+
"""Get the color mode of the light [hs|xy|ct]"""
|
|
122
|
+
self._colormode = self._get("colormode")
|
|
123
|
+
assert self._colormode is not None, "Failed to get 'colormode' from bridge."
|
|
124
|
+
assert isinstance(self._colormode, str), (
|
|
125
|
+
f"Expected str for colormode, got {type(self._colormode)}"
|
|
126
|
+
)
|
|
127
|
+
return self._colormode
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def brightness(self) -> int:
|
|
131
|
+
"""Get or set the brightness of the light [0-254].
|
|
132
|
+
|
|
133
|
+
0 is not off"""
|
|
134
|
+
self._brightness = self._get("bri")
|
|
135
|
+
assert self._brightness is not None, (
|
|
136
|
+
"Failed to get brightness ('bri') from bridge."
|
|
137
|
+
)
|
|
138
|
+
assert isinstance(self._brightness, int), (
|
|
139
|
+
f"Expected int for brightness, got {type(self._brightness)}"
|
|
140
|
+
)
|
|
141
|
+
return self._brightness
|
|
142
|
+
|
|
143
|
+
@brightness.setter
|
|
144
|
+
def brightness(self, value: int) -> None:
|
|
145
|
+
self._brightness = value
|
|
146
|
+
self._set("bri", self._brightness)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def hue(self) -> int:
|
|
150
|
+
"""Get or set the hue of the light [0-65535]"""
|
|
151
|
+
self._hue = self._get("hue")
|
|
152
|
+
assert self._hue is not None, "Failed to get hue from bridge."
|
|
153
|
+
assert isinstance(self._hue, int), (
|
|
154
|
+
f"Expected int for hue, got {type(self._hue)}"
|
|
155
|
+
)
|
|
156
|
+
return self._hue
|
|
157
|
+
|
|
158
|
+
@hue.setter
|
|
159
|
+
def hue(self, value: int) -> None:
|
|
160
|
+
self._hue = int(value)
|
|
161
|
+
self._set("hue", self._hue)
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def saturation(self) -> int:
|
|
165
|
+
"""Get or set the saturation of the light [0-254]
|
|
166
|
+
|
|
167
|
+
0 = white
|
|
168
|
+
254 = most saturated
|
|
169
|
+
"""
|
|
170
|
+
self._saturation = self._get("sat")
|
|
171
|
+
assert self._saturation is not None, (
|
|
172
|
+
"Failed to get saturation ('sat') from bridge."
|
|
173
|
+
)
|
|
174
|
+
assert isinstance(self._saturation, int), (
|
|
175
|
+
f"Expected int for saturation, got {type(self._saturation)}"
|
|
176
|
+
)
|
|
177
|
+
return self._saturation
|
|
178
|
+
|
|
179
|
+
@saturation.setter
|
|
180
|
+
def saturation(self, value: int) -> None:
|
|
181
|
+
self._saturation = value
|
|
182
|
+
self._set("sat", self._saturation)
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def xy(self) -> tuple[float, float]:
|
|
186
|
+
"""Get or set the color coordinates of the light [ [0.0-1.0, 0.0-1.0] ]
|
|
187
|
+
|
|
188
|
+
This is in a color space similar to CIE 1931 (but not quite identical)
|
|
189
|
+
"""
|
|
190
|
+
self._xy = self._get("xy")
|
|
191
|
+
assert self._xy is not None, (
|
|
192
|
+
"Failed to get color coordinates ('xy') from bridge."
|
|
193
|
+
)
|
|
194
|
+
assert isinstance(self._xy, tuple | list) and len(self._xy) == 2, (
|
|
195
|
+
f"Expected tuple/list of 2 floats for xy, got {self._xy}"
|
|
196
|
+
)
|
|
197
|
+
try:
|
|
198
|
+
xy_tuple = (float(self._xy[0]), float(self._xy[1]))
|
|
199
|
+
self._xy = xy_tuple
|
|
200
|
+
return xy_tuple
|
|
201
|
+
except (ValueError, TypeError, IndexError):
|
|
202
|
+
raise AssertionError(
|
|
203
|
+
f"Could not parse xy coordinates as tuple[float, float]: {self._xy}"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
@xy.setter
|
|
207
|
+
def xy(self, value: tuple[float, float]) -> None:
|
|
208
|
+
self._xy = value
|
|
209
|
+
self._set("xy", self._xy)
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def colortemp(self) -> int:
|
|
213
|
+
"""Get or set the color temperature of the light, in units of mireds [154-500]"""
|
|
214
|
+
self._colortemp = self._get("ct")
|
|
215
|
+
assert self._colortemp is not None, (
|
|
216
|
+
"Failed to get color temperature ('ct') from bridge."
|
|
217
|
+
)
|
|
218
|
+
assert isinstance(self._colortemp, int), (
|
|
219
|
+
f"Expected int for color temperature, got {type(self._colortemp)}"
|
|
220
|
+
)
|
|
221
|
+
return self._colortemp
|
|
222
|
+
|
|
223
|
+
@colortemp.setter
|
|
224
|
+
def colortemp(self, value: int) -> None:
|
|
225
|
+
if value < 154:
|
|
226
|
+
logger.warning("154 mireds is coolest allowed color temp")
|
|
227
|
+
elif value > 500:
|
|
228
|
+
logger.warning("500 mireds is warmest allowed color temp")
|
|
229
|
+
self._colortemp = value
|
|
230
|
+
self._set("ct", self._colortemp)
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def colortemp_k(self) -> int:
|
|
234
|
+
"""Get or set the color temperature of the light, in units of Kelvin [2000-6500]"""
|
|
235
|
+
ct_mired = self.colortemp
|
|
236
|
+
return int(round(1e6 / ct_mired))
|
|
237
|
+
|
|
238
|
+
@colortemp_k.setter
|
|
239
|
+
def colortemp_k(self, value: int) -> None:
|
|
240
|
+
if value > 6500:
|
|
241
|
+
logger.warning("6500 K is max allowed color temp")
|
|
242
|
+
value = 6500
|
|
243
|
+
elif value < 2000:
|
|
244
|
+
logger.warning("2000 K is min allowed color temp")
|
|
245
|
+
value = 2000
|
|
246
|
+
|
|
247
|
+
colortemp_mireds = int(round(1e6 / value))
|
|
248
|
+
logger.debug(f"{value:d} K is {colortemp_mireds} mireds")
|
|
249
|
+
self.colortemp = colortemp_mireds
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def effect(self) -> str:
|
|
253
|
+
"""Check the effect setting of the light. [none|colorloop]"""
|
|
254
|
+
self._effect = self._get("effect")
|
|
255
|
+
assert self._effect is not None, "Failed to get effect from bridge."
|
|
256
|
+
assert isinstance(self._effect, str), (
|
|
257
|
+
f"Expected str for effect, got {type(self._effect)}"
|
|
258
|
+
)
|
|
259
|
+
return self._effect
|
|
260
|
+
|
|
261
|
+
@effect.setter
|
|
262
|
+
def effect(self, value: str) -> None:
|
|
263
|
+
self._effect = value
|
|
264
|
+
self._set("effect", self._effect)
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def alert(self) -> str:
|
|
268
|
+
"""Get or set the alert state of the light [select|lselect|none]"""
|
|
269
|
+
self._alert = self._get("alert")
|
|
270
|
+
assert self._alert is not None, "Failed to get alert state from bridge."
|
|
271
|
+
assert isinstance(self._alert, str), (
|
|
272
|
+
f"Expected str for alert state, got {type(self._alert)}"
|
|
273
|
+
)
|
|
274
|
+
return self._alert
|
|
275
|
+
|
|
276
|
+
@alert.setter
|
|
277
|
+
def alert(self, value: str | None) -> None:
|
|
278
|
+
if value is None:
|
|
279
|
+
value = "none"
|
|
280
|
+
self._alert = value
|
|
281
|
+
self._set("alert", self._alert)
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def reachable(self) -> bool:
|
|
285
|
+
"""Get the reachable state of the light [boolean]"""
|
|
286
|
+
self._reachable = self._get("reachable")
|
|
287
|
+
assert self._reachable is not None, "Failed to get reachable state from bridge."
|
|
288
|
+
assert isinstance(self._reachable, bool), (
|
|
289
|
+
f"Expected bool for reachable state, got {type(self._reachable)}"
|
|
290
|
+
)
|
|
291
|
+
return self._reachable
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def type(self) -> str:
|
|
295
|
+
"""Get the type of the light [string]"""
|
|
296
|
+
self._type = self._get("type")
|
|
297
|
+
assert self._type is not None, "Failed to get type from bridge."
|
|
298
|
+
assert isinstance(self._type, str), (
|
|
299
|
+
f"Expected str for type, got {type(self._type)}"
|
|
300
|
+
)
|
|
301
|
+
return self._type
|
phue2/scene.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Scene class for Philips Hue scenes."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Scene:
|
|
7
|
+
"""Container for Scene"""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
sid: str,
|
|
12
|
+
appdata: dict[str, Any] | None = None,
|
|
13
|
+
lastupdated: str | None = None,
|
|
14
|
+
lights: list[str] | None = None,
|
|
15
|
+
locked: bool = False,
|
|
16
|
+
name: str = "",
|
|
17
|
+
owner: str = "",
|
|
18
|
+
picture: str = "",
|
|
19
|
+
recycle: bool = False,
|
|
20
|
+
version: int = 0,
|
|
21
|
+
type: str = "",
|
|
22
|
+
group: str = "",
|
|
23
|
+
):
|
|
24
|
+
"""Initialize a Scene object.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
sid: Scene ID
|
|
28
|
+
appdata: Application data associated with the scene
|
|
29
|
+
lastupdated: Last time the scene was updated
|
|
30
|
+
lights: List of light IDs in the scene
|
|
31
|
+
locked: Whether the scene is locked
|
|
32
|
+
name: Name of the scene
|
|
33
|
+
owner: Owner of the scene
|
|
34
|
+
picture: Picture associated with the scene
|
|
35
|
+
recycle: Whether the scene can be recycled
|
|
36
|
+
version: Version of the scene
|
|
37
|
+
type: Type of the scene
|
|
38
|
+
group: Group associated with the scene
|
|
39
|
+
"""
|
|
40
|
+
self.scene_id = sid
|
|
41
|
+
self.appdata = appdata or {}
|
|
42
|
+
self.lastupdated = lastupdated
|
|
43
|
+
if lights is not None:
|
|
44
|
+
self.lights = sorted([int(x) for x in lights])
|
|
45
|
+
else:
|
|
46
|
+
self.lights = []
|
|
47
|
+
self.locked = locked
|
|
48
|
+
self.name = name
|
|
49
|
+
self.owner = owner
|
|
50
|
+
self.picture = picture
|
|
51
|
+
self.recycle = recycle
|
|
52
|
+
self.version = version
|
|
53
|
+
self.type = type
|
|
54
|
+
self.group = group
|
|
55
|
+
|
|
56
|
+
def __repr__(self) -> str:
|
|
57
|
+
# like default python repr function, but add scene name
|
|
58
|
+
return f'<{self.__class__.__module__}.{self.__class__.__name__} id="{self.scene_id}" name="{self.name}" lights={self.lights}>'
|