Modernize Sleepiq and add new entities (#66336)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
beb30a1ff1
commit
a367d2be40
22 changed files with 330 additions and 400 deletions
|
@ -850,8 +850,8 @@ homeassistant/components/sisyphus/* @jkeljo
|
|||
homeassistant/components/sky_hub/* @rogerselwyn
|
||||
homeassistant/components/slack/* @bachya
|
||||
tests/components/slack/* @bachya
|
||||
homeassistant/components/sleepiq/* @mfugate1
|
||||
tests/components/sleepiq/* @mfugate1
|
||||
homeassistant/components/sleepiq/* @mfugate1 @kbickar
|
||||
tests/components/sleepiq/* @mfugate1 @kbickar
|
||||
homeassistant/components/slide/* @ualex73
|
||||
homeassistant/components/sma/* @kellerza @rklomp
|
||||
tests/components/sma/* @kellerza @rklomp
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
"""Support for SleepIQ from SleepNumber."""
|
||||
import logging
|
||||
|
||||
from sleepyq import Sleepyq
|
||||
from asyncsleepiq import (
|
||||
AsyncSleepIQ,
|
||||
SleepIQAPIException,
|
||||
SleepIQLoginException,
|
||||
SleepIQTimeoutException,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
|
@ -15,6 +22,8 @@ from .coordinator import SleepIQDataUpdateCoordinator
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: {
|
||||
|
@ -43,18 +52,34 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up the SleepIQ config entry."""
|
||||
client = Sleepyq(entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD])
|
||||
try:
|
||||
await hass.async_add_executor_job(client.login)
|
||||
except ValueError:
|
||||
_LOGGER.error("SleepIQ login failed, double check your username and password")
|
||||
return False
|
||||
conf = entry.data
|
||||
email = conf[CONF_USERNAME]
|
||||
password = conf[CONF_PASSWORD]
|
||||
|
||||
coordinator = SleepIQDataUpdateCoordinator(
|
||||
hass,
|
||||
client=client,
|
||||
username=entry.data[CONF_USERNAME],
|
||||
)
|
||||
client_session = async_get_clientsession(hass)
|
||||
|
||||
gateway = AsyncSleepIQ(client_session=client_session)
|
||||
|
||||
try:
|
||||
await gateway.login(email, password)
|
||||
except SleepIQLoginException:
|
||||
_LOGGER.error("Could not authenticate with SleepIQ server")
|
||||
return False
|
||||
except SleepIQTimeoutException as err:
|
||||
raise ConfigEntryNotReady(
|
||||
str(err) or "Timed out during authentication"
|
||||
) from err
|
||||
|
||||
try:
|
||||
await gateway.init_beds()
|
||||
except SleepIQTimeoutException as err:
|
||||
raise ConfigEntryNotReady(
|
||||
str(err) or "Timed out during initialization"
|
||||
) from err
|
||||
except SleepIQAPIException as err:
|
||||
raise ConfigEntryNotReady(str(err) or "Error reading from SleepIQ API") from err
|
||||
|
||||
coordinator = SleepIQDataUpdateCoordinator(hass, gateway, email)
|
||||
|
||||
# Call the SleepIQ API to refresh data
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Support for SleepIQ sensors."""
|
||||
from asyncsleepiq import SleepIQBed, SleepIQSleeper
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
|
@ -6,8 +8,9 @@ from homeassistant.components.binary_sensor import (
|
|||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import BED, DOMAIN, ICON_EMPTY, ICON_OCCUPIED, IS_IN_BED, SIDES
|
||||
from .const import DOMAIN, ICON_EMPTY, ICON_OCCUPIED, IS_IN_BED
|
||||
from .coordinator import SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQSensor
|
||||
|
||||
|
@ -20,10 +23,9 @@ async def async_setup_entry(
|
|||
"""Set up the SleepIQ bed binary sensors."""
|
||||
coordinator: SleepIQDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
IsInBedBinarySensor(coordinator, bed_id, side)
|
||||
for side in SIDES
|
||||
for bed_id in coordinator.data
|
||||
if getattr(coordinator.data[bed_id][BED], side) is not None
|
||||
IsInBedBinarySensor(coordinator, bed, sleeper)
|
||||
for bed in coordinator.client.beds.values()
|
||||
for sleeper in bed.sleepers
|
||||
)
|
||||
|
||||
|
||||
|
@ -34,16 +36,15 @@ class IsInBedBinarySensor(SleepIQSensor, BinarySensorEntity):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SleepIQDataUpdateCoordinator,
|
||||
bed_id: str,
|
||||
side: str,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
bed: SleepIQBed,
|
||||
sleeper: SleepIQSleeper,
|
||||
) -> None:
|
||||
"""Initialize the SleepIQ bed side binary sensor."""
|
||||
super().__init__(coordinator, bed_id, side, IS_IN_BED)
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator, bed, sleeper, IS_IN_BED)
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update sensor attributes."""
|
||||
super()._async_update_attrs()
|
||||
self._attr_is_on = getattr(self.side_data, IS_IN_BED)
|
||||
self._attr_icon = ICON_OCCUPIED if self.is_on else ICON_EMPTY
|
||||
self._attr_is_on = self.sleeper.in_bed
|
||||
self._attr_icon = ICON_OCCUPIED if self.sleeper.in_bed else ICON_EMPTY
|
||||
|
|
|
@ -3,14 +3,16 @@ from __future__ import annotations
|
|||
|
||||
from typing import Any
|
||||
|
||||
from sleepyq import Sleepyq
|
||||
from asyncsleepiq import AsyncSleepIQ, SleepIQLoginException, SleepIQTimeoutException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN, SLEEPYQ_INVALID_CREDENTIALS_MESSAGE
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
class SleepIQFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
@ -41,19 +43,17 @@ class SleepIQFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
await self.async_set_unique_id(user_input[CONF_USERNAME].lower())
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
login_error = await self.hass.async_add_executor_job(
|
||||
try_connection, user_input
|
||||
)
|
||||
if not login_error:
|
||||
try:
|
||||
await try_connection(self.hass, user_input)
|
||||
except SleepIQLoginException:
|
||||
errors["base"] = "invalid_auth"
|
||||
except SleepIQTimeoutException:
|
||||
errors["base"] = "cannot_connect"
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_USERNAME], data=user_input
|
||||
)
|
||||
|
||||
if SLEEPYQ_INVALID_CREDENTIALS_MESSAGE in login_error:
|
||||
errors["base"] = "invalid_auth"
|
||||
else:
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
|
@ -72,14 +72,10 @@ class SleepIQFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
)
|
||||
|
||||
|
||||
def try_connection(user_input: dict[str, Any]) -> str:
|
||||
async def try_connection(hass: HomeAssistant, user_input: dict[str, Any]) -> None:
|
||||
"""Test if the given credentials can successfully login to SleepIQ."""
|
||||
|
||||
client = Sleepyq(user_input[CONF_USERNAME], user_input[CONF_PASSWORD])
|
||||
client_session = async_get_clientsession(hass)
|
||||
|
||||
try:
|
||||
client.login()
|
||||
except ValueError as error:
|
||||
return str(error)
|
||||
|
||||
return ""
|
||||
gateway = AsyncSleepIQ(client_session=client_session)
|
||||
await gateway.login(user_input[CONF_USERNAME], user_input[CONF_PASSWORD])
|
||||
|
|
|
@ -14,3 +14,6 @@ SENSOR_TYPES = {SLEEP_NUMBER: "SleepNumber", IS_IN_BED: "Is In Bed"}
|
|||
LEFT = "left"
|
||||
RIGHT = "right"
|
||||
SIDES = [LEFT, RIGHT]
|
||||
|
||||
SLEEPIQ_DATA = "sleepiq_data"
|
||||
SLEEPIQ_STATUS_COORDINATOR = "sleepiq_status"
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from sleepyq import Sleepyq
|
||||
from asyncsleepiq import AsyncSleepIQ
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import BED
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
UPDATE_INTERVAL = timedelta(seconds=60)
|
||||
|
@ -20,21 +18,15 @@ class SleepIQDataUpdateCoordinator(DataUpdateCoordinator[dict[str, dict]]):
|
|||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
client: Sleepyq,
|
||||
client: AsyncSleepIQ,
|
||||
username: str,
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
super().__init__(
|
||||
hass, _LOGGER, name=f"{username}@SleepIQ", update_interval=UPDATE_INTERVAL
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{username}@SleepIQ",
|
||||
update_method=client.fetch_bed_statuses,
|
||||
update_interval=UPDATE_INTERVAL,
|
||||
)
|
||||
self.client = client
|
||||
|
||||
async def _async_update_data(self) -> dict[str, dict]:
|
||||
return await self.hass.async_add_executor_job(self.update_data)
|
||||
|
||||
def update_data(self) -> dict[str, dict]:
|
||||
"""Get latest data from the client."""
|
||||
return {
|
||||
bed.bed_id: {BED: bed} for bed in self.client.beds_with_sleeper_status()
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
"""Entity for the SleepIQ integration."""
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from abc import abstractmethod
|
||||
|
||||
from .const import BED, ICON_OCCUPIED, SENSOR_TYPES
|
||||
from .coordinator import SleepIQDataUpdateCoordinator
|
||||
from asyncsleepiq import SleepIQBed, SleepIQSleeper
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
DataUpdateCoordinator,
|
||||
)
|
||||
|
||||
from .const import ICON_OCCUPIED, SENSOR_TYPES
|
||||
|
||||
|
||||
class SleepIQSensor(CoordinatorEntity):
|
||||
|
@ -13,22 +19,19 @@ class SleepIQSensor(CoordinatorEntity):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SleepIQDataUpdateCoordinator,
|
||||
bed_id: str,
|
||||
side: str,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
bed: SleepIQBed,
|
||||
sleeper: SleepIQSleeper,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the SleepIQ side entity."""
|
||||
super().__init__(coordinator)
|
||||
self.bed_id = bed_id
|
||||
self.side = side
|
||||
|
||||
self.bed = bed
|
||||
self.sleeper = sleeper
|
||||
self._async_update_attrs()
|
||||
|
||||
self._attr_name = f"SleepNumber {self.bed_data.name} {self.side_data.sleeper.first_name} {SENSOR_TYPES[name]}"
|
||||
self._attr_unique_id = (
|
||||
f"{self.bed_id}_{self.side_data.sleeper.first_name}_{name}"
|
||||
)
|
||||
self._attr_name = f"SleepNumber {bed.name} {sleeper.name} {SENSOR_TYPES[name]}"
|
||||
self._attr_unique_id = f"{bed.id}_{sleeper.name}_{name}"
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
|
@ -37,7 +40,6 @@ class SleepIQSensor(CoordinatorEntity):
|
|||
super()._handle_coordinator_update()
|
||||
|
||||
@callback
|
||||
@abstractmethod
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update sensor attributes."""
|
||||
self.bed_data = self.coordinator.data[self.bed_id][BED]
|
||||
self.side_data = getattr(self.bed_data, self.side)
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"name": "SleepIQ",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/sleepiq",
|
||||
"requirements": ["sleepyq==0.8.1"],
|
||||
"codeowners": ["@mfugate1"],
|
||||
"requirements": ["asyncsleepiq==1.0.0"],
|
||||
"codeowners": ["@mfugate1", "@kbickar"],
|
||||
"dhcp": [
|
||||
{"macaddress": "64DBA0*"}
|
||||
{
|
||||
"macaddress": "64DBA0*"
|
||||
}
|
||||
],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["sleepyq"]
|
||||
"loggers": ["asyncsleepiq"]
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
"""Support for SleepIQ sensors."""
|
||||
"""Support for SleepIQ Sensor."""
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncsleepiq import SleepIQBed, SleepIQSleeper
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import BED, DOMAIN, SIDES, SLEEP_NUMBER
|
||||
from .const import DOMAIN, SLEEP_NUMBER
|
||||
from .coordinator import SleepIQDataUpdateCoordinator
|
||||
from .entity import SleepIQSensor
|
||||
|
||||
|
@ -17,27 +22,27 @@ async def async_setup_entry(
|
|||
"""Set up the SleepIQ bed sensors."""
|
||||
coordinator: SleepIQDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SleepNumberSensor(coordinator, bed_id, side)
|
||||
for side in SIDES
|
||||
for bed_id in coordinator.data
|
||||
if getattr(coordinator.data[bed_id][BED], side) is not None
|
||||
SleepNumberSensorEntity(coordinator, bed, sleeper)
|
||||
for bed in coordinator.client.beds.values()
|
||||
for sleeper in bed.sleepers
|
||||
)
|
||||
|
||||
|
||||
class SleepNumberSensor(SleepIQSensor, SensorEntity):
|
||||
"""Implementation of a SleepIQ sensor."""
|
||||
class SleepNumberSensorEntity(SleepIQSensor, SensorEntity):
|
||||
"""Representation of an SleepIQ Entity with CoordinatorEntity."""
|
||||
|
||||
_attr_icon = "mdi:bed"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: SleepIQDataUpdateCoordinator,
|
||||
bed_id: str,
|
||||
side: str,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
bed: SleepIQBed,
|
||||
sleeper: SleepIQSleeper,
|
||||
) -> None:
|
||||
"""Initialize the SleepIQ sleep number sensor."""
|
||||
super().__init__(coordinator, bed_id, side, SLEEP_NUMBER)
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator, bed, sleeper, SLEEP_NUMBER)
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update sensor attributes."""
|
||||
super()._async_update_attrs()
|
||||
self._attr_native_value = self.side_data.sleep_number
|
||||
self._attr_native_value = self.sleeper.sleep_number
|
||||
|
|
|
@ -349,6 +349,9 @@ async-upnp-client==0.23.5
|
|||
# homeassistant.components.supla
|
||||
asyncpysupla==0.0.5
|
||||
|
||||
# homeassistant.components.sleepiq
|
||||
asyncsleepiq==1.0.0
|
||||
|
||||
# homeassistant.components.aten_pe
|
||||
atenpdu==0.3.2
|
||||
|
||||
|
@ -2201,9 +2204,6 @@ skybellpy==0.6.3
|
|||
# homeassistant.components.slack
|
||||
slackclient==2.5.0
|
||||
|
||||
# homeassistant.components.sleepiq
|
||||
sleepyq==0.8.1
|
||||
|
||||
# homeassistant.components.xmpp
|
||||
slixmpp==1.7.1
|
||||
|
||||
|
|
|
@ -254,6 +254,9 @@ arcam-fmj==0.12.0
|
|||
# homeassistant.components.yeelight
|
||||
async-upnp-client==0.23.5
|
||||
|
||||
# homeassistant.components.sleepiq
|
||||
asyncsleepiq==1.0.0
|
||||
|
||||
# homeassistant.components.aurora
|
||||
auroranoaa==0.0.2
|
||||
|
||||
|
@ -1354,9 +1357,6 @@ simplisafe-python==2022.02.1
|
|||
# homeassistant.components.slack
|
||||
slackclient==2.5.0
|
||||
|
||||
# homeassistant.components.sleepiq
|
||||
sleepyq==0.8.1
|
||||
|
||||
# homeassistant.components.smart_meter_texas
|
||||
smart-meter-texas==0.4.7
|
||||
|
||||
|
|
|
@ -1,75 +1,67 @@
|
|||
"""Common fixtures for sleepiq tests."""
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
"""Common methods for SleepIQ."""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from sleepyq import Bed, FamilyStatus, Sleeper
|
||||
|
||||
from homeassistant.components.sleepiq.const import DOMAIN
|
||||
from homeassistant.components.sleepiq import DOMAIN
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
def mock_beds(account_type):
|
||||
"""Mock sleepnumber bed data."""
|
||||
return [
|
||||
Bed(bed)
|
||||
for bed in json.loads(load_fixture(f"bed{account_type}.json", "sleepiq"))[
|
||||
"beds"
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def mock_sleepers():
|
||||
"""Mock sleeper data."""
|
||||
return [
|
||||
Sleeper(sleeper)
|
||||
for sleeper in json.loads(load_fixture("sleeper.json", "sleepiq"))["sleepers"]
|
||||
]
|
||||
|
||||
|
||||
def mock_bed_family_status(account_type):
|
||||
"""Mock family status data."""
|
||||
return [
|
||||
FamilyStatus(status)
|
||||
for status in json.loads(
|
||||
load_fixture(f"familystatus{account_type}.json", "sleepiq")
|
||||
)["beds"]
|
||||
]
|
||||
BED_ID = "123456"
|
||||
BED_NAME = "Test Bed"
|
||||
BED_NAME_LOWER = BED_NAME.lower().replace(" ", "_")
|
||||
SLEEPER_L_NAME = "SleeperL"
|
||||
SLEEPER_R_NAME = "Sleeper R"
|
||||
SLEEPER_L_NAME_LOWER = SLEEPER_L_NAME.lower().replace(" ", "_")
|
||||
SLEEPER_R_NAME_LOWER = SLEEPER_R_NAME.lower().replace(" ", "_")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_data():
|
||||
"""Provide configuration data for tests."""
|
||||
return {
|
||||
CONF_USERNAME: "username",
|
||||
CONF_PASSWORD: "password",
|
||||
}
|
||||
def mock_asyncsleepiq():
|
||||
"""Mock an AsyncSleepIQ object."""
|
||||
with patch("homeassistant.components.sleepiq.AsyncSleepIQ", autospec=True) as mock:
|
||||
client = mock.return_value
|
||||
bed = MagicMock()
|
||||
client.beds = {BED_ID: bed}
|
||||
bed.name = BED_NAME
|
||||
bed.id = BED_ID
|
||||
bed.mac_addr = "12:34:56:78:AB:CD"
|
||||
bed.model = "C10"
|
||||
bed.paused = False
|
||||
sleeper_l = MagicMock()
|
||||
sleeper_r = MagicMock()
|
||||
bed.sleepers = [sleeper_l, sleeper_r]
|
||||
|
||||
sleeper_l.side = "L"
|
||||
sleeper_l.name = SLEEPER_L_NAME
|
||||
sleeper_l.in_bed = True
|
||||
sleeper_l.sleep_number = 40
|
||||
|
||||
sleeper_r.side = "R"
|
||||
sleeper_r.name = SLEEPER_R_NAME
|
||||
sleeper_r.in_bed = False
|
||||
sleeper_r.sleep_number = 80
|
||||
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_entry(config_data):
|
||||
"""Create a mock config entry."""
|
||||
return MockConfigEntry(
|
||||
async def setup_platform(hass: HomeAssistant, platform) -> MockConfigEntry:
|
||||
"""Set up the SleepIQ platform."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=config_data,
|
||||
options={},
|
||||
data={
|
||||
CONF_USERNAME: "user@email.com",
|
||||
CONF_PASSWORD: "password",
|
||||
},
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
|
||||
@pytest.fixture(params=["-single", ""])
|
||||
async def setup_entry(hass, request, config_entry):
|
||||
"""Initialize the config entry."""
|
||||
with patch("sleepyq.Sleepyq.beds", return_value=mock_beds(request.param)), patch(
|
||||
"sleepyq.Sleepyq.sleepers", return_value=mock_sleepers()
|
||||
), patch(
|
||||
"sleepyq.Sleepyq.bed_family_status",
|
||||
return_value=mock_bed_family_status(request.param),
|
||||
), patch(
|
||||
"sleepyq.Sleepyq.login"
|
||||
):
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
if platform:
|
||||
with patch("homeassistant.components.sleepiq.PLATFORMS", [platform]):
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
return {"account_type": request.param, "mock_entry": config_entry}
|
||||
|
||||
return mock_entry
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"beds" : [
|
||||
{
|
||||
"dualSleep" : false,
|
||||
"base" : "FlexFit",
|
||||
"sku" : "AILE",
|
||||
"model" : "ILE",
|
||||
"size" : "KING",
|
||||
"isKidsBed" : false,
|
||||
"sleeperRightId" : "-80",
|
||||
"accountId" : "-32",
|
||||
"bedId" : "-31",
|
||||
"registrationDate" : "2016-07-22T14:00:58Z",
|
||||
"serial" : null,
|
||||
"reference" : "95000794555-1",
|
||||
"macAddress" : "CD13A384BA51",
|
||||
"version" : null,
|
||||
"purchaseDate" : "2016-06-22T00:00:00Z",
|
||||
"sleeperLeftId" : "0",
|
||||
"zipcode" : "12345",
|
||||
"returnRequestStatus" : 0,
|
||||
"name" : "ILE",
|
||||
"status" : 1,
|
||||
"timezone" : "US/Eastern"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"beds" : [
|
||||
{
|
||||
"dualSleep" : true,
|
||||
"base" : "FlexFit",
|
||||
"sku" : "AILE",
|
||||
"model" : "ILE",
|
||||
"size" : "KING",
|
||||
"isKidsBed" : false,
|
||||
"sleeperRightId" : "-80",
|
||||
"accountId" : "-32",
|
||||
"bedId" : "-31",
|
||||
"registrationDate" : "2016-07-22T14:00:58Z",
|
||||
"serial" : null,
|
||||
"reference" : "95000794555-1",
|
||||
"macAddress" : "CD13A384BA51",
|
||||
"version" : null,
|
||||
"purchaseDate" : "2016-06-22T00:00:00Z",
|
||||
"sleeperLeftId" : "-92",
|
||||
"zipcode" : "12345",
|
||||
"returnRequestStatus" : 0,
|
||||
"name" : "ILE",
|
||||
"status" : 1,
|
||||
"timezone" : "US/Eastern"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"beds" : [
|
||||
{
|
||||
"bedId" : "-31",
|
||||
"rightSide" : {
|
||||
"alertId" : 0,
|
||||
"lastLink" : "00:00:00",
|
||||
"isInBed" : true,
|
||||
"sleepNumber" : 40,
|
||||
"alertDetailedMessage" : "No Alert",
|
||||
"pressure" : -16
|
||||
},
|
||||
"status" : 1,
|
||||
"leftSide" : null
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"beds" : [
|
||||
{
|
||||
"bedId" : "-31",
|
||||
"rightSide" : {
|
||||
"alertId" : 0,
|
||||
"lastLink" : "00:00:00",
|
||||
"isInBed" : true,
|
||||
"sleepNumber" : 40,
|
||||
"alertDetailedMessage" : "No Alert",
|
||||
"pressure" : -16
|
||||
},
|
||||
"status" : 1,
|
||||
"leftSide" : {
|
||||
"alertId" : 0,
|
||||
"lastLink" : "00:00:00",
|
||||
"sleepNumber" : 80,
|
||||
"alertDetailedMessage" : "No Alert",
|
||||
"isInBed" : false,
|
||||
"pressure" : 2191
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"edpLoginStatus" : 200,
|
||||
"userId" : "-42",
|
||||
"registrationState" : 13,
|
||||
"key" : "0987",
|
||||
"edpLoginMessage" : "not used"
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
{
|
||||
"sleepers" : [
|
||||
{
|
||||
"timezone" : "US/Eastern",
|
||||
"firstName" : "Test1",
|
||||
"weight" : 150,
|
||||
"birthMonth" : 12,
|
||||
"birthYear" : "1990",
|
||||
"active" : true,
|
||||
"lastLogin" : "2016-08-26 21:43:27 CDT",
|
||||
"side" : 1,
|
||||
"accountId" : "-32",
|
||||
"height" : 60,
|
||||
"bedId" : "-31",
|
||||
"username" : "test1@example.com",
|
||||
"sleeperId" : "-80",
|
||||
"avatar" : "",
|
||||
"emailValidated" : true,
|
||||
"licenseVersion" : 6,
|
||||
"duration" : null,
|
||||
"email" : "test1@example.com",
|
||||
"isAccountOwner" : true,
|
||||
"sleepGoal" : 480,
|
||||
"zipCode" : "12345",
|
||||
"isChild" : false,
|
||||
"isMale" : true
|
||||
},
|
||||
{
|
||||
"email" : "test2@example.com",
|
||||
"duration" : null,
|
||||
"emailValidated" : true,
|
||||
"licenseVersion" : 5,
|
||||
"isChild" : false,
|
||||
"isMale" : false,
|
||||
"zipCode" : "12345",
|
||||
"isAccountOwner" : false,
|
||||
"sleepGoal" : 480,
|
||||
"side" : 0,
|
||||
"lastLogin" : "2016-07-17 15:37:30 CDT",
|
||||
"birthMonth" : 1,
|
||||
"birthYear" : "1991",
|
||||
"active" : true,
|
||||
"weight" : 151,
|
||||
"firstName" : "Test2",
|
||||
"timezone" : "US/Eastern",
|
||||
"avatar" : "",
|
||||
"username" : "test2@example.com",
|
||||
"sleeperId" : "-92",
|
||||
"bedId" : "-31",
|
||||
"height" : 65,
|
||||
"accountId" : "-32"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,34 +1,61 @@
|
|||
"""The tests for SleepIQ binary sensor platform."""
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_FRIENDLY_NAME, ATTR_ICON
|
||||
from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDeviceClass
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_ICON,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
)
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.components.sleepiq.conftest import (
|
||||
BED_ID,
|
||||
BED_NAME,
|
||||
BED_NAME_LOWER,
|
||||
SLEEPER_L_NAME,
|
||||
SLEEPER_L_NAME_LOWER,
|
||||
SLEEPER_R_NAME,
|
||||
SLEEPER_R_NAME_LOWER,
|
||||
setup_platform,
|
||||
)
|
||||
|
||||
async def test_binary_sensors(hass, setup_entry):
|
||||
|
||||
async def test_binary_sensors(hass, mock_asyncsleepiq):
|
||||
"""Test the SleepIQ binary sensors."""
|
||||
await setup_platform(hass, DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
state = hass.states.get("binary_sensor.sleepnumber_ile_test1_is_in_bed")
|
||||
assert state.state == "on"
|
||||
state = hass.states.get(
|
||||
f"binary_sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_L_NAME_LOWER}_is_in_bed"
|
||||
)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:bed"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.OCCUPANCY
|
||||
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "SleepNumber ILE Test1 Is In Bed"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== f"SleepNumber {BED_NAME} {SLEEPER_L_NAME} Is In Bed"
|
||||
)
|
||||
|
||||
entry = entity_registry.async_get("binary_sensor.sleepnumber_ile_test1_is_in_bed")
|
||||
assert entry
|
||||
assert entry.unique_id == "-31_Test1_is_in_bed"
|
||||
entity = entity_registry.async_get(
|
||||
f"binary_sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_L_NAME_LOWER}_is_in_bed"
|
||||
)
|
||||
assert entity
|
||||
assert entity.unique_id == f"{BED_ID}_{SLEEPER_L_NAME}_is_in_bed"
|
||||
|
||||
# If account type is set, only a single bed account was created and there will
|
||||
# not be a second entity
|
||||
if setup_entry["account_type"]:
|
||||
return
|
||||
|
||||
entry = entity_registry.async_get("binary_sensor.sleepnumber_ile_test2_is_in_bed")
|
||||
assert entry
|
||||
assert entry.unique_id == "-31_Test2_is_in_bed"
|
||||
|
||||
state = hass.states.get("binary_sensor.sleepnumber_ile_test2_is_in_bed")
|
||||
assert state.state == "off"
|
||||
state = hass.states.get(
|
||||
f"binary_sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_R_NAME_LOWER}_is_in_bed"
|
||||
)
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:bed-empty"
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.OCCUPANCY
|
||||
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "SleepNumber ILE Test2 Is In Bed"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== f"SleepNumber {BED_NAME} {SLEEPER_R_NAME} Is In Bed"
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(
|
||||
f"binary_sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_R_NAME_LOWER}_is_in_bed"
|
||||
)
|
||||
assert entity
|
||||
assert entity.unique_id == f"{BED_ID}_{SLEEPER_R_NAME}_is_in_bed"
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
"""Tests for the SleepIQ config flow."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from asyncsleepiq import SleepIQLoginException, SleepIQTimeoutException
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow, setup
|
||||
from homeassistant.components.sleepiq.const import (
|
||||
DOMAIN,
|
||||
SLEEPYQ_INVALID_CREDENTIALS_MESSAGE,
|
||||
)
|
||||
from homeassistant.components.sleepiq.const import DOMAIN
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
@ -17,7 +16,7 @@ SLEEPIQ_CONFIG = {
|
|||
|
||||
async def test_import(hass: HomeAssistant) -> None:
|
||||
"""Test that we can import a config entry."""
|
||||
with patch("sleepyq.Sleepyq.login"):
|
||||
with patch("asyncsleepiq.AsyncSleepIQ.login"):
|
||||
assert await setup.async_setup_component(hass, DOMAIN, {DOMAIN: SLEEPIQ_CONFIG})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -29,7 +28,7 @@ async def test_import(hass: HomeAssistant) -> None:
|
|||
|
||||
async def test_show_set_form(hass: HomeAssistant) -> None:
|
||||
"""Test that the setup form is served."""
|
||||
with patch("sleepyq.Sleepyq.login"):
|
||||
with patch("asyncsleepiq.AsyncSleepIQ.login"):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=None
|
||||
)
|
||||
|
@ -41,8 +40,8 @@ async def test_show_set_form(hass: HomeAssistant) -> None:
|
|||
async def test_login_invalid_auth(hass: HomeAssistant) -> None:
|
||||
"""Test we show user form with appropriate error on login failure."""
|
||||
with patch(
|
||||
"sleepyq.Sleepyq.login",
|
||||
side_effect=ValueError(SLEEPYQ_INVALID_CREDENTIALS_MESSAGE),
|
||||
"asyncsleepiq.AsyncSleepIQ.login",
|
||||
side_effect=SleepIQLoginException,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=SLEEPIQ_CONFIG
|
||||
|
@ -56,8 +55,8 @@ async def test_login_invalid_auth(hass: HomeAssistant) -> None:
|
|||
async def test_login_cannot_connect(hass: HomeAssistant) -> None:
|
||||
"""Test we show user form with appropriate error on login failure."""
|
||||
with patch(
|
||||
"sleepyq.Sleepyq.login",
|
||||
side_effect=ValueError("Unexpected response code"),
|
||||
"asyncsleepiq.AsyncSleepIQ.login",
|
||||
side_effect=SleepIQTimeoutException,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=SLEEPIQ_CONFIG
|
||||
|
@ -70,11 +69,23 @@ async def test_login_cannot_connect(hass: HomeAssistant) -> None:
|
|||
|
||||
async def test_success(hass: HomeAssistant) -> None:
|
||||
"""Test successful flow provides entry creation data."""
|
||||
with patch("sleepyq.Sleepyq.login"):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}, data=SLEEPIQ_CONFIG
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] == {}
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["data"][CONF_USERNAME] == SLEEPIQ_CONFIG[CONF_USERNAME]
|
||||
assert result["data"][CONF_PASSWORD] == SLEEPIQ_CONFIG[CONF_PASSWORD]
|
||||
with patch("asyncsleepiq.AsyncSleepIQ.login", return_value=True), patch(
|
||||
"homeassistant.components.sleepiq.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], SLEEPIQ_CONFIG
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["data"][CONF_USERNAME] == SLEEPIQ_CONFIG[CONF_USERNAME]
|
||||
assert result2["data"][CONF_PASSWORD] == SLEEPIQ_CONFIG[CONF_PASSWORD]
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"""Tests for the SleepIQ integration."""
|
||||
from unittest.mock import patch
|
||||
from asyncsleepiq import (
|
||||
SleepIQAPIException,
|
||||
SleepIQLoginException,
|
||||
SleepIQTimeoutException,
|
||||
)
|
||||
|
||||
from homeassistant.components.sleepiq.const import DOMAIN
|
||||
from homeassistant.components.sleepiq.coordinator import UPDATE_INTERVAL
|
||||
|
@ -8,16 +12,12 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
from tests.components.sleepiq.conftest import (
|
||||
mock_bed_family_status,
|
||||
mock_beds,
|
||||
mock_sleepers,
|
||||
)
|
||||
from tests.components.sleepiq.conftest import setup_platform
|
||||
|
||||
|
||||
async def test_unload_entry(hass: HomeAssistant, setup_entry) -> None:
|
||||
async def test_unload_entry(hass: HomeAssistant, mock_asyncsleepiq) -> None:
|
||||
"""Test unloading the SleepIQ entry."""
|
||||
entry = setup_entry["mock_entry"]
|
||||
entry = await setup_platform(hass, "sensor")
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -25,30 +25,42 @@ async def test_unload_entry(hass: HomeAssistant, setup_entry) -> None:
|
|||
assert not hass.data.get(DOMAIN)
|
||||
|
||||
|
||||
async def test_entry_setup_login_error(hass: HomeAssistant, config_entry) -> None:
|
||||
"""Test when sleepyq client is unable to login."""
|
||||
with patch("sleepyq.Sleepyq.login", side_effect=ValueError):
|
||||
config_entry.add_to_hass(hass)
|
||||
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
async def test_entry_setup_login_error(hass: HomeAssistant, mock_asyncsleepiq) -> None:
|
||||
"""Test when sleepiq client is unable to login."""
|
||||
mock_asyncsleepiq.login.side_effect = SleepIQLoginException
|
||||
entry = await setup_platform(hass, None)
|
||||
assert not await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
||||
|
||||
async def test_update_interval(hass: HomeAssistant, setup_entry) -> None:
|
||||
async def test_entry_setup_timeout_error(
|
||||
hass: HomeAssistant, mock_asyncsleepiq
|
||||
) -> None:
|
||||
"""Test when sleepiq client timeout."""
|
||||
mock_asyncsleepiq.login.side_effect = SleepIQTimeoutException
|
||||
entry = await setup_platform(hass, None)
|
||||
assert not await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
||||
|
||||
async def test_update_interval(hass: HomeAssistant, mock_asyncsleepiq) -> None:
|
||||
"""Test update interval."""
|
||||
with patch("sleepyq.Sleepyq.beds", return_value=mock_beds("")) as beds, patch(
|
||||
"sleepyq.Sleepyq.sleepers", return_value=mock_sleepers()
|
||||
) as sleepers, patch(
|
||||
"sleepyq.Sleepyq.bed_family_status",
|
||||
return_value=mock_bed_family_status(""),
|
||||
) as bed_family_status, patch(
|
||||
"sleepyq.Sleepyq.login", return_value=True
|
||||
):
|
||||
assert beds.call_count == 0
|
||||
assert sleepers.call_count == 0
|
||||
assert bed_family_status.call_count == 0
|
||||
await setup_platform(hass, "sensor")
|
||||
assert mock_asyncsleepiq.fetch_bed_statuses.call_count == 1
|
||||
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert beds.call_count == 1
|
||||
assert sleepers.call_count == 1
|
||||
assert bed_family_status.call_count == 1
|
||||
assert mock_asyncsleepiq.fetch_bed_statuses.call_count == 2
|
||||
|
||||
|
||||
async def test_api_error(hass: HomeAssistant, mock_asyncsleepiq) -> None:
|
||||
"""Test when sleepiq client is unable to login."""
|
||||
mock_asyncsleepiq.init_beds.side_effect = SleepIQAPIException
|
||||
entry = await setup_platform(hass, None)
|
||||
assert not await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
||||
|
||||
async def test_api_timeout(hass: HomeAssistant, mock_asyncsleepiq) -> None:
|
||||
"""Test when sleepiq client timeout."""
|
||||
mock_asyncsleepiq.init_beds.side_effect = SleepIQTimeoutException
|
||||
entry = await setup_platform(hass, None)
|
||||
assert not await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
|
|
@ -1,35 +1,53 @@
|
|||
"""The tests for SleepIQ sensor platform."""
|
||||
from homeassistant.components.sensor import DOMAIN
|
||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_ICON
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.components.sleepiq.conftest import (
|
||||
BED_ID,
|
||||
BED_NAME,
|
||||
BED_NAME_LOWER,
|
||||
SLEEPER_L_NAME,
|
||||
SLEEPER_L_NAME_LOWER,
|
||||
SLEEPER_R_NAME,
|
||||
SLEEPER_R_NAME_LOWER,
|
||||
setup_platform,
|
||||
)
|
||||
|
||||
async def test_sensors(hass, setup_entry):
|
||||
|
||||
async def test_sensors(hass, mock_asyncsleepiq):
|
||||
"""Test the SleepIQ binary sensors for a bed with two sides."""
|
||||
entry = await setup_platform(hass, DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
state = hass.states.get("sensor.sleepnumber_ile_test1_sleepnumber")
|
||||
state = hass.states.get(
|
||||
f"sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_L_NAME_LOWER}_sleepnumber"
|
||||
)
|
||||
assert state.state == "40"
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:bed"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME) == "SleepNumber ILE Test1 SleepNumber"
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== f"SleepNumber {BED_NAME} {SLEEPER_L_NAME} SleepNumber"
|
||||
)
|
||||
|
||||
entry = entity_registry.async_get("sensor.sleepnumber_ile_test1_sleepnumber")
|
||||
entry = entity_registry.async_get(
|
||||
f"sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_L_NAME_LOWER}_sleepnumber"
|
||||
)
|
||||
assert entry
|
||||
assert entry.unique_id == "-31_Test1_sleep_number"
|
||||
assert entry.unique_id == f"{BED_ID}_{SLEEPER_L_NAME}_sleep_number"
|
||||
|
||||
# If account type is set, only a single bed account was created and there will
|
||||
# not be a second entity
|
||||
if setup_entry["account_type"]:
|
||||
return
|
||||
|
||||
state = hass.states.get("sensor.sleepnumber_ile_test2_sleepnumber")
|
||||
state = hass.states.get(
|
||||
f"sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_R_NAME_LOWER}_sleepnumber"
|
||||
)
|
||||
assert state.state == "80"
|
||||
assert state.attributes.get(ATTR_ICON) == "mdi:bed"
|
||||
assert (
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME) == "SleepNumber ILE Test2 SleepNumber"
|
||||
state.attributes.get(ATTR_FRIENDLY_NAME)
|
||||
== f"SleepNumber {BED_NAME} {SLEEPER_R_NAME} SleepNumber"
|
||||
)
|
||||
|
||||
entry = entity_registry.async_get("sensor.sleepnumber_ile_test2_sleepnumber")
|
||||
entry = entity_registry.async_get(
|
||||
f"sensor.sleepnumber_{BED_NAME_LOWER}_{SLEEPER_R_NAME_LOWER}_sleepnumber"
|
||||
)
|
||||
assert entry
|
||||
assert entry.unique_id == "-31_Test2_sleep_number"
|
||||
assert entry.unique_id == f"{BED_ID}_{SLEEPER_R_NAME}_sleep_number"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue