mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Add sleep health metrics to SleepIQ integration (#163403)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ from .coordinator import (
|
||||
SleepIQData,
|
||||
SleepIQDataUpdateCoordinator,
|
||||
SleepIQPauseUpdateCoordinator,
|
||||
SleepIQSleepDataCoordinator,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -96,14 +97,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
coordinator = SleepIQDataUpdateCoordinator(hass, entry, gateway)
|
||||
pause_coordinator = SleepIQPauseUpdateCoordinator(hass, entry, gateway)
|
||||
sleep_data_coordinator = SleepIQSleepDataCoordinator(hass, entry, gateway)
|
||||
|
||||
# Call the SleepIQ API to refresh data
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
await pause_coordinator.async_config_entry_first_refresh()
|
||||
await sleep_data_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = SleepIQData(
|
||||
data_coordinator=coordinator,
|
||||
pause_coordinator=pause_coordinator,
|
||||
sleep_data_coordinator=sleep_data_coordinator,
|
||||
client=gateway,
|
||||
)
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ PRESSURE = "pressure"
|
||||
SLEEP_NUMBER = "sleep_number"
|
||||
FOOT_WARMING_TIMER = "foot_warming_timer"
|
||||
FOOT_WARMER = "foot_warmer"
|
||||
SLEEP_SCORE = "sleep_score"
|
||||
SLEEP_DURATION = "sleep_duration"
|
||||
HEART_RATE = "heart_rate"
|
||||
RESPIRATORY_RATE = "respiratory_rate"
|
||||
HRV = "hrv"
|
||||
ENTITY_TYPES = {
|
||||
ACTUATOR: "Position",
|
||||
CORE_CLIMATE_TIMER: "Core Climate Timer",
|
||||
@@ -25,6 +30,11 @@ ENTITY_TYPES = {
|
||||
SLEEP_NUMBER: "SleepNumber",
|
||||
FOOT_WARMING_TIMER: "Foot Warming Timer",
|
||||
FOOT_WARMER: "Foot Warmer",
|
||||
SLEEP_SCORE: "Sleep Score",
|
||||
SLEEP_DURATION: "Sleep Duration",
|
||||
HEART_RATE: "Heart Rate Average",
|
||||
RESPIRATORY_RATE: "Respiratory Rate Average",
|
||||
HRV: "Heart Rate Variability",
|
||||
}
|
||||
|
||||
LEFT = "left"
|
||||
|
||||
@@ -5,17 +5,18 @@ from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from asyncsleepiq import AsyncSleepIQ
|
||||
from asyncsleepiq import AsyncSleepIQ, SleepIQAPIException, SleepIQTimeoutException
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
UPDATE_INTERVAL = timedelta(seconds=60)
|
||||
LONGER_UPDATE_INTERVAL = timedelta(minutes=5)
|
||||
SLEEP_DATA_UPDATE_INTERVAL = timedelta(hours=1) # Sleep data doesn't change frequently
|
||||
|
||||
|
||||
class SleepIQDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
@@ -74,10 +75,48 @@ class SleepIQPauseUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
)
|
||||
|
||||
|
||||
class SleepIQSleepDataCoordinator(DataUpdateCoordinator[None]):
|
||||
"""SleepIQ sleep health data coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
client: AsyncSleepIQ,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
config_entry=config_entry,
|
||||
name=f"{config_entry.data[CONF_USERNAME]}@SleepIQSleepData",
|
||||
update_interval=SLEEP_DATA_UPDATE_INTERVAL,
|
||||
)
|
||||
self.client = client
|
||||
|
||||
async def _async_update_data(self) -> None:
|
||||
"""Fetch sleep health data from API via asyncsleepiq library."""
|
||||
try:
|
||||
await asyncio.gather(
|
||||
*[
|
||||
sleeper.fetch_sleep_data()
|
||||
for bed in self.client.beds.values()
|
||||
for sleeper in bed.sleepers
|
||||
]
|
||||
)
|
||||
except SleepIQTimeoutException as err:
|
||||
raise UpdateFailed(f"Timed out fetching SleepIQ sleep data: {err}") from err
|
||||
except SleepIQAPIException as err:
|
||||
raise UpdateFailed(f"Failed to fetch SleepIQ sleep data: {err}") from err
|
||||
|
||||
|
||||
@dataclass
|
||||
class SleepIQData:
|
||||
"""Data for the sleepiq integration."""
|
||||
|
||||
data_coordinator: SleepIQDataUpdateCoordinator
|
||||
pause_coordinator: SleepIQPauseUpdateCoordinator
|
||||
sleep_data_coordinator: SleepIQSleepDataCoordinator
|
||||
client: AsyncSleepIQ
|
||||
|
||||
@@ -11,9 +11,17 @@ from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import ENTITY_TYPES, ICON_OCCUPIED
|
||||
from .coordinator import SleepIQDataUpdateCoordinator, SleepIQPauseUpdateCoordinator
|
||||
from .coordinator import (
|
||||
SleepIQDataUpdateCoordinator,
|
||||
SleepIQPauseUpdateCoordinator,
|
||||
SleepIQSleepDataCoordinator,
|
||||
)
|
||||
|
||||
type _DataCoordinatorType = SleepIQDataUpdateCoordinator | SleepIQPauseUpdateCoordinator
|
||||
type _DataCoordinatorType = (
|
||||
SleepIQDataUpdateCoordinator
|
||||
| SleepIQPauseUpdateCoordinator
|
||||
| SleepIQSleepDataCoordinator
|
||||
)
|
||||
|
||||
|
||||
def device_from_bed(bed: SleepIQBed) -> DeviceInfo:
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"heart_rate_avg": {
|
||||
"default": "mdi:heart-pulse"
|
||||
},
|
||||
"hrv": {
|
||||
"default": "mdi:heart-flash"
|
||||
},
|
||||
"respiratory_rate_avg": {
|
||||
"default": "mdi:lungs"
|
||||
},
|
||||
"sleep_duration": {
|
||||
"default": "mdi:sleep"
|
||||
},
|
||||
"sleep_score": {
|
||||
"default": "mdi:sleep"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,31 @@ from dataclasses import dataclass
|
||||
from asyncsleepiq import SleepIQBed, SleepIQSleeper
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import UnitOfTime
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, PRESSURE, SLEEP_NUMBER
|
||||
from .coordinator import SleepIQData, SleepIQDataUpdateCoordinator
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
HEART_RATE,
|
||||
HRV,
|
||||
PRESSURE,
|
||||
RESPIRATORY_RATE,
|
||||
SLEEP_DURATION,
|
||||
SLEEP_NUMBER,
|
||||
SLEEP_SCORE,
|
||||
)
|
||||
from .coordinator import (
|
||||
SleepIQData,
|
||||
SleepIQDataUpdateCoordinator,
|
||||
SleepIQSleepDataCoordinator,
|
||||
)
|
||||
from .entity import SleepIQSleeperEntity
|
||||
|
||||
|
||||
@@ -28,7 +43,7 @@ class SleepIQSensorEntityDescription(SensorEntityDescription):
|
||||
value_fn: Callable[[SleepIQSleeper], float | int | None]
|
||||
|
||||
|
||||
SENSORS: tuple[SleepIQSensorEntityDescription, ...] = (
|
||||
BED_SENSORS: tuple[SleepIQSensorEntityDescription, ...] = (
|
||||
SleepIQSensorEntityDescription(
|
||||
key=PRESSURE,
|
||||
translation_key="pressure",
|
||||
@@ -43,6 +58,57 @@ SENSORS: tuple[SleepIQSensorEntityDescription, ...] = (
|
||||
),
|
||||
)
|
||||
|
||||
SLEEP_HEALTH_SENSORS: tuple[SleepIQSensorEntityDescription, ...] = (
|
||||
SleepIQSensorEntityDescription(
|
||||
key=SLEEP_SCORE,
|
||||
translation_key="sleep_score",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="score",
|
||||
value_fn=lambda sleeper: (
|
||||
sleeper.sleep_data.sleep_score if sleeper.sleep_data else None
|
||||
),
|
||||
),
|
||||
SleepIQSensorEntityDescription(
|
||||
key=SLEEP_DURATION,
|
||||
translation_key="sleep_duration",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=UnitOfTime.HOURS,
|
||||
suggested_display_precision=1,
|
||||
value_fn=lambda sleeper: (
|
||||
round(sleeper.sleep_data.duration / 3600, 1)
|
||||
if sleeper.sleep_data and sleeper.sleep_data.duration
|
||||
else None
|
||||
),
|
||||
),
|
||||
SleepIQSensorEntityDescription(
|
||||
key=HEART_RATE,
|
||||
translation_key="heart_rate_avg",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="bpm",
|
||||
value_fn=lambda sleeper: (
|
||||
sleeper.sleep_data.heart_rate if sleeper.sleep_data else None
|
||||
),
|
||||
),
|
||||
SleepIQSensorEntityDescription(
|
||||
key=RESPIRATORY_RATE,
|
||||
translation_key="respiratory_rate_avg",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement="brpm",
|
||||
value_fn=lambda sleeper: (
|
||||
sleeper.sleep_data.respiratory_rate if sleeper.sleep_data else None
|
||||
),
|
||||
),
|
||||
SleepIQSensorEntityDescription(
|
||||
key=HRV,
|
||||
translation_key="hrv",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=UnitOfTime.MILLISECONDS,
|
||||
value_fn=lambda sleeper: sleeper.sleep_data.hrv if sleeper.sleep_data else None,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -51,16 +117,29 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed sensors."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
|
||||
entities: list[SensorEntity] = []
|
||||
|
||||
entities.extend(
|
||||
SleepIQSensorEntity(data.data_coordinator, bed, sleeper, description)
|
||||
for bed in data.client.beds.values()
|
||||
for sleeper in bed.sleepers
|
||||
for description in SENSORS
|
||||
for description in BED_SENSORS
|
||||
)
|
||||
|
||||
entities.extend(
|
||||
SleepIQSensorEntity(data.sleep_data_coordinator, bed, sleeper, description)
|
||||
for bed in data.client.beds.values()
|
||||
for sleeper in bed.sleepers
|
||||
for description in SLEEP_HEALTH_SENSORS
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class SleepIQSensorEntity(
|
||||
SleepIQSleeperEntity[SleepIQDataUpdateCoordinator], SensorEntity
|
||||
SleepIQSleeperEntity[SleepIQDataUpdateCoordinator | SleepIQSleepDataCoordinator],
|
||||
SensorEntity,
|
||||
):
|
||||
"""Representation of a SleepIQ sensor."""
|
||||
|
||||
@@ -68,7 +147,7 @@ class SleepIQSensorEntity(
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SleepIQDataUpdateCoordinator,
|
||||
coordinator: SleepIQDataUpdateCoordinator | SleepIQSleepDataCoordinator,
|
||||
bed: SleepIQBed,
|
||||
sleeper: SleepIQSleeper,
|
||||
description: SleepIQSensorEntityDescription,
|
||||
|
||||
@@ -10,6 +10,7 @@ from asyncsleepiq import (
|
||||
CoreTemps,
|
||||
FootWarmingTemps,
|
||||
Side,
|
||||
SleepData,
|
||||
SleepIQActuator,
|
||||
SleepIQBed,
|
||||
SleepIQCoreClimate,
|
||||
@@ -76,6 +77,13 @@ def mock_bed() -> MagicMock:
|
||||
sleeper_l.sleep_number = 40
|
||||
sleeper_l.pressure = 1000
|
||||
sleeper_l.sleeper_id = SLEEPER_L_ID
|
||||
sleeper_l.sleep_data = SleepData(
|
||||
duration=28800, # 8 hours in seconds
|
||||
sleep_score=85,
|
||||
heart_rate=60,
|
||||
respiratory_rate=14,
|
||||
hrv=68,
|
||||
)
|
||||
|
||||
sleeper_r.side = Side.RIGHT
|
||||
sleeper_r.name = SLEEPER_R_NAME
|
||||
@@ -83,6 +91,13 @@ def mock_bed() -> MagicMock:
|
||||
sleeper_r.sleep_number = 80
|
||||
sleeper_r.pressure = 1400
|
||||
sleeper_r.sleeper_id = SLEEPER_R_ID
|
||||
sleeper_r.sleep_data = SleepData(
|
||||
duration=25200, # 7 hours in seconds
|
||||
sleep_score=78,
|
||||
heart_rate=65,
|
||||
respiratory_rate=15,
|
||||
hrv=72,
|
||||
)
|
||||
|
||||
bed.foundation = create_autospec(SleepIQFoundation)
|
||||
light_1 = create_autospec(SleepIQLight)
|
||||
|
||||
@@ -1,4 +1,116 @@
|
||||
# serializer version: 1
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_heart_rate_average-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_heart_rate_average',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed Sleeper R Heart Rate Average',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed Sleeper R Heart Rate Average',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'heart_rate_avg',
|
||||
'unique_id': '43219_heart_rate',
|
||||
'unit_of_measurement': 'bpm',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_heart_rate_average-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'SleepNumber Test Bed Sleeper R Heart Rate Average',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'bpm',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_heart_rate_average',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '65',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_heart_rate_variability-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_heart_rate_variability',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed Sleeper R Heart Rate Variability',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed Sleeper R Heart Rate Variability',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'hrv',
|
||||
'unique_id': '43219_hrv',
|
||||
'unit_of_measurement': <UnitOfTime.MILLISECONDS: 'ms'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_heart_rate_variability-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'SleepNumber Test Bed Sleeper R Heart Rate Variability',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTime.MILLISECONDS: 'ms'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_heart_rate_variability',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '72',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_pressure-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -52,6 +164,172 @@
|
||||
'state': '1400',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_respiratory_rate_average-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_respiratory_rate_average',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed Sleeper R Respiratory Rate Average',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed Sleeper R Respiratory Rate Average',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'respiratory_rate_avg',
|
||||
'unique_id': '43219_respiratory_rate',
|
||||
'unit_of_measurement': 'brpm',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_respiratory_rate_average-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'SleepNumber Test Bed Sleeper R Respiratory Rate Average',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'brpm',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_respiratory_rate_average',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '15',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_sleep_duration-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_sleep_duration',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed Sleeper R Sleep Duration',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed Sleeper R Sleep Duration',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sleep_duration',
|
||||
'unique_id': '43219_sleep_duration',
|
||||
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_sleep_duration-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'SleepNumber Test Bed Sleeper R Sleep Duration',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_sleep_duration',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '7.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_sleep_score-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_sleep_score',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed Sleeper R Sleep Score',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed Sleeper R Sleep Score',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sleep_score',
|
||||
'unique_id': '43219_sleep_score',
|
||||
'unit_of_measurement': 'score',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_sleep_score-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'SleepNumber Test Bed Sleeper R Sleep Score',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'score',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeper_r_sleep_score',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '78',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeper_r_sleepnumber-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -105,6 +383,118 @@
|
||||
'state': '80',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_heart_rate_average-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_heart_rate_average',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed SleeperL Heart Rate Average',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed SleeperL Heart Rate Average',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'heart_rate_avg',
|
||||
'unique_id': '98765_heart_rate',
|
||||
'unit_of_measurement': 'bpm',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_heart_rate_average-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'SleepNumber Test Bed SleeperL Heart Rate Average',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'bpm',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_heart_rate_average',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '60',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_heart_rate_variability-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_heart_rate_variability',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed SleeperL Heart Rate Variability',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed SleeperL Heart Rate Variability',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'hrv',
|
||||
'unique_id': '98765_hrv',
|
||||
'unit_of_measurement': <UnitOfTime.MILLISECONDS: 'ms'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_heart_rate_variability-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'SleepNumber Test Bed SleeperL Heart Rate Variability',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTime.MILLISECONDS: 'ms'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_heart_rate_variability',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '68',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_pressure-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -158,6 +548,172 @@
|
||||
'state': '1000',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_respiratory_rate_average-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_respiratory_rate_average',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed SleeperL Respiratory Rate Average',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed SleeperL Respiratory Rate Average',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'respiratory_rate_avg',
|
||||
'unique_id': '98765_respiratory_rate',
|
||||
'unit_of_measurement': 'brpm',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_respiratory_rate_average-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'SleepNumber Test Bed SleeperL Respiratory Rate Average',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'brpm',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_respiratory_rate_average',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '14',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_sleep_duration-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_sleep_duration',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed SleeperL Sleep Duration',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed SleeperL Sleep Duration',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sleep_duration',
|
||||
'unique_id': '98765_sleep_duration',
|
||||
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_sleep_duration-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'SleepNumber Test Bed SleeperL Sleep Duration',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTime.HOURS: 'h'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_sleep_duration',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '8.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_sleep_score-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_sleep_score',
|
||||
'has_entity_name': False,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'SleepNumber Test Bed SleeperL Sleep Score',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:bed',
|
||||
'original_name': 'SleepNumber Test Bed SleeperL Sleep Score',
|
||||
'platform': 'sleepiq',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sleep_score',
|
||||
'unique_id': '98765_sleep_score',
|
||||
'unit_of_measurement': 'score',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_sleep_score-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'SleepNumber Test Bed SleeperL Sleep Score',
|
||||
'icon': 'mdi:bed',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'score',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sleepnumber_test_bed_sleeperl_sleep_score',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '85',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.sleepnumber_test_bed_sleeperl_sleepnumber-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
Reference in New Issue
Block a user