1 """Support for Hyperion-NG remotes."""
3 from __future__
import annotations
5 from collections.abc
import Callable, Mapping, Sequence
8 from types
import MappingProxyType
11 from hyperion
import client, const
25 async_dispatcher_connect,
26 async_dispatcher_send,
32 get_hyperion_device_id,
33 get_hyperion_unique_id,
34 listen_for_instance_updates,
37 CONF_EFFECT_HIDE_LIST,
38 CONF_INSTANCE_CLIENTS,
43 HYPERION_MANUFACTURER_NAME,
49 _LOGGER = logging.getLogger(__name__)
51 CONF_DEFAULT_COLOR =
"default_color"
52 CONF_HDMI_PRIORITY =
"hdmi_priority"
53 CONF_EFFECT_LIST =
"effect_list"
62 KEY_EFFECT_SOLID =
"Solid"
64 DEFAULT_COLOR = [255, 255, 255]
65 DEFAULT_BRIGHTNESS = 255
66 DEFAULT_EFFECT = KEY_EFFECT_SOLID
67 DEFAULT_NAME =
"Hyperion"
68 DEFAULT_PORT = const.DEFAULT_PORT_JSON
69 DEFAULT_HDMI_PRIORITY = 880
70 DEFAULT_EFFECT_LIST: list[str] = []
72 ICON_LIGHTBULB =
"mdi:lightbulb"
73 ICON_EFFECT =
"mdi:lava-lamp"
78 config_entry: ConfigEntry,
79 async_add_entities: AddEntitiesCallback,
81 """Set up a Hyperion platform from config entry."""
83 entry_data = hass.data[DOMAIN][config_entry.entry_id]
84 server_id = config_entry.unique_id
87 def instance_add(instance_num: int, instance_name: str) ->
None:
88 """Add entities for a new Hyperion instance."""
95 entry_data[CONF_INSTANCE_CLIENTS][instance_num],
104 def instance_remove(instance_num: int) ->
None:
105 """Remove entities for an old Hyperion instance."""
109 SIGNAL_ENTITY_REMOVE.format(
118 """A Hyperion light that acts as a client for the configured priority."""
120 _attr_has_entity_name =
True
122 _attr_color_mode = ColorMode.HS
123 _attr_should_poll =
False
124 _attr_supported_color_modes = {ColorMode.HS}
125 _attr_supported_features = LightEntityFeature.EFFECT
132 options: MappingProxyType[str, Any],
133 hyperion_client: client.HyperionClient,
135 """Initialize the light."""
144 self.
_rgb_color_rgb_color: Sequence[int] = DEFAULT_COLOR
145 self.
_effect_effect: str = KEY_EFFECT_SOLID
147 self._static_effect_list: list[str] = [KEY_EFFECT_SOLID]
148 self.
_effect_list_effect_list: list[str] = self._static_effect_list[:]
150 self._client_callbacks: Mapping[str, Callable[[dict[str, Any]],
None]] = {
151 f
"{const.KEY_ADJUSTMENT}-{const.KEY_UPDATE}": self.
_update_adjustment_update_adjustment,
152 f
"{const.KEY_COMPONENTS}-{const.KEY_UPDATE}": self.
_update_components_update_components,
154 f
"{const.KEY_PRIORITIES}-{const.KEY_UPDATE}": self.
_update_priorities_update_priorities,
155 f
"{const.KEY_CLIENT}-{const.KEY_UPDATE}": self.
_update_client_update_client,
158 identifiers={(DOMAIN, self.
_device_id_device_id)},
159 manufacturer=HYPERION_MANUFACTURER_NAME,
160 model=HYPERION_MODEL_NAME,
162 configuration_url=self.
_client_client.remote_url,
166 """Compute a unique id for this instance."""
171 """Return the brightness of this light between 0..255."""
176 """Return last color value set."""
177 return color_util.color_RGB_to_hs(*self.
_rgb_color_rgb_color)
181 """Return state specific icon."""
185 return ICON_LIGHTBULB
189 """Return the current effect."""
194 """Return the list of supported effects."""
199 """Return server availability."""
203 """Get a value from the provided options."""
205 CONF_PRIORITY: DEFAULT_PRIORITY,
206 CONF_EFFECT_HIDE_LIST: [],
208 return self.
_options_options.
get(key, defaults[key])
212 """Return true if light is on. Light is considered on when there is a source at the configured HA priority."""
216 """Turn on the light."""
218 if ATTR_EFFECT
not in kwargs
and ATTR_HS_COLOR
in kwargs:
219 effect = KEY_EFFECT_SOLID
221 effect = kwargs.get(ATTR_EFFECT, self.
_effect_effect)
222 rgb_color: Sequence[int]
223 if ATTR_HS_COLOR
in kwargs:
224 rgb_color = color_util.color_hs_to_RGB(*kwargs[ATTR_HS_COLOR])
229 if ATTR_BRIGHTNESS
in kwargs:
230 brightness = kwargs[ATTR_BRIGHTNESS]
231 for item
in self.
_client_client.adjustment
or []:
234 and not await self.
_client_client.async_send_set_adjustment(
236 const.KEY_ADJUSTMENT: {
237 const.KEY_BRIGHTNESS:
int(
238 round((
float(brightness) * 100) / 255)
240 const.KEY_ID: item[const.KEY_ID],
248 if effect
and effect != KEY_EFFECT_SOLID:
249 if not await self.
_client_client.async_send_set_effect(
251 const.KEY_PRIORITY: self.
_get_option_get_option(CONF_PRIORITY),
252 const.KEY_EFFECT: {const.KEY_NAME: effect},
253 const.KEY_ORIGIN: DEFAULT_ORIGIN,
259 elif not await self.
_client_client.async_send_set_color(
261 const.KEY_PRIORITY: self.
_get_option_get_option(CONF_PRIORITY),
262 const.KEY_COLOR: rgb_color,
263 const.KEY_ORIGIN: DEFAULT_ORIGIN,
269 """Turn off the light i.e. clear the configured priority."""
270 if not await self.
_client_client.async_send_clear(
271 **{const.KEY_PRIORITY: self.
_get_option_get_option(CONF_PRIORITY)}
277 brightness: int |
None =
None,
278 rgb_color: Sequence[int] |
None =
None,
279 effect: str |
None =
None,
281 """Set the internal state."""
282 if brightness
is not None:
284 if rgb_color
is not None:
286 if effect
is not None:
291 """Update Hyperion components."""
296 """Update Hyperion adjustments."""
297 if self.
_client_client.adjustment:
298 brightness_pct = self.
_client_client.adjustment[0].
get(
299 const.KEY_BRIGHTNESS, DEFAULT_BRIGHTNESS
301 if brightness_pct < 0
or brightness_pct > 100:
304 brightness=
int(round((brightness_pct * 255) /
float(100)))
310 """Update Hyperion priorities."""
313 component_id = priority.get(const.KEY_COMPONENTID)
314 if component_id == const.KEY_COMPONENTID_EFFECT:
318 rgb_color=DEFAULT_COLOR, effect=priority[const.KEY_OWNER]
320 elif component_id == const.KEY_COMPONENTID_COLOR:
322 rgb_color=priority[const.KEY_VALUE][const.KEY_RGB],
323 effect=KEY_EFFECT_SOLID,
329 """Update Hyperion effects."""
330 if not self.
_client_client.effects:
332 effect_list: list[str] = []
333 hide_effects = self.
_get_option_get_option(CONF_EFFECT_HIDE_LIST)
335 for effect
in self.
_client_client.effects
or []:
336 if const.KEY_NAME
in effect:
337 effect_name = effect[const.KEY_NAME]
338 if effect_name
not in hide_effects:
339 effect_list.append(effect_name)
342 effect
for effect
in self._static_effect_list
if effect
not in hide_effects
348 """Update full Hyperion state."""
355 "Hyperion full state update: On=%s,Brightness=%i,Effect=%s "
356 "(%i effects total),Color=%s"
367 """Update client connection state."""
371 """Register callbacks when entity added to hass."""
375 SIGNAL_ENTITY_REMOVE.format(self.
unique_idunique_id),
376 functools.partial(self.
async_removeasync_remove, force_remove=
True),
380 self.
_client_client.add_callbacks(self._client_callbacks)
386 """Cleanup prior to hass removal."""
387 self.
_client_client.remove_callbacks(self._client_callbacks)
390 """Get the relevant Hyperion priority entry to consider."""
392 for priority
in self.
_client_client.priorities
or []:
393 if priority.get(const.KEY_PRIORITY) == self.
_get_option_get_option(CONF_PRIORITY):
None async_turn_off(self, **Any kwargs)
None _set_internal_state(self, int|None brightness=None, Sequence[int]|None rgb_color=None, str|None effect=None)
str _compute_unique_id(self, str server_id, int instance_num)
None __init__(self, str server_id, int instance_num, str instance_name, MappingProxyType[str, Any] options, client.HyperionClient hyperion_client)
None async_added_to_hass(self)
None _update_effect_list(self, dict[str, Any]|None _=None)
None async_turn_on(self, **Any kwargs)
None _update_adjustment(self, dict[str, Any]|None _=None)
None _update_priorities(self, dict[str, Any]|None _=None)
dict[str, Any]|None _get_priority_entry_that_dictates_state(self)
None _update_client(self, dict[str, Any]|None _=None)
None _update_full_state(self)
Any _get_option(self, str key)
None _update_components(self, dict[str, Any]|None _=None)
tuple[float, float] hs_color(self)
None async_will_remove_from_hass(self)
list[str] effect_list(self)
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
None async_remove(self, *bool force_remove=False)
web.Response get(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
str get_hyperion_device_id(str server_id, int instance)
str get_hyperion_unique_id(str server_id, int instance, str name)
None listen_for_instance_updates(HomeAssistant hass, ConfigEntry config_entry, Callable add_func, Callable remove_func)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)