bump
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
"""Support for SleepIQ SleepNumber firmness number entities."""
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, cast, override
|
||||
|
||||
from asyncsleepiq import (
|
||||
CoreTemps,
|
||||
FootWarmingTemps,
|
||||
SleepIQActuator,
|
||||
SleepIQBed,
|
||||
SleepIQCoreClimate,
|
||||
SleepIQFootWarmer,
|
||||
SleepIQSleeper,
|
||||
)
|
||||
|
||||
from homeassistant.components.number import (
|
||||
NumberDeviceClass,
|
||||
NumberEntity,
|
||||
NumberEntityDescription,
|
||||
)
|
||||
from homeassistant.const import UnitOfTime
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
ACTUATOR,
|
||||
CORE_CLIMATE_TIMER,
|
||||
ENTITY_TYPES,
|
||||
FIRMNESS,
|
||||
FOOT_WARMING_TIMER,
|
||||
ICON_OCCUPIED,
|
||||
)
|
||||
from .coordinator import SleepIQConfigEntry, SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQBedEntity, sleeper_for_side
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class SleepIQNumberEntityDescription(NumberEntityDescription):
|
||||
"""Class to describe a SleepIQ number entity."""
|
||||
|
||||
value_fn: Callable[[Any], float]
|
||||
set_value_fn: Callable[[Any, int], Coroutine[None, None, None]]
|
||||
get_name_fn: Callable[[SleepIQBed, Any], str]
|
||||
get_unique_id_fn: Callable[[SleepIQBed, Any], str]
|
||||
|
||||
|
||||
async def _async_set_firmness(sleeper: SleepIQSleeper, firmness: int) -> None:
|
||||
await sleeper.set_sleepnumber(firmness)
|
||||
|
||||
|
||||
async def _async_set_actuator_position(
|
||||
actuator: SleepIQActuator, position: int
|
||||
) -> None:
|
||||
await actuator.set_position(position)
|
||||
|
||||
|
||||
def _get_actuator_name(bed: SleepIQBed, actuator: SleepIQActuator) -> str:
|
||||
if actuator.side:
|
||||
return (
|
||||
"SleepNumber"
|
||||
f" {bed.name} {actuator.side_full}"
|
||||
f" {actuator.actuator_full}"
|
||||
f" {ENTITY_TYPES[ACTUATOR]}"
|
||||
)
|
||||
|
||||
return f"SleepNumber {bed.name} {actuator.actuator_full} {ENTITY_TYPES[ACTUATOR]}"
|
||||
|
||||
|
||||
def _get_actuator_unique_id(bed: SleepIQBed, actuator: SleepIQActuator) -> str:
|
||||
if actuator.side:
|
||||
return f"{bed.id}_{actuator.side.value}_{actuator.actuator}"
|
||||
|
||||
return f"{bed.id}_{actuator.actuator}"
|
||||
|
||||
|
||||
def _get_sleeper_name(bed: SleepIQBed, sleeper: SleepIQSleeper) -> str:
|
||||
return f"SleepNumber {bed.name} {sleeper.name} {ENTITY_TYPES[FIRMNESS]}"
|
||||
|
||||
|
||||
def _get_sleeper_unique_id(bed: SleepIQBed, sleeper: SleepIQSleeper) -> str:
|
||||
return f"{sleeper.sleeper_id}_{FIRMNESS}"
|
||||
|
||||
|
||||
async def _async_set_foot_warmer_time(
|
||||
foot_warmer: SleepIQFootWarmer, time: int
|
||||
) -> None:
|
||||
temperature = FootWarmingTemps(foot_warmer.temperature)
|
||||
if temperature != FootWarmingTemps.OFF:
|
||||
await foot_warmer.turn_on(temperature, time)
|
||||
|
||||
foot_warmer.timer = time
|
||||
|
||||
|
||||
def _get_foot_warming_name(bed: SleepIQBed, foot_warmer: SleepIQFootWarmer) -> str:
|
||||
sleeper = sleeper_for_side(bed, foot_warmer.side)
|
||||
return f"SleepNumber {bed.name} {sleeper.name} {ENTITY_TYPES[FOOT_WARMING_TIMER]}"
|
||||
|
||||
|
||||
def _get_foot_warming_unique_id(bed: SleepIQBed, foot_warmer: SleepIQFootWarmer) -> str:
|
||||
return f"{bed.id}_{foot_warmer.side.value}_{FOOT_WARMING_TIMER}"
|
||||
|
||||
|
||||
async def _async_set_core_climate_time(
|
||||
core_climate: SleepIQCoreClimate, time: int
|
||||
) -> None:
|
||||
temperature = CoreTemps(core_climate.temperature)
|
||||
if temperature != CoreTemps.OFF:
|
||||
await core_climate.turn_on(temperature, time)
|
||||
|
||||
core_climate.timer = time
|
||||
|
||||
|
||||
def _get_core_climate_name(bed: SleepIQBed, core_climate: SleepIQCoreClimate) -> str:
|
||||
sleeper = sleeper_for_side(bed, core_climate.side)
|
||||
return f"SleepNumber {bed.name} {sleeper.name} {ENTITY_TYPES[CORE_CLIMATE_TIMER]}"
|
||||
|
||||
|
||||
def _get_core_climate_unique_id(
|
||||
bed: SleepIQBed, core_climate: SleepIQCoreClimate
|
||||
) -> str:
|
||||
return f"{bed.id}_{core_climate.side.value}_{CORE_CLIMATE_TIMER}"
|
||||
|
||||
|
||||
NUMBER_DESCRIPTIONS: dict[str, SleepIQNumberEntityDescription] = {
|
||||
FIRMNESS: SleepIQNumberEntityDescription(
|
||||
key=FIRMNESS,
|
||||
native_min_value=5,
|
||||
native_max_value=100,
|
||||
native_step=5,
|
||||
name=ENTITY_TYPES[FIRMNESS],
|
||||
icon=ICON_OCCUPIED,
|
||||
value_fn=lambda sleeper: cast(float, sleeper.sleep_number),
|
||||
set_value_fn=_async_set_firmness,
|
||||
get_name_fn=_get_sleeper_name,
|
||||
get_unique_id_fn=_get_sleeper_unique_id,
|
||||
),
|
||||
ACTUATOR: SleepIQNumberEntityDescription(
|
||||
key=ACTUATOR,
|
||||
native_min_value=0,
|
||||
native_max_value=100,
|
||||
native_step=1,
|
||||
name=ENTITY_TYPES[ACTUATOR],
|
||||
icon=ICON_OCCUPIED,
|
||||
value_fn=lambda actuator: cast(float, actuator.position),
|
||||
set_value_fn=_async_set_actuator_position,
|
||||
get_name_fn=_get_actuator_name,
|
||||
get_unique_id_fn=_get_actuator_unique_id,
|
||||
),
|
||||
FOOT_WARMING_TIMER: SleepIQNumberEntityDescription(
|
||||
key=FOOT_WARMING_TIMER,
|
||||
native_min_value=30,
|
||||
native_max_value=360,
|
||||
native_step=30,
|
||||
name=ENTITY_TYPES[FOOT_WARMING_TIMER],
|
||||
icon="mdi:timer",
|
||||
value_fn=lambda foot_warmer: foot_warmer.timer,
|
||||
set_value_fn=_async_set_foot_warmer_time,
|
||||
get_name_fn=_get_foot_warming_name,
|
||||
get_unique_id_fn=_get_foot_warming_unique_id,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
device_class=NumberDeviceClass.DURATION,
|
||||
),
|
||||
CORE_CLIMATE_TIMER: SleepIQNumberEntityDescription(
|
||||
key=CORE_CLIMATE_TIMER,
|
||||
native_min_value=0,
|
||||
native_max_value=SleepIQCoreClimate.max_core_climate_time,
|
||||
native_step=30,
|
||||
name=ENTITY_TYPES[CORE_CLIMATE_TIMER],
|
||||
icon="mdi:timer",
|
||||
value_fn=lambda core_climate: core_climate.timer,
|
||||
set_value_fn=_async_set_core_climate_time,
|
||||
get_name_fn=_get_core_climate_name,
|
||||
get_unique_id_fn=_get_core_climate_unique_id,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
device_class=NumberDeviceClass.DURATION,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: SleepIQConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed sensors."""
|
||||
data = entry.runtime_data
|
||||
|
||||
entities: list[SleepIQNumberEntity] = []
|
||||
for bed in data.client.beds.values():
|
||||
entities.extend(
|
||||
SleepIQNumberEntity(
|
||||
data.data_coordinator,
|
||||
bed,
|
||||
sleeper,
|
||||
NUMBER_DESCRIPTIONS[FIRMNESS],
|
||||
)
|
||||
for sleeper in bed.sleepers
|
||||
)
|
||||
entities.extend(
|
||||
SleepIQNumberEntity(
|
||||
data.data_coordinator,
|
||||
bed,
|
||||
actuator,
|
||||
NUMBER_DESCRIPTIONS[ACTUATOR],
|
||||
)
|
||||
for actuator in bed.foundation.actuators
|
||||
)
|
||||
entities.extend(
|
||||
SleepIQNumberEntity(
|
||||
data.data_coordinator,
|
||||
bed,
|
||||
foot_warmer,
|
||||
NUMBER_DESCRIPTIONS[FOOT_WARMING_TIMER],
|
||||
)
|
||||
for foot_warmer in bed.foundation.foot_warmers
|
||||
)
|
||||
entities.extend(
|
||||
SleepIQNumberEntity(
|
||||
data.data_coordinator,
|
||||
bed,
|
||||
core_climate,
|
||||
NUMBER_DESCRIPTIONS[CORE_CLIMATE_TIMER],
|
||||
)
|
||||
for core_climate in bed.foundation.core_climates
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class SleepIQNumberEntity(SleepIQBedEntity[SleepIQDataUpdateCoordinator], NumberEntity):
|
||||
"""Representation of a SleepIQ number entity."""
|
||||
|
||||
entity_description: SleepIQNumberEntityDescription
|
||||
_attr_icon = "mdi:bed"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SleepIQDataUpdateCoordinator,
|
||||
bed: SleepIQBed,
|
||||
device: Any,
|
||||
description: SleepIQNumberEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the number."""
|
||||
self.entity_description = description
|
||||
self.device = device
|
||||
|
||||
self._attr_name = description.get_name_fn(bed, device)
|
||||
self._attr_unique_id = description.get_unique_id_fn(bed, device)
|
||||
if description.icon:
|
||||
self._attr_icon = description.icon
|
||||
|
||||
super().__init__(coordinator, bed)
|
||||
|
||||
@callback
|
||||
@override
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update number attributes."""
|
||||
self._attr_native_value = float(self.entity_description.value_fn(self.device))
|
||||
|
||||
@override
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set the number value."""
|
||||
await self.entity_description.set_value_fn(self.device, int(value))
|
||||
self._attr_native_value = value
|
||||
self.async_write_ha_state()
|
||||
Reference in New Issue
Block a user