From 8a9f197918365c2088e338bcbdcf75ce257c256a Mon Sep 17 00:00:00 2001 From: Tim Rightnour <6556271+garbled1@users.noreply.github.com> Date: Tue, 30 Nov 2021 09:04:24 -0700 Subject: [PATCH] Binary sensor platform for the Balboa Spa (#60409) --- .../components/balboa/binary_sensor.py | 61 +++++++++++++++ homeassistant/components/balboa/const.py | 2 +- tests/components/balboa/test_binary_sensor.py | 76 +++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/balboa/binary_sensor.py create mode 100644 tests/components/balboa/test_binary_sensor.py diff --git a/homeassistant/components/balboa/binary_sensor.py b/homeassistant/components/balboa/binary_sensor.py new file mode 100644 index 00000000000..133ed2da9f4 --- /dev/null +++ b/homeassistant/components/balboa/binary_sensor.py @@ -0,0 +1,61 @@ +"""Support for Balboa Spa binary sensors.""" +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_MOVING, + BinarySensorEntity, +) + +from .const import CIRC_PUMP, DOMAIN, FILTER +from .entity import BalboaEntity + +FILTER_STATES = [ + [False, False], # self.FILTER_OFF + [True, False], # self.FILTER_1 + [False, True], # self.FILTER_2 + [True, True], # self.FILTER_1_2 +] + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up the spa's binary sensors.""" + spa = hass.data[DOMAIN][entry.entry_id] + entities = [ + BalboaSpaFilter(hass, entry, spa, FILTER, index) for index in range(1, 3) + ] + if spa.have_circ_pump(): + entities.append(BalboaSpaCircPump(hass, entry, spa, CIRC_PUMP)) + + async_add_entities(entities) + + +class BalboaSpaBinarySensor(BalboaEntity, BinarySensorEntity): + """Representation of a Balboa Spa binary sensor entity.""" + + _attr_device_class = DEVICE_CLASS_MOVING + + +class BalboaSpaCircPump(BalboaSpaBinarySensor): + """Representation of a Balboa Spa circulation pump.""" + + @property + def is_on(self) -> bool: + """Return true if the filter is on.""" + return self._client.get_circ_pump() + + @property + def icon(self): + """Return the icon to use in the frontend.""" + return "mdi:water-pump" if self.is_on else "mdi:water-pump-off" + + +class BalboaSpaFilter(BalboaSpaBinarySensor): + """Representation of a Balboa Spa Filter.""" + + @property + def is_on(self) -> bool: + """Return true if the filter is on.""" + return FILTER_STATES[self._client.get_filtermode()][self._num - 1] + + @property + def icon(self): + """Return the icon to use in the frontend.""" + return "mdi:sync" if self.is_on else "mdi:sync-off" diff --git a/homeassistant/components/balboa/const.py b/homeassistant/components/balboa/const.py index 121c6be3bfa..59ca0041bf6 100644 --- a/homeassistant/components/balboa/const.py +++ b/homeassistant/components/balboa/const.py @@ -21,7 +21,7 @@ CLIMATE_SUPPORTED_MODES = [HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF] CONF_SYNC_TIME = "sync_time" DEFAULT_SYNC_TIME = False FAN_SUPPORTED_SPEEDS = [SPEED_OFF, SPEED_LOW, SPEED_HIGH] -PLATFORMS = ["climate"] +PLATFORMS = ["binary_sensor", "climate"] AUX = "Aux" CIRC_PUMP = "Circ Pump" diff --git a/tests/components/balboa/test_binary_sensor.py b/tests/components/balboa/test_binary_sensor.py new file mode 100644 index 00000000000..748801b7b8f --- /dev/null +++ b/tests/components/balboa/test_binary_sensor.py @@ -0,0 +1,76 @@ +"""Tests of the climate entity of the balboa integration.""" + +from unittest.mock import patch + +from homeassistant.components.balboa.const import DOMAIN as BALBOA_DOMAIN, SIGNAL_UPDATE +from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.setup import async_setup_component + +from . import init_integration_mocked + +ENTITY_BINARY_SENSOR = "binary_sensor.fakespa_" + +FILTER_MAP = [ + [STATE_OFF, STATE_OFF], + [STATE_ON, STATE_OFF], + [STATE_OFF, STATE_ON], + [STATE_ON, STATE_ON], +] + + +async def test_filters(hass: HomeAssistant): + """Test spa filters.""" + + config_entry = await _setup_binary_sensor_test(hass) + + for filter_mode in range(4): + for spa_filter in range(1, 3): + state = await _patch_filter(hass, config_entry, filter_mode, spa_filter) + assert state.state == FILTER_MAP[filter_mode][spa_filter - 1] + + +async def test_circ_pump(hass: HomeAssistant): + """Test spa circ pump.""" + with patch( + "homeassistant.components.balboa.BalboaSpaWifi.have_circ_pump", + return_value=True, + ): + config_entry = await _setup_binary_sensor_test(hass) + + state = await _patch_circ_pump(hass, config_entry, True) + assert state.state == STATE_ON + state = await _patch_circ_pump(hass, config_entry, False) + assert state.state == STATE_OFF + + +async def _patch_circ_pump(hass, config_entry, pump_state): + """Patch the circ pump state.""" + with patch( + "homeassistant.components.balboa.BalboaSpaWifi.get_circ_pump", + return_value=pump_state, + ): + async_dispatcher_send(hass, SIGNAL_UPDATE.format(config_entry.entry_id)) + await hass.async_block_till_done() + return hass.states.get(f"{ENTITY_BINARY_SENSOR}circ_pump") + + +async def _patch_filter(hass, config_entry, filter_mode, num): + """Patch the filter state.""" + with patch( + "homeassistant.components.balboa.BalboaSpaWifi.get_filtermode", + return_value=filter_mode, + ): + async_dispatcher_send(hass, SIGNAL_UPDATE.format(config_entry.entry_id)) + await hass.async_block_till_done() + return hass.states.get(f"{ENTITY_BINARY_SENSOR}filter{num}") + + +async def _setup_binary_sensor_test(hass): + """Prepare the test.""" + config_entry = await init_integration_mocked(hass) + await async_setup_component(hass, BALBOA_DOMAIN, config_entry) + await hass.async_block_till_done() + + return config_entry