Create button entities for SleepIQ (#66849)

pull/66905/head
Keilin Bickar 2022-02-19 12:54:52 -05:00 committed by GitHub
parent 18f26d312a
commit d59dbbe859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 169 additions and 15 deletions

View File

@ -22,7 +22,7 @@ from .coordinator import SleepIQDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
PLATFORMS = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR]
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:
"""Set up sleepiq component."""
if DOMAIN in config:

View 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)

View File

@ -5,7 +5,7 @@ from asyncsleepiq import SleepIQBed, SleepIQSleeper
from homeassistant.core import callback
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 (
CoordinatorEntity,
DataUpdateCoordinator,
@ -14,6 +14,20 @@ from homeassistant.helpers.update_coordinator import (
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):
"""Implementation of a SleepIQ sensor."""
@ -26,14 +40,10 @@ class SleepIQSensor(CoordinatorEntity):
sleeper: SleepIQSleeper,
name: str,
) -> None:
"""Initialize the SleepIQ side entity."""
"""Initialize the SleepIQ sensor entity."""
super().__init__(coordinator)
self.bed = bed
self.sleeper = sleeper
self._async_update_attrs()
self._attr_name = f"SleepNumber {bed.name} {sleeper.name} {SENSOR_TYPES[name]}"
self._attr_unique_id = f"{bed.id}_{sleeper.name}_{name}"
self.bed = bed
self._attr_device_info = DeviceInfo(
connections={(device_registry.CONNECTION_NETWORK_MAC, bed.mac_addr)},
manufacturer="SleepNumber",
@ -41,6 +51,10 @@ class SleepIQSensor(CoordinatorEntity):
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
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""

View File

@ -1,6 +1,7 @@
"""Common methods for SleepIQ."""
from unittest.mock import MagicMock, patch
from unittest.mock import create_autospec, patch
from asyncsleepiq import SleepIQBed, SleepIQSleeper
import pytest
from homeassistant.components.sleepiq import DOMAIN
@ -24,15 +25,15 @@ def mock_asyncsleepiq():
"""Mock an AsyncSleepIQ object."""
with patch("homeassistant.components.sleepiq.AsyncSleepIQ", autospec=True) as mock:
client = mock.return_value
bed = MagicMock()
bed = create_autospec(SleepIQBed)
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()
sleeper_l = create_autospec(SleepIQSleeper)
sleeper_r = create_autospec(SleepIQSleeper)
bed.sleepers = [sleeper_l, sleeper_r]
sleeper_l.side = "L"

View 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()