Add apsystems power switch (#122447)

* bring back power switch

* fix pylint issues

* add SWITCH to platform list

* improve run_on and turn_off functions

* ruff formatting

* replace _state with _attr_is_on

* Update homeassistant/components/apsystems/switch.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* remove unused dependencies

* Update homeassistant/components/apsystems/switch.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* use async functions from api

* convert Api IntEnum Status Information to bool

* add translation key

* implement async_update again

* replace finally with else

* better handling of bool value

* Update homeassistant/components/apsystems/switch.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Update homeassistant/components/apsystems/switch.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* rename power switch to inverter switch

* add test_number and test_switch module

* remove test_number

* Add mock entry for get_device_power_status

* Add mock entry for get_device_power_status

* Update test snapshots

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Simon Hörrle 2024-07-30 18:11:08 +02:00 committed by GitHub
parent 18a7d15d14
commit ea727546d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 173 additions and 12 deletions

View File

@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant
from .const import DEFAULT_PORT from .const import DEFAULT_PORT
from .coordinator import ApSystemsDataCoordinator from .coordinator import ApSystemsDataCoordinator
PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR] PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SENSOR, Platform.SWITCH]
@dataclass @dataclass

View File

@ -20,18 +20,43 @@
}, },
"entity": { "entity": {
"sensor": { "sensor": {
"total_power": { "name": "Total power" }, "total_power": {
"total_power_p1": { "name": "Power of P1" }, "name": "Total power"
"total_power_p2": { "name": "Power of P2" }, },
"lifetime_production": { "name": "Total lifetime production" }, "total_power_p1": {
"lifetime_production_p1": { "name": "Lifetime production of P1" }, "name": "Power of P1"
"lifetime_production_p2": { "name": "Lifetime production of P2" }, },
"today_production": { "name": "Production of today" }, "total_power_p2": {
"today_production_p1": { "name": "Production of today from P1" }, "name": "Power of P2"
"today_production_p2": { "name": "Production of today from 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": { "number": {
"max_output": { "name": "Max output" } "max_output": {
"name": "Max output"
}
},
"switch": {
"inverter_status": {
"name": "Inverter status"
}
} }
} }
} }

View File

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

View File

@ -3,7 +3,7 @@
from collections.abc import Generator from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from APsystemsEZ1 import ReturnDeviceInfo, ReturnOutputData from APsystemsEZ1 import ReturnDeviceInfo, ReturnOutputData, Status
import pytest import pytest
from homeassistant.components.apsystems.const import DOMAIN from homeassistant.components.apsystems.const import DOMAIN
@ -52,6 +52,7 @@ def mock_apsystems() -> Generator[MagicMock]:
e2=6.0, e2=6.0,
te2=7.0, te2=7.0,
) )
mock_api.get_device_power_status.return_value = Status.normal
yield mock_api yield mock_api

View File

@ -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': <ANY>,
'device_class': None,
'device_id': <ANY>,
'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': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
'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': <ANY>,
'entity_id': 'switch.mock_title_inverter_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

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