Die Firmware kann sACN/E1.31 - jetzt auch in Home Assistant, aufgebaut wie in
ha-ninlight: Schalter fuer den Empfang, Zahlen fuer Universe und Startslot,
Auswahl fuer das Verhalten nach Stromende, zwei Diagnose-Binaersensoren.
Die Einstellungen liegen hinter system/settings, der Koordinator liest sie
deshalb mit - eine vierte Anfrage je Zyklus. Nach dem Schreiben wird
zurueckgelesen statt angenommen, wie in ha-ninlight.
Zwei Unterschiede zu ninlight:
- Kein Layout-Feld: ninstrip nutzt fest drei Kanaele je Pixel.
- Der Startslot gilt in JEDEM belegten Universe. Die Obergrenze ist deshalb
513 minus Kanaele je Pixel, also 510 - ein festes 512 boete Werte an, bei
denen kein einziges Pixel mehr ins Universe passt.
Aeltere Firmware ohne sacn-Block: die Entitaeten entstehen trotzdem und
melden sich nicht verfuegbar, bis ein Update kommt.
In ha-dev an der Stehlampe geprueft: Universe, Startslot, on_timeout und der
Schalter kommen jeweils auf dem Geraet an; der Aktiv-Sensor steht waehrend
eines Stroms auf an, danach auf aus. 511 als Startslot weist Home Assistant
selbst ab ("outside valid range 1 - 510"). Einstellungen der Lampe danach
zurueckgestellt.
89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
"""sACN-Zahlenfelder eines ninstrip-Geraets: Universe und Startslot."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from homeassistant.components.number import NumberEntity, NumberMode
|
|
from homeassistant.const import EntityCategory
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
|
|
from .coordinator import NinstripConfigEntry, NinstripCoordinator
|
|
from .entity import NinstripSacnEntity
|
|
|
|
PARALLEL_UPDATES = 0
|
|
|
|
# Kanaele je Pixel, falls das Geraet sie nicht meldet. Die Firmware stellt fest
|
|
# drei ein (R, G, B) - so senden LedFx, xLights und Resolume.
|
|
_KANAELE_VORGABE = 3
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: NinstripConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
coordinator = entry.runtime_data
|
|
async_add_entities(
|
|
[NinstripSacnUniverse(coordinator), NinstripSacnStartSlot(coordinator)]
|
|
)
|
|
|
|
|
|
class NinstripSacnUniverse(NinstripSacnEntity, NumberEntity):
|
|
"""Das erste Universe. Braucht der Strip mehr, folgen die naechsten."""
|
|
|
|
_attr_entity_category = EntityCategory.CONFIG
|
|
_attr_translation_key = "sacn_universe"
|
|
_attr_native_min_value = 1
|
|
_attr_native_max_value = 63_999
|
|
_attr_native_step = 1
|
|
_attr_mode = NumberMode.BOX
|
|
|
|
def __init__(self, coordinator: NinstripCoordinator) -> None:
|
|
super().__init__(coordinator, "sacn_universe")
|
|
|
|
@property
|
|
def native_value(self) -> float | None:
|
|
value = self.sacn.get("universe")
|
|
return float(value) if isinstance(value, (int, float)) else None
|
|
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
await self.coordinator.async_set_sacn(universe=int(value))
|
|
|
|
|
|
class NinstripSacnStartSlot(NinstripSacnEntity, NumberEntity):
|
|
"""Der erste Slot - er gilt in JEDEM belegten Universe, nicht nur im ersten.
|
|
|
|
Anders als bei ninlight, wo eine Lampe ein einzelner Slotsatz ist: ein
|
|
Strip verteilt sich ueber mehrere Universes, und in jedem beginnt er an
|
|
derselben Stelle.
|
|
"""
|
|
|
|
_attr_entity_category = EntityCategory.CONFIG
|
|
_attr_translation_key = "sacn_start_slot"
|
|
_attr_native_min_value = 1
|
|
_attr_native_step = 1
|
|
_attr_mode = NumberMode.BOX
|
|
|
|
def __init__(self, coordinator: NinstripCoordinator) -> None:
|
|
super().__init__(coordinator, "sacn_start_slot")
|
|
|
|
@property
|
|
def native_max_value(self) -> float:
|
|
"""Der hoechste Startslot, hinter dem noch ein ganzes Pixel Platz hat.
|
|
|
|
Bei drei Kanaelen sind das 510. Ein festes 512 wuerde Werte anbieten,
|
|
bei denen kein einziges Pixel mehr ins Universe passt.
|
|
"""
|
|
kanaele = self.sacn.get("channels_per_pixel")
|
|
if not isinstance(kanaele, int) or not 1 <= kanaele <= 512:
|
|
kanaele = _KANAELE_VORGABE
|
|
return 513 - kanaele
|
|
|
|
@property
|
|
def native_value(self) -> float | None:
|
|
value = self.sacn.get("start_slot")
|
|
return float(value) if isinstance(value, (int, float)) else None
|
|
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
await self.coordinator.async_set_sacn(start_slot=int(value))
|