feat(api-input): expose timeout/interpolation, fix 422 on light turn-on
- coordinator.update_source now auto-injects source_type from cached data so the server's discriminated-union body validates (was rejecting fallback_color updates with 422). - Add Fallback Timeout (number) and Interpolation (select) entities per api_input CSS source. - Light no longer zeros fallback_color on turn_off; state restored across HA restarts via RestoreEntity so the chosen default color persists. - Bump manifest to 0.2.2 and update en/ru translations.
This commit is contained in:
@@ -27,6 +27,11 @@ async def async_setup_entry(
|
||||
coordinator: LedGrabCoordinator = data[DATA_COORDINATOR]
|
||||
|
||||
entities: list[NumberEntity] = []
|
||||
if coordinator.data:
|
||||
for source in coordinator.data.get("css_sources") or []:
|
||||
if source.get("source_type") == "api_input":
|
||||
entities.append(ApiInputTimeout(coordinator, source, entry.entry_id))
|
||||
|
||||
if coordinator.data and "targets" in coordinator.data:
|
||||
devices = coordinator.data.get("devices") or {}
|
||||
|
||||
@@ -231,3 +236,67 @@ class HALightColorTolerance(_HALightNumberBase):
|
||||
super().__init__(coordinator, target_id, entry_id, field_name="color_tolerance")
|
||||
self._attr_unique_id = f"{target_id}_color_tolerance"
|
||||
self._attr_translation_key = "ha_light_color_tolerance"
|
||||
|
||||
|
||||
# --- api_input CSS source number entities ---
|
||||
|
||||
|
||||
class ApiInputTimeout(CoordinatorEntity, NumberEntity):
|
||||
"""Fallback timeout for an api_input CSS source.
|
||||
|
||||
The server reverts the strip to ``fallback_color`` when no LED data has
|
||||
arrived for this many seconds. Stored server-side as a ``BindableFloat``
|
||||
(plain number when unbound, ``{"value", "source_id"}`` when bound).
|
||||
"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_native_min_value = 0.0
|
||||
_attr_native_max_value = 300.0
|
||||
_attr_native_step = 0.5
|
||||
_attr_native_unit_of_measurement = "s"
|
||||
_attr_mode = NumberMode.BOX
|
||||
_attr_icon = "mdi:timer-sand"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: LedGrabCoordinator,
|
||||
source: dict[str, Any],
|
||||
entry_id: str,
|
||||
) -> None:
|
||||
super().__init__(coordinator)
|
||||
self._source_id: str = source["id"]
|
||||
self._entry_id = entry_id
|
||||
self._attr_unique_id = f"{self._source_id}_timeout"
|
||||
self._attr_translation_key = "api_input_timeout"
|
||||
|
||||
@property
|
||||
def device_info(self) -> dict[str, Any]:
|
||||
return {"identifiers": {(DOMAIN, self._source_id)}}
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
source = self._get_source()
|
||||
if not source:
|
||||
return None
|
||||
raw = source.get("timeout")
|
||||
if isinstance(raw, dict):
|
||||
return raw.get("value")
|
||||
if isinstance(raw, (int, float)):
|
||||
return float(raw)
|
||||
return None
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
return self._get_source() is not None
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
await self.coordinator.update_source(self._source_id, timeout=round(value, 2))
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
def _get_source(self) -> dict[str, Any] | None:
|
||||
if not self.coordinator.data:
|
||||
return None
|
||||
for source in self.coordinator.data.get("css_sources") or []:
|
||||
if source.get("id") == self._source_id:
|
||||
return source
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user