mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 08:47:10 +00:00
Add binary sensor for smarttub errors (#49364)
This commit is contained in:
parent
64661ee2b7
commit
ffb9ab21c1
@ -1,7 +1,7 @@
|
|||||||
"""Platform for binary sensor integration."""
|
"""Platform for binary sensor integration."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from smarttub import SpaReminder
|
from smarttub import SpaError, SpaReminder
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
@ -11,7 +11,7 @@ from homeassistant.components.binary_sensor import (
|
|||||||
)
|
)
|
||||||
from homeassistant.helpers import entity_platform
|
from homeassistant.helpers import entity_platform
|
||||||
|
|
||||||
from .const import ATTR_REMINDERS, DOMAIN, SMARTTUB_CONTROLLER
|
from .const import ATTR_ERRORS, ATTR_REMINDERS, DOMAIN, SMARTTUB_CONTROLLER
|
||||||
from .entity import SmartTubEntity, SmartTubSensorBase
|
from .entity import SmartTubEntity, SmartTubSensorBase
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -19,6 +19,13 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
# whether the reminder has been snoozed (bool)
|
# whether the reminder has been snoozed (bool)
|
||||||
ATTR_REMINDER_SNOOZED = "snoozed"
|
ATTR_REMINDER_SNOOZED = "snoozed"
|
||||||
|
|
||||||
|
ATTR_ERROR_CODE = "error_code"
|
||||||
|
ATTR_ERROR_TITLE = "error_title"
|
||||||
|
ATTR_ERROR_DESCRIPTION = "error_description"
|
||||||
|
ATTR_ERROR_TYPE = "error_type"
|
||||||
|
ATTR_CREATED_AT = "created_at"
|
||||||
|
ATTR_UPDATED_AT = "updated_at"
|
||||||
|
|
||||||
# how many days to snooze the reminder for
|
# how many days to snooze the reminder for
|
||||||
ATTR_SNOOZE_DAYS = "days"
|
ATTR_SNOOZE_DAYS = "days"
|
||||||
SNOOZE_REMINDER_SCHEMA = {
|
SNOOZE_REMINDER_SCHEMA = {
|
||||||
@ -34,6 +41,7 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||||||
entities = []
|
entities = []
|
||||||
for spa in controller.spas:
|
for spa in controller.spas:
|
||||||
entities.append(SmartTubOnline(controller.coordinator, spa))
|
entities.append(SmartTubOnline(controller.coordinator, spa))
|
||||||
|
entities.append(SmartTubError(controller.coordinator, spa))
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SmartTubReminder(controller.coordinator, spa, reminder)
|
SmartTubReminder(controller.coordinator, spa, reminder)
|
||||||
for reminder in controller.coordinator.data[spa.id][ATTR_REMINDERS].values()
|
for reminder in controller.coordinator.data[spa.id][ATTR_REMINDERS].values()
|
||||||
@ -119,3 +127,54 @@ class SmartTubReminder(SmartTubEntity, BinarySensorEntity):
|
|||||||
"""Snooze this reminder for the specified number of days."""
|
"""Snooze this reminder for the specified number of days."""
|
||||||
await self.reminder.snooze(days)
|
await self.reminder.snooze(days)
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
|
||||||
|
class SmartTubError(SmartTubEntity, BinarySensorEntity):
|
||||||
|
"""Indicates whether an error code is present.
|
||||||
|
|
||||||
|
There may be 0 or more errors. If there are >0, we show the first one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, coordinator, spa):
|
||||||
|
"""Initialize the entity."""
|
||||||
|
super().__init__(
|
||||||
|
coordinator,
|
||||||
|
spa,
|
||||||
|
"Error",
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def error(self) -> SpaError:
|
||||||
|
"""Return the underlying SpaError object for this entity."""
|
||||||
|
errors = self.coordinator.data[self.spa.id][ATTR_ERRORS]
|
||||||
|
if len(errors) == 0:
|
||||||
|
return None
|
||||||
|
return errors[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return true if an error is signaled."""
|
||||||
|
return self.error is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
|
||||||
|
error = self.error
|
||||||
|
|
||||||
|
if error is None:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ATTR_ERROR_CODE: error.code,
|
||||||
|
ATTR_ERROR_TITLE: error.title,
|
||||||
|
ATTR_ERROR_DESCRIPTION: error.description,
|
||||||
|
ATTR_ERROR_TYPE: error.error_type,
|
||||||
|
ATTR_CREATED_AT: error.created_at.isoformat(),
|
||||||
|
ATTR_UPDATED_AT: error.updated_at.isoformat(),
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self) -> str:
|
||||||
|
"""Return the device class for this entity."""
|
||||||
|
return DEVICE_CLASS_PROBLEM
|
||||||
|
@ -21,6 +21,7 @@ DEFAULT_LIGHT_EFFECT = "purple"
|
|||||||
# default to 50% brightness
|
# default to 50% brightness
|
||||||
DEFAULT_LIGHT_BRIGHTNESS = 128
|
DEFAULT_LIGHT_BRIGHTNESS = 128
|
||||||
|
|
||||||
|
ATTR_ERRORS = "errors"
|
||||||
ATTR_LIGHTS = "lights"
|
ATTR_LIGHTS = "lights"
|
||||||
ATTR_PUMPS = "pumps"
|
ATTR_PUMPS = "pumps"
|
||||||
ATTR_REMINDERS = "reminders"
|
ATTR_REMINDERS = "reminders"
|
||||||
|
@ -16,6 +16,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
ATTR_ERRORS,
|
||||||
ATTR_LIGHTS,
|
ATTR_LIGHTS,
|
||||||
ATTR_PUMPS,
|
ATTR_PUMPS,
|
||||||
ATTR_REMINDERS,
|
ATTR_REMINDERS,
|
||||||
@ -92,15 +93,17 @@ class SmartTubController:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
async def _get_spa_data(self, spa):
|
async def _get_spa_data(self, spa):
|
||||||
full_status, reminders = await asyncio.gather(
|
full_status, reminders, errors = await asyncio.gather(
|
||||||
spa.get_status_full(),
|
spa.get_status_full(),
|
||||||
spa.get_reminders(),
|
spa.get_reminders(),
|
||||||
|
spa.get_errors(),
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
ATTR_STATUS: full_status,
|
ATTR_STATUS: full_status,
|
||||||
ATTR_PUMPS: {pump.id: pump for pump in full_status.pumps},
|
ATTR_PUMPS: {pump.id: pump for pump in full_status.pumps},
|
||||||
ATTR_LIGHTS: {light.zone: light for light in full_status.lights},
|
ATTR_LIGHTS: {light.zone: light for light in full_status.lights},
|
||||||
ATTR_REMINDERS: {reminder.id: reminder for reminder in reminders},
|
ATTR_REMINDERS: {reminder.id: reminder for reminder in reminders},
|
||||||
|
ATTR_ERRORS: errors,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def async_register_devices(self, entry):
|
async def async_register_devices(self, entry):
|
||||||
|
@ -87,6 +87,8 @@ def mock_spa(spa_state):
|
|||||||
|
|
||||||
mock_spa.get_reminders.return_value = [mock_filter_reminder]
|
mock_spa.get_reminders.return_value = [mock_filter_reminder]
|
||||||
|
|
||||||
|
mock_spa.get_errors.return_value = []
|
||||||
|
|
||||||
return mock_spa
|
return mock_spa
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
"""Test the SmartTub binary sensor platform."""
|
"""Test the SmartTub binary sensor platform."""
|
||||||
from homeassistant.components.binary_sensor import STATE_OFF
|
from datetime import datetime
|
||||||
|
from unittest.mock import create_autospec
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import smarttub
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import STATE_OFF, STATE_ON
|
||||||
|
|
||||||
|
|
||||||
async def test_binary_sensors(spa, setup_entry, hass):
|
async def test_binary_sensors(spa, setup_entry, hass):
|
||||||
@ -10,6 +16,11 @@ async def test_binary_sensors(spa, setup_entry, hass):
|
|||||||
# disabled by default
|
# disabled by default
|
||||||
assert state is None
|
assert state is None
|
||||||
|
|
||||||
|
entity_id = f"binary_sensor.{spa.brand}_{spa.model}_error"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
async def test_reminders(spa, setup_entry, hass):
|
async def test_reminders(spa, setup_entry, hass):
|
||||||
"""Test the reminder sensor."""
|
"""Test the reminder sensor."""
|
||||||
@ -21,6 +32,37 @@ async def test_reminders(spa, setup_entry, hass):
|
|||||||
assert state.attributes["snoozed"] is False
|
assert state.attributes["snoozed"] is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_error(spa):
|
||||||
|
"""Mock error."""
|
||||||
|
error = create_autospec(smarttub.SpaError, instance=True)
|
||||||
|
error.code = 11
|
||||||
|
error.title = "Flow Switch Stuck Open"
|
||||||
|
error.description = None
|
||||||
|
error.active = True
|
||||||
|
error.created_at = datetime.now()
|
||||||
|
error.updated_at = datetime.now()
|
||||||
|
error.error_type = "TUB_ERROR"
|
||||||
|
return error
|
||||||
|
|
||||||
|
|
||||||
|
async def test_error(spa, hass, config_entry, mock_error):
|
||||||
|
"""Test the error sensor."""
|
||||||
|
|
||||||
|
spa.get_errors.return_value = [mock_error]
|
||||||
|
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entity_id = f"binary_sensor.{spa.brand}_{spa.model}_error"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["error_code"] == 11
|
||||||
|
|
||||||
|
|
||||||
async def test_snooze(spa, setup_entry, hass):
|
async def test_snooze(spa, setup_entry, hass):
|
||||||
"""Test snoozing a reminder."""
|
"""Test snoozing a reminder."""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user