mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
Create button entities for SleepIQ (#66849)
This commit is contained in:
parent
18f26d312a
commit
d59dbbe859
@ -22,7 +22,7 @@ from .coordinator import SleepIQDataUpdateCoordinator
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR]
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
@ -35,9 +35,6 @@ CONFIG_SCHEMA = vol.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up sleepiq component."""
|
"""Set up sleepiq component."""
|
||||||
if DOMAIN in config:
|
if DOMAIN in config:
|
||||||
|
81
homeassistant/components/sleepiq/button.py
Normal file
81
homeassistant/components/sleepiq/button.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
"""Support for SleepIQ buttons."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from asyncsleepiq import SleepIQBed
|
||||||
|
|
||||||
|
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .coordinator import SleepIQDataUpdateCoordinator
|
||||||
|
from .entity import SleepIQEntity
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SleepIQButtonEntityDescriptionMixin:
|
||||||
|
"""Describes a SleepIQ Button entity."""
|
||||||
|
|
||||||
|
press_action: Callable[[SleepIQBed], Any]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SleepIQButtonEntityDescription(
|
||||||
|
ButtonEntityDescription, SleepIQButtonEntityDescriptionMixin
|
||||||
|
):
|
||||||
|
"""Class to describe a Button entity."""
|
||||||
|
|
||||||
|
|
||||||
|
ENTITY_DESCRIPTIONS = [
|
||||||
|
SleepIQButtonEntityDescription(
|
||||||
|
key="calibrate",
|
||||||
|
name="Calibrate",
|
||||||
|
press_action=lambda client: client.calibrate(),
|
||||||
|
icon="mdi:target",
|
||||||
|
),
|
||||||
|
SleepIQButtonEntityDescription(
|
||||||
|
key="stop-pump",
|
||||||
|
name="Stop Pump",
|
||||||
|
press_action=lambda client: client.stop_pump(),
|
||||||
|
icon="mdi:stop",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the sleep number buttons."""
|
||||||
|
coordinator: SleepIQDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
|
async_add_entities(
|
||||||
|
SleepNumberButton(bed, ed)
|
||||||
|
for bed in coordinator.client.beds.values()
|
||||||
|
for ed in ENTITY_DESCRIPTIONS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SleepNumberButton(SleepIQEntity, ButtonEntity):
|
||||||
|
"""Representation of an SleepIQ button."""
|
||||||
|
|
||||||
|
entity_description: SleepIQButtonEntityDescription
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, bed: SleepIQBed, entity_description: SleepIQButtonEntityDescription
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the Button."""
|
||||||
|
super().__init__(bed)
|
||||||
|
self._attr_name = f"SleepNumber {bed.name} {entity_description.name}"
|
||||||
|
self._attr_unique_id = f"{bed.id}-{entity_description.key}"
|
||||||
|
self.entity_description = entity_description
|
||||||
|
|
||||||
|
async def async_press(self) -> None:
|
||||||
|
"""Press the button."""
|
||||||
|
await self.entity_description.press_action(self.bed)
|
@ -5,7 +5,7 @@ from asyncsleepiq import SleepIQBed, SleepIQSleeper
|
|||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import device_registry
|
from homeassistant.helpers import device_registry
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||||
from homeassistant.helpers.update_coordinator import (
|
from homeassistant.helpers.update_coordinator import (
|
||||||
CoordinatorEntity,
|
CoordinatorEntity,
|
||||||
DataUpdateCoordinator,
|
DataUpdateCoordinator,
|
||||||
@ -14,6 +14,20 @@ from homeassistant.helpers.update_coordinator import (
|
|||||||
from .const import ICON_OCCUPIED, SENSOR_TYPES
|
from .const import ICON_OCCUPIED, SENSOR_TYPES
|
||||||
|
|
||||||
|
|
||||||
|
class SleepIQEntity(Entity):
|
||||||
|
"""Implementation of a SleepIQ entity."""
|
||||||
|
|
||||||
|
def __init__(self, bed: SleepIQBed) -> None:
|
||||||
|
"""Initialize the SleepIQ entity."""
|
||||||
|
self.bed = bed
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
connections={(device_registry.CONNECTION_NETWORK_MAC, bed.mac_addr)},
|
||||||
|
manufacturer="SleepNumber",
|
||||||
|
name=bed.name,
|
||||||
|
model=bed.model,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SleepIQSensor(CoordinatorEntity):
|
class SleepIQSensor(CoordinatorEntity):
|
||||||
"""Implementation of a SleepIQ sensor."""
|
"""Implementation of a SleepIQ sensor."""
|
||||||
|
|
||||||
@ -26,14 +40,10 @@ class SleepIQSensor(CoordinatorEntity):
|
|||||||
sleeper: SleepIQSleeper,
|
sleeper: SleepIQSleeper,
|
||||||
name: str,
|
name: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the SleepIQ side entity."""
|
"""Initialize the SleepIQ sensor entity."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self.bed = bed
|
|
||||||
self.sleeper = sleeper
|
self.sleeper = sleeper
|
||||||
self._async_update_attrs()
|
self.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._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
connections={(device_registry.CONNECTION_NETWORK_MAC, bed.mac_addr)},
|
connections={(device_registry.CONNECTION_NETWORK_MAC, bed.mac_addr)},
|
||||||
manufacturer="SleepNumber",
|
manufacturer="SleepNumber",
|
||||||
@ -41,6 +51,10 @@ class SleepIQSensor(CoordinatorEntity):
|
|||||||
model=bed.model,
|
model=bed.model,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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
|
@callback
|
||||||
def _handle_coordinator_update(self) -> None:
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Common methods for SleepIQ."""
|
"""Common methods for SleepIQ."""
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import create_autospec, patch
|
||||||
|
|
||||||
|
from asyncsleepiq import SleepIQBed, SleepIQSleeper
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.sleepiq import DOMAIN
|
from homeassistant.components.sleepiq import DOMAIN
|
||||||
@ -24,15 +25,15 @@ def mock_asyncsleepiq():
|
|||||||
"""Mock an AsyncSleepIQ object."""
|
"""Mock an AsyncSleepIQ object."""
|
||||||
with patch("homeassistant.components.sleepiq.AsyncSleepIQ", autospec=True) as mock:
|
with patch("homeassistant.components.sleepiq.AsyncSleepIQ", autospec=True) as mock:
|
||||||
client = mock.return_value
|
client = mock.return_value
|
||||||
bed = MagicMock()
|
bed = create_autospec(SleepIQBed)
|
||||||
client.beds = {BED_ID: bed}
|
client.beds = {BED_ID: bed}
|
||||||
bed.name = BED_NAME
|
bed.name = BED_NAME
|
||||||
bed.id = BED_ID
|
bed.id = BED_ID
|
||||||
bed.mac_addr = "12:34:56:78:AB:CD"
|
bed.mac_addr = "12:34:56:78:AB:CD"
|
||||||
bed.model = "C10"
|
bed.model = "C10"
|
||||||
bed.paused = False
|
bed.paused = False
|
||||||
sleeper_l = MagicMock()
|
sleeper_l = create_autospec(SleepIQSleeper)
|
||||||
sleeper_r = MagicMock()
|
sleeper_r = create_autospec(SleepIQSleeper)
|
||||||
bed.sleepers = [sleeper_l, sleeper_r]
|
bed.sleepers = [sleeper_l, sleeper_r]
|
||||||
|
|
||||||
sleeper_l.side = "L"
|
sleeper_l.side = "L"
|
||||||
|
61
tests/components/sleepiq/test_button.py
Normal file
61
tests/components/sleepiq/test_button.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
"""The tests for SleepIQ binary sensor platform."""
|
||||||
|
from homeassistant.components.button import DOMAIN
|
||||||
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from tests.components.sleepiq.conftest import (
|
||||||
|
BED_ID,
|
||||||
|
BED_NAME,
|
||||||
|
BED_NAME_LOWER,
|
||||||
|
setup_platform,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_button_calibrate(hass, mock_asyncsleepiq):
|
||||||
|
"""Test the SleepIQ calibrate button."""
|
||||||
|
await setup_platform(hass, DOMAIN)
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
|
state = hass.states.get(f"button.sleepnumber_{BED_NAME_LOWER}_calibrate")
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_FRIENDLY_NAME) == f"SleepNumber {BED_NAME} Calibrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
entity = entity_registry.async_get(f"button.sleepnumber_{BED_NAME_LOWER}_calibrate")
|
||||||
|
assert entity
|
||||||
|
assert entity.unique_id == f"{BED_ID}-calibrate"
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
"press",
|
||||||
|
{ATTR_ENTITY_ID: f"button.sleepnumber_{BED_NAME_LOWER}_calibrate"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_asyncsleepiq.beds[BED_ID].calibrate.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_button_stop_pump(hass, mock_asyncsleepiq):
|
||||||
|
"""Test the SleepIQ stop pump button."""
|
||||||
|
await setup_platform(hass, DOMAIN)
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
|
state = hass.states.get(f"button.sleepnumber_{BED_NAME_LOWER}_stop_pump")
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_FRIENDLY_NAME) == f"SleepNumber {BED_NAME} Stop Pump"
|
||||||
|
)
|
||||||
|
|
||||||
|
entity = entity_registry.async_get(f"button.sleepnumber_{BED_NAME_LOWER}_stop_pump")
|
||||||
|
assert entity
|
||||||
|
assert entity.unique_id == f"{BED_ID}-stop-pump"
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
"press",
|
||||||
|
{ATTR_ENTITY_ID: f"button.sleepnumber_{BED_NAME_LOWER}_stop_pump"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_asyncsleepiq.beds[BED_ID].stop_pump.assert_called_once()
|
Loading…
x
Reference in New Issue
Block a user