diff --git a/homeassistant/components/apsystems/__init__.py b/homeassistant/components/apsystems/__init__.py index 40e62a32475..91650201a87 100644 --- a/homeassistant/components/apsystems/__init__.py +++ b/homeassistant/components/apsystems/__init__.py @@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant from .const import DEFAULT_PORT from .coordinator import ApSystemsDataCoordinator -PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR] +PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR, Platform.SWITCH] @dataclass diff --git a/homeassistant/components/apsystems/strings.json b/homeassistant/components/apsystems/strings.json index 95499e96b4d..18200f7b49d 100644 --- a/homeassistant/components/apsystems/strings.json +++ b/homeassistant/components/apsystems/strings.json @@ -20,18 +20,43 @@ }, "entity": { "sensor": { - "total_power": { "name": "Total power" }, - "total_power_p1": { "name": "Power of P1" }, - "total_power_p2": { "name": "Power of P2" }, - "lifetime_production": { "name": "Total lifetime production" }, - "lifetime_production_p1": { "name": "Lifetime production of P1" }, - "lifetime_production_p2": { "name": "Lifetime production of P2" }, - "today_production": { "name": "Production of today" }, - "today_production_p1": { "name": "Production of today from P1" }, - "today_production_p2": { "name": "Production of today from P2" } + "total_power": { + "name": "Total power" + }, + "total_power_p1": { + "name": "Power of P1" + }, + "total_power_p2": { + "name": "Power of P2" + }, + "lifetime_production": { + "name": "Total lifetime production" + }, + "lifetime_production_p1": { + "name": "Lifetime production of P1" + }, + "lifetime_production_p2": { + "name": "Lifetime production of P2" + }, + "today_production": { + "name": "Production of today" + }, + "today_production_p1": { + "name": "Production of today from P1" + }, + "today_production_p2": { + "name": "Production of today from P2" + } }, "number": { - "max_output": { "name": "Max output" } + "max_output": { + "name": "Max output" + } + }, + "switch": { + "inverter_status": { + "name": "Inverter status" + } } } } diff --git a/homeassistant/components/apsystems/switch.py b/homeassistant/components/apsystems/switch.py new file mode 100644 index 00000000000..405adc94b27 --- /dev/null +++ b/homeassistant/components/apsystems/switch.py @@ -0,0 +1,56 @@ +"""The power switch which can be toggled via the APsystems local API integration.""" + +from __future__ import annotations + +from typing import Any + +from aiohttp.client_exceptions import ClientConnectionError +from APsystemsEZ1 import Status + +from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ApSystemsConfigEntry, ApSystemsData +from .entity import ApSystemsEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ApSystemsConfigEntry, + add_entities: AddEntitiesCallback, +) -> None: + """Set up the switch platform.""" + + add_entities([ApSystemsInverterSwitch(config_entry.runtime_data)], True) + + +class ApSystemsInverterSwitch(ApSystemsEntity, SwitchEntity): + """The switch class for APSystems switches.""" + + _attr_device_class = SwitchDeviceClass.SWITCH + _attr_translation_key = "inverter_status" + + def __init__(self, data: ApSystemsData) -> None: + """Initialize the switch.""" + super().__init__(data) + self._api = data.coordinator.api + self._attr_unique_id = f"{data.device_id}_inverter_status" + + async def async_update(self) -> None: + """Update switch status and availability.""" + try: + status = await self._api.get_device_power_status() + except (TimeoutError, ClientConnectionError): + self._attr_available = False + else: + self._attr_available = True + self._attr_is_on = status == Status.normal + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the switch on.""" + await self._api.set_device_power_status(0) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the switch off.""" + await self._api.set_device_power_status(1) diff --git a/tests/components/apsystems/conftest.py b/tests/components/apsystems/conftest.py index 682086be380..c191c7ca2dc 100644 --- a/tests/components/apsystems/conftest.py +++ b/tests/components/apsystems/conftest.py @@ -3,7 +3,7 @@ from collections.abc import Generator from unittest.mock import AsyncMock, MagicMock, patch -from APsystemsEZ1 import ReturnDeviceInfo, ReturnOutputData +from APsystemsEZ1 import ReturnDeviceInfo, ReturnOutputData, Status import pytest from homeassistant.components.apsystems.const import DOMAIN @@ -52,6 +52,7 @@ def mock_apsystems() -> Generator[MagicMock]: e2=6.0, te2=7.0, ) + mock_api.get_device_power_status.return_value = Status.normal yield mock_api diff --git a/tests/components/apsystems/snapshots/test_switch.ambr b/tests/components/apsystems/snapshots/test_switch.ambr new file mode 100644 index 00000000000..6daa9fd6e14 --- /dev/null +++ b/tests/components/apsystems/snapshots/test_switch.ambr @@ -0,0 +1,48 @@ +# serializer version: 1 +# name: test_all_entities[switch.mock_title_inverter_status-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.mock_title_inverter_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Inverter status', + 'platform': 'apsystems', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'inverter_status', + 'unique_id': 'MY_SERIAL_NUMBER_inverter_status', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[switch.mock_title_inverter_status-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'switch', + 'friendly_name': 'Mock Title Inverter status', + }), + 'context': , + 'entity_id': 'switch.mock_title_inverter_status', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- diff --git a/tests/components/apsystems/test_switch.py b/tests/components/apsystems/test_switch.py new file mode 100644 index 00000000000..afd889fe958 --- /dev/null +++ b/tests/components/apsystems/test_switch.py @@ -0,0 +1,31 @@ +"""Test the APSystem switch module.""" + +from unittest.mock import AsyncMock, patch + +from syrupy import SnapshotAssertion + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, snapshot_platform + + +async def test_all_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_apsystems: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test all entities.""" + with patch( + "homeassistant.components.apsystems.PLATFORMS", + [Platform.SWITCH], + ): + await setup_integration(hass, mock_config_entry) + await snapshot_platform( + hass, entity_registry, snapshot, mock_config_entry.entry_id + )