forked from anki/vector-python-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlights.py
More file actions
executable file
·210 lines (167 loc) · 7.5 KB
/
lights.py
File metadata and controls
executable file
·210 lines (167 loc) · 7.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# Copyright (c) 2018 Anki, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License in the file LICENSE.txt or at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper routines for dealing with Vector's Cube lights and colors."""
# __all__ should order by constants, event classes, other classes, functions.
__all__ = ['MAX_COLOR_PROFILE', 'WHITE_BALANCED_CUBE_PROFILE',
'blue_light', 'cyan_light', 'green_light', 'magenta_light', 'off_light',
'red_light', 'white_light', 'yellow_light',
'Color', 'ColorProfile', 'Light', 'package_request_params']
from .color import Color, green, red, blue, cyan, magenta, yellow, white, off
class ColorProfile:
"""Applies transforms to make Vector's Cube lights and colors appear as
intended, by limiting maximum channel intensity.
:param red_multiplier: Scaling value for the brightness of red Lights
:param green_multiplier: Scaling value for the brightness of green Lights
:param blue_multiplier: Scaling value for the brightness of blue Lights
"""
def __init__(self, red_multiplier: float, green_multiplier: float, blue_multiplier: float):
self._red_multiplier = red_multiplier
self._green_multiplier = green_multiplier
self._blue_multiplier = blue_multiplier
# TODO Needs docs, param types, sample code
def augment_color(self, original_color):
rgb = [
(original_color.int_color >> 24) & 0xff,
(original_color.int_color >> 16) & 0xff,
(original_color.int_color >> 8) & 0xff
]
rgb[0] = int(self._red_multiplier * rgb[0])
rgb[1] = int(self._green_multiplier * rgb[1])
rgb[2] = int(self._blue_multiplier * rgb[2])
result_int_code = (rgb[0] << 24) | (rgb[1] << 16) | (rgb[2] << 8) | 0xff
return Color(result_int_code)
# TODO Needs example code, more descriptive docs
@property
def red_multiplier(self):
"""float: The multiplier used on the red channel."""
return self._red_multiplier
# TODO Needs example code, more descriptive docs
@property
def green_multiplier(self):
"""float: The multiplier used on the red channel."""
return self._green_multiplier
# TODO Needs example code, more descriptive docs
@property
def blue_multiplier(self):
"""float: The multiplier used on the red channel."""
return self._blue_multiplier
#: :class:`ColorProfile`: Color profile to get the maximum possible brightness out of each LED.
MAX_COLOR_PROFILE = ColorProfile(red_multiplier=1.0,
green_multiplier=1.0,
blue_multiplier=1.0)
#: :class:`ColorProfile`: Color profile balanced so that a max color value more closely resembles pure white.
# TODO: Balance this more carefully once robots with proper color pipe
# hardware becomes available
WHITE_BALANCED_CUBE_PROFILE = ColorProfile(red_multiplier=1.0,
green_multiplier=0.95,
blue_multiplier=0.7)
class Light:
"""Lights are used with Vector's Cube.
Lights may either be "on" or "off", though in practice any colors may be
assigned to either state (including no color/light).
"""
def __init__(self,
on_color: Color = off,
off_color: Color = off,
on_period_ms: int = 250,
off_period_ms: int = 0,
transition_on_period_ms: int = 0,
transition_off_period_ms: int = 0):
self._on_color = on_color
self._off_color = off_color
self._on_period_ms = on_period_ms
self._off_period_ms = off_period_ms
self._transition_on_period_ms = transition_on_period_ms
self._transition_off_period_ms = transition_off_period_ms
@property
def on_color(self) -> Color:
"""The color shown when the light is on."""
return self._on_color
@on_color.setter
def on_color(self, color):
if not isinstance(color, Color):
raise TypeError("Must specify a Color")
self._on_color = color
@property
def off_color(self) -> Color:
"""The color shown when the light is off."""
return self._off_color
@off_color.setter
def off_color(self, color):
if not isinstance(color, Color):
raise TypeError("Must specify a Color")
self._off_color = color
@property
def on_period_ms(self) -> int:
"""The number of milliseconds the light should be "on" for for each cycle."""
return self._on_period_ms
@on_period_ms.setter
def on_period_ms(self, ms):
if not 0 < ms < 2**32:
raise ValueError("Invalid value")
self._on_period_ms = ms
@property
def off_period_ms(self) -> int:
"""The number of milliseconds the light should be "off" for for each cycle."""
return self._off_period_ms
@off_period_ms.setter
def off_period_ms(self, ms):
if not 0 < ms < 2**32:
raise ValueError("Invalid value")
self._off_period_ms = ms
@property
def transition_on_period_ms(self) -> int:
"""The number of milliseconds to take to transition the light to the on color."""
return self._transition_on_period_ms
@transition_on_period_ms.setter
def transition_on_period_ms(self, ms):
if not 0 < ms < 2**32:
raise ValueError("Invalid value")
self._transition_on_period_ms = ms
@property
def transition_off_period_ms(self) -> int:
"""The number of milliseconds to take to transition the light to the off color."""
return self._transition_off_period_ms
@transition_off_period_ms.setter
def transition_off_period_ms(self, ms):
if not 0 < ms < 2**32:
raise ValueError("Invalid value")
self._transition_off_period_ms = ms
# TODO needs docs, param types. Should this be private? Maybe a more descriptive name?
def package_request_params(lights, color_profile):
merged_params = {}
for light in lights:
for attr_name in vars(light):
attr_name = attr_name[1:]
attr_val = getattr(light, attr_name)
if isinstance(attr_val, Color):
attr_val = color_profile.augment_color(attr_val).int_color
merged_params.setdefault(attr_name, []).append(attr_val)
return merged_params
#: :class:`Light`: A steady green colored LED light.
green_light = Light(on_color=green)
#: :class:`Light`: A steady red colored LED light.
red_light = Light(on_color=red)
#: :class:`Light`: A steady blue colored LED light.
blue_light = Light(on_color=blue)
#: :class:`Light`: A steady cyan colored LED light.
cyan_light = Light(on_color=cyan)
#: :class:`Light`: A steady magenta colored LED light.
magenta_light = Light(on_color=magenta)
#: :class:`Light`: A steady yellow colored LED light.
yellow_light = Light(on_color=yellow)
#: :class:`Light`: A steady white colored LED light.
white_light = Light(on_color=white)
#: :class:`Light`: A steady off (non-illuminated LED light).
off_light = Light(on_color=off)