mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add light entity to SleepIQ (#67363)
This commit is contained in:
parent
cfed1ff799
commit
423c14e2a1
@ -26,7 +26,13 @@ from .coordinator import (
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR, Platform.SWITCH]
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.BUTTON,
|
||||
Platform.LIGHT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN, ICON_EMPTY, ICON_OCCUPIED, IS_IN_BED
|
||||
from .coordinator import SleepIQData
|
||||
from .entity import SleepIQSensor
|
||||
from .entity import SleepIQSleeperEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@ -29,7 +29,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class IsInBedBinarySensor(SleepIQSensor, BinarySensorEntity):
|
||||
class IsInBedBinarySensor(SleepIQSleeperEntity, BinarySensorEntity):
|
||||
"""Implementation of a SleepIQ presence sensor."""
|
||||
|
||||
_attr_device_class = BinarySensorDeviceClass.OCCUPANCY
|
||||
|
@ -34,7 +34,10 @@ class SleepIQDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
self.client = client
|
||||
|
||||
async def _async_update_data(self) -> None:
|
||||
await self.client.fetch_bed_statuses()
|
||||
tasks = [self.client.fetch_bed_statuses()] + [
|
||||
bed.foundation.update_lights() for bed in self.client.beds.values()
|
||||
]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
|
||||
class SleepIQPauseUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
|
@ -33,7 +33,7 @@ class SleepIQEntity(Entity):
|
||||
self._attr_device_info = device_from_bed(bed)
|
||||
|
||||
|
||||
class SleepIQSensor(CoordinatorEntity):
|
||||
class SleepIQBedEntity(CoordinatorEntity):
|
||||
"""Implementation of a SleepIQ sensor."""
|
||||
|
||||
_attr_icon = ICON_OCCUPIED
|
||||
@ -42,17 +42,11 @@ class SleepIQSensor(CoordinatorEntity):
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
bed: SleepIQBed,
|
||||
sleeper: SleepIQSleeper,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the SleepIQ sensor entity."""
|
||||
super().__init__(coordinator)
|
||||
self.sleeper = sleeper
|
||||
self.bed = bed
|
||||
self._attr_device_info = device_from_bed(bed)
|
||||
|
||||
self._attr_name = f"SleepNumber {bed.name} {sleeper.name} {SENSOR_TYPES[name]}"
|
||||
self._attr_unique_id = f"{bed.id}_{sleeper.name}_{name}"
|
||||
self._async_update_attrs()
|
||||
|
||||
@callback
|
||||
@ -67,7 +61,7 @@ class SleepIQSensor(CoordinatorEntity):
|
||||
"""Update sensor attributes."""
|
||||
|
||||
|
||||
class SleepIQBedCoordinator(CoordinatorEntity):
|
||||
class SleepIQSleeperEntity(SleepIQBedEntity):
|
||||
"""Implementation of a SleepIQ sensor."""
|
||||
|
||||
_attr_icon = ICON_OCCUPIED
|
||||
@ -76,8 +70,12 @@ class SleepIQBedCoordinator(CoordinatorEntity):
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
bed: SleepIQBed,
|
||||
sleeper: SleepIQSleeper,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize the SleepIQ sensor entity."""
|
||||
super().__init__(coordinator)
|
||||
self.bed = bed
|
||||
self._attr_device_info = device_from_bed(bed)
|
||||
self.sleeper = sleeper
|
||||
super().__init__(coordinator, bed)
|
||||
|
||||
self._attr_name = f"SleepNumber {bed.name} {sleeper.name} {SENSOR_TYPES[name]}"
|
||||
self._attr_unique_id = f"{bed.id}_{sleeper.name}_{name}"
|
||||
|
59
homeassistant/components/sleepiq/light.py
Normal file
59
homeassistant/components/sleepiq/light.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""Support for SleepIQ outlet lights."""
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from asyncsleepiq import SleepIQBed, SleepIQLight
|
||||
|
||||
from homeassistant.components.light import LightEntity
|
||||
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 DOMAIN
|
||||
from .coordinator import SleepIQData
|
||||
from .entity import SleepIQBedEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the SleepIQ bed lights."""
|
||||
data: SleepIQData = hass.data[DOMAIN][entry.entry_id]
|
||||
async_add_entities(
|
||||
SleepIQLightEntity(data.data_coordinator, bed, light)
|
||||
for bed in data.client.beds.values()
|
||||
for light in bed.foundation.lights
|
||||
)
|
||||
|
||||
|
||||
class SleepIQLightEntity(SleepIQBedEntity, LightEntity):
|
||||
"""Representation of a light."""
|
||||
|
||||
def __init__(
|
||||
self, coordinator: DataUpdateCoordinator, bed: SleepIQBed, light: SleepIQLight
|
||||
) -> None:
|
||||
"""Initialize the light."""
|
||||
self.light = light
|
||||
super().__init__(coordinator, bed)
|
||||
self._attr_name = f"SleepNumber {bed.name} Light {light.outlet_id}"
|
||||
self._attr_unique_id = f"{bed.id}-light-{light.outlet_id}"
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on light."""
|
||||
await self.light.turn_on()
|
||||
self._handle_coordinator_update()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off light."""
|
||||
await self.light.turn_off()
|
||||
self._handle_coordinator_update()
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update light attributes."""
|
||||
self._attr_is_on = self.light.is_on
|
@ -11,7 +11,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN, SLEEP_NUMBER
|
||||
from .coordinator import SleepIQData
|
||||
from .entity import SleepIQSensor
|
||||
from .entity import SleepIQSleeperEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@ -28,7 +28,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class SleepNumberSensorEntity(SleepIQSensor, SensorEntity):
|
||||
class SleepNumberSensorEntity(SleepIQSleeperEntity, SensorEntity):
|
||||
"""Representation of an SleepIQ Entity with CoordinatorEntity."""
|
||||
|
||||
_attr_icon = "mdi:bed"
|
||||
|
@ -7,12 +7,12 @@ from asyncsleepiq import SleepIQBed
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import SleepIQData, SleepIQPauseUpdateCoordinator
|
||||
from .entity import SleepIQBedCoordinator
|
||||
from .entity import SleepIQBedEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@ -28,7 +28,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class SleepNumberPrivateSwitch(SleepIQBedCoordinator, SwitchEntity):
|
||||
class SleepNumberPrivateSwitch(SleepIQBedEntity, SwitchEntity):
|
||||
"""Representation of SleepIQ privacy mode."""
|
||||
|
||||
def __init__(
|
||||
@ -39,15 +39,17 @@ class SleepNumberPrivateSwitch(SleepIQBedCoordinator, SwitchEntity):
|
||||
self._attr_name = f"SleepNumber {bed.name} Pause Mode"
|
||||
self._attr_unique_id = f"{bed.id}-pause-mode"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return whether the switch is on or off."""
|
||||
return bool(self.bed.paused)
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn on switch."""
|
||||
await self.bed.set_pause_mode(True)
|
||||
self._handle_coordinator_update()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn off switch."""
|
||||
await self.bed.set_pause_mode(False)
|
||||
self._handle_coordinator_update()
|
||||
|
||||
@callback
|
||||
def _async_update_attrs(self) -> None:
|
||||
"""Update switch attributes."""
|
||||
self._attr_is_on = self.bed.paused
|
||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from unittest.mock import create_autospec, patch
|
||||
|
||||
from asyncsleepiq import SleepIQBed, SleepIQSleeper
|
||||
from asyncsleepiq import SleepIQBed, SleepIQFoundation, SleepIQLight, SleepIQSleeper
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.sleepiq import DOMAIN
|
||||
@ -54,6 +54,15 @@ def mock_asyncsleepiq():
|
||||
sleeper_r.in_bed = False
|
||||
sleeper_r.sleep_number = 80
|
||||
|
||||
bed.foundation = create_autospec(SleepIQFoundation)
|
||||
light_1 = create_autospec(SleepIQLight)
|
||||
light_1.outlet_id = 1
|
||||
light_1.is_on = False
|
||||
light_2 = create_autospec(SleepIQLight)
|
||||
light_2.outlet_id = 2
|
||||
light_2.is_on = False
|
||||
bed.foundation.lights = [light_1, light_2]
|
||||
|
||||
yield client
|
||||
|
||||
|
||||
|
@ -118,6 +118,9 @@ async def test_reauth_password(hass):
|
||||
with patch(
|
||||
"homeassistant.components.sleepiq.config_flow.AsyncSleepIQ.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.sleepiq.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
73
tests/components/sleepiq/test_light.py
Normal file
73
tests/components/sleepiq/test_light.py
Normal file
@ -0,0 +1,73 @@
|
||||
"""The tests for SleepIQ light platform."""
|
||||
from homeassistant.components.light import DOMAIN
|
||||
from homeassistant.components.sleepiq.coordinator import LONGER_UPDATE_INTERVAL
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
from tests.components.sleepiq.conftest import (
|
||||
BED_ID,
|
||||
BED_NAME,
|
||||
BED_NAME_LOWER,
|
||||
setup_platform,
|
||||
)
|
||||
|
||||
|
||||
async def test_setup(hass, mock_asyncsleepiq):
|
||||
"""Test for successfully setting up the SleepIQ platform."""
|
||||
entry = await setup_platform(hass, DOMAIN)
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
assert len(entity_registry.entities) == 2
|
||||
|
||||
entry = entity_registry.async_get(f"light.sleepnumber_{BED_NAME_LOWER}_light_1")
|
||||
assert entry
|
||||
assert entry.original_name == f"SleepNumber {BED_NAME} Light 1"
|
||||
assert entry.unique_id == f"{BED_ID}-light-1"
|
||||
|
||||
entry = entity_registry.async_get(f"light.sleepnumber_{BED_NAME_LOWER}_light_2")
|
||||
assert entry
|
||||
assert entry.original_name == f"SleepNumber {BED_NAME} Light 2"
|
||||
assert entry.unique_id == f"{BED_ID}-light-2"
|
||||
|
||||
|
||||
async def test_light_set_states(hass, mock_asyncsleepiq):
|
||||
"""Test light change."""
|
||||
await setup_platform(hass, DOMAIN)
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"turn_on",
|
||||
{ATTR_ENTITY_ID: f"light.sleepnumber_{BED_NAME_LOWER}_light_1"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_asyncsleepiq.beds[BED_ID].foundation.lights[0].turn_on.assert_called_once()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"turn_off",
|
||||
{ATTR_ENTITY_ID: f"light.sleepnumber_{BED_NAME_LOWER}_light_1"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_asyncsleepiq.beds[BED_ID].foundation.lights[0].turn_off.assert_called_once()
|
||||
|
||||
|
||||
async def test_switch_get_states(hass, mock_asyncsleepiq):
|
||||
"""Test light update."""
|
||||
await setup_platform(hass, DOMAIN)
|
||||
|
||||
assert (
|
||||
hass.states.get(f"light.sleepnumber_{BED_NAME_LOWER}_light_1").state
|
||||
== STATE_OFF
|
||||
)
|
||||
mock_asyncsleepiq.beds[BED_ID].foundation.lights[0].is_on = True
|
||||
|
||||
async_fire_time_changed(hass, utcnow() + LONGER_UPDATE_INTERVAL)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
hass.states.get(f"light.sleepnumber_{BED_NAME_LOWER}_light_1").state == STATE_ON
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user