1 """Class to hold all light accessories."""
3 from __future__
import annotations
5 from datetime
import datetime
9 from pyhap.const
import CATEGORY_LIGHTBULB
15 ATTR_COLOR_TEMP_KELVIN,
17 ATTR_MAX_COLOR_TEMP_KELVIN,
18 ATTR_MIN_COLOR_TEMP_KELVIN,
21 ATTR_SUPPORTED_COLOR_MODES,
23 DOMAIN
as LIGHT_DOMAIN,
38 color_temperature_kelvin_to_mired,
39 color_temperature_mired_to_kelvin,
40 color_temperature_to_hs,
41 color_temperature_to_rgbww,
44 from .accessories
import TYPES, HomeAccessory
47 CHAR_COLOR_TEMPERATURE,
56 _LOGGER = logging.getLogger(__name__)
59 CHANGE_COALESCE_TIME_WINDOW = 0.01
61 DEFAULT_MIN_COLOR_TEMP = 2000
62 DEFAULT_MAX_COLOR_TEMP = 6500
64 COLOR_MODES_WITH_WHITES = {ColorMode.RGBW, ColorMode.RGBWW, ColorMode.WHITE}
67 @TYPES.register("Light")
69 """Generate a Light accessory for a light entity.
71 Currently supports: state, brightness, color temperature, rgb_color.
75 """Initialize a new Light accessory object."""
76 super().
__init__(*args, category=CATEGORY_LIGHTBULB)
79 ATTR_SUPPORTED_COLOR_MODES,
80 ATTR_MAX_COLOR_TEMP_KELVIN,
81 ATTR_MIN_COLOR_TEMP_KELVIN,
85 self.
_event_timer_event_timer: CALLBACK_TYPE |
None =
None
90 attributes = state.attributes
92 attributes.get(ATTR_SUPPORTED_COLOR_MODES)
or []
103 self.
charschars.append(CHAR_BRIGHTNESS)
106 self.
charschars.extend([CHAR_HUE, CHAR_SATURATION])
111 self.
charschars.append(CHAR_COLOR_TEMPERATURE)
113 serv_light = self.add_preload_service(SERV_LIGHTBULB, self.
charschars)
114 self.
char_onchar_on = serv_light.configure_char(CHAR_ON, value=0)
120 self.
char_brightnesschar_brightness = serv_light.configure_char(CHAR_BRIGHTNESS, value=100)
122 if CHAR_COLOR_TEMPERATURE
in self.
charschars:
124 attributes.get(ATTR_MAX_COLOR_TEMP_KELVIN, DEFAULT_MAX_COLOR_TEMP)
127 attributes.get(ATTR_MIN_COLOR_TEMP_KELVIN, DEFAULT_MIN_COLOR_TEMP)
132 CHAR_COLOR_TEMPERATURE,
141 self.
char_huechar_hue = serv_light.configure_char(CHAR_HUE, value=0)
142 self.
char_saturationchar_saturation = serv_light.configure_char(CHAR_SATURATION, value=75)
145 serv_light.setter_callback = self.
_set_chars_set_chars
148 _LOGGER.debug(
"Light _set_chars: %s", char_values)
151 CHAR_SATURATION
in char_values
or CHAR_HUE
in char_values
154 for char
in (CHAR_HUE, CHAR_SATURATION):
155 if char
in self.
_pending_events_pending_events
and CHAR_COLOR_TEMPERATURE
in char_values:
167 """Process all changes at once."""
168 _LOGGER.debug(
"Coalesced _set_chars: %s", self.
_pending_events_pending_events)
172 service = SERVICE_TURN_ON
173 params: dict[str, Any] = {ATTR_ENTITY_ID: self.
entity_identity_id}
174 has_on = CHAR_ON
in char_values
177 if not char_values[CHAR_ON]:
178 service = SERVICE_TURN_OFF
179 events.append(f
"Set state to {char_values[CHAR_ON]}")
181 brightness_pct =
None
182 if CHAR_BRIGHTNESS
in char_values:
183 if char_values[CHAR_BRIGHTNESS] == 0:
185 events[-1] =
"Set state to 0"
187 events.append(
"Set state to 0")
188 service = SERVICE_TURN_OFF
190 brightness_pct = char_values[CHAR_BRIGHTNESS]
191 events.append(f
"brightness at {char_values[CHAR_BRIGHTNESS]}%")
193 if service == SERVICE_TURN_OFF:
197 {ATTR_ENTITY_ID: self.
entity_identity_id},
203 if CHAR_COLOR_TEMPERATURE
in char_values:
204 temp = char_values[CHAR_COLOR_TEMPERATURE]
205 events.append(f
"color temperature at {temp}")
207 ((brightness_pct
or self.
char_brightnesschar_brightness.value) * 255) / 100
219 params[ATTR_RGBW_COLOR] = (*(0,) * 3, bright_val)
221 params[ATTR_WHITE] = bright_val
223 elif CHAR_HUE
in char_values
or CHAR_SATURATION
in char_values:
225 char_values.get(CHAR_HUE, self.
char_huechar_hue.value),
226 char_values.get(CHAR_SATURATION, self.
char_saturationchar_saturation.value),
228 _LOGGER.debug(
"%s: Set hs_color to %s", self.
entity_identity_id, hue_sat)
229 events.append(f
"set color at {hue_sat}")
230 params[ATTR_HS_COLOR] = hue_sat
234 and ATTR_RGBWW_COLOR
not in params
235 and ATTR_RGBW_COLOR
not in params
237 params[ATTR_BRIGHTNESS_PCT] = brightness_pct
240 "Calling light service with params: %s -> %s", char_values, params
242 self.
async_call_serviceasync_call_service(LIGHT_DOMAIN, service, params,
", ".join(events))
246 """Update light after state change."""
248 state = new_state.state
249 attributes = new_state.attributes
250 color_mode = attributes.get(ATTR_COLOR_MODE)
251 self.
char_onchar_on.set_value(
int(state == STATE_ON))
258 and (brightness := attributes.get(ATTR_BRIGHTNESS))
is not None
259 and isinstance(brightness, (int, float))
261 brightness = round(brightness / 255 * 100, 0)
272 if brightness == 0
and state == STATE_ON:
275 if color_mode_changed:
281 if color_temp := attributes.get(ATTR_COLOR_TEMP_KELVIN):
283 elif color_mode == ColorMode.WHITE:
284 hue, saturation = 0, 0
285 elif hue_sat := attributes.get(ATTR_HS_COLOR):
286 hue, saturation = hue_sat
290 if isinstance(hue, (int, float))
and isinstance(saturation, (int, float)):
291 self.
char_huechar_hue.set_value(round(hue, 0))
293 if color_mode_changed:
299 if CHAR_COLOR_TEMPERATURE
in self.
charschars:
302 color_temp_kelvin = attributes.get(ATTR_COLOR_TEMP_KELVIN)
303 if color_temp_kelvin
is not None:
305 elif color_mode == ColorMode.WHITE:
307 if isinstance(color_temp, (int, float)):
309 if color_mode_changed:
None async_call_service(self, str domain, str service, dict[str, Any]|None service_data, Any|None value=None)
None async_update_state(self, State new_state)
None __init__(self, *Any args)
None _set_chars(self, dict[str, Any] char_values)
None async_update_state(self, State new_state)
None _async_send_events(self, datetime _now)
IssData update(pyiss.ISS iss)
CALLBACK_TYPE async_call_later(HomeAssistant hass, float|timedelta delay, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action)
int color_temperature_mired_to_kelvin(float mired_temperature)
tuple[float, float] color_temperature_to_hs(float color_temperature_kelvin)
tuple[int, int, int, int, int] color_temperature_to_rgbww(int temperature, int brightness, int min_kelvin, int max_kelvin)
int color_temperature_kelvin_to_mired(float kelvin_temperature)