From 65691fffd6d00c5292ea1cff990662268652fc2e Mon Sep 17 00:00:00 2001 From: Charles Garwood Date: Tue, 22 Aug 2023 21:28:39 -0400 Subject: [PATCH] Change Enphase dry contact relay binary_sensor to switch (#98467) * Switch relay status from binary_sensor to switch * docstring * Bump pyenphase to 1.7.1 * review comments pt1 * review comments pt2 * Mutate data in lib instead of HA * Bump pyenphase to 1.8.1 --- .../components/enphase_envoy/binary_sensor.py | 43 ---------- .../components/enphase_envoy/manifest.json | 2 +- .../components/enphase_envoy/strings.json | 3 - .../components/enphase_envoy/switch.py | 81 ++++++++++++++++++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 6 files changed, 83 insertions(+), 50 deletions(-) diff --git a/homeassistant/components/enphase_envoy/binary_sensor.py b/homeassistant/components/enphase_envoy/binary_sensor.py index 009b5d18338..7060943deb8 100644 --- a/homeassistant/components/enphase_envoy/binary_sensor.py +++ b/homeassistant/components/enphase_envoy/binary_sensor.py @@ -5,7 +5,6 @@ from collections.abc import Callable from dataclasses import dataclass from pyenphase import EnvoyEncharge, EnvoyEnpower -from pyenphase.models.dry_contacts import DryContactStatus from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -53,12 +52,6 @@ ENCHARGE_SENSORS = ( ), ) -RELAY_STATUS_SENSOR = BinarySensorEntityDescription( - key="relay_status", - translation_key="relay", - icon="mdi:power-plug", -) - @dataclass class EnvoyEnpowerRequiredKeysMixin: @@ -114,11 +107,6 @@ async def async_setup_entry( for description in ENPOWER_SENSORS ) - if envoy_data.dry_contact_status: - entities.extend( - EnvoyRelayBinarySensorEntity(coordinator, RELAY_STATUS_SENSOR, relay) - for relay in envoy_data.dry_contact_status - ) async_add_entities(entities) @@ -190,34 +178,3 @@ class EnvoyEnpowerBinarySensorEntity(EnvoyBaseBinarySensorEntity): enpower = self.data.enpower assert enpower is not None return self.entity_description.value_fn(enpower) - - -class EnvoyRelayBinarySensorEntity(EnvoyBaseBinarySensorEntity): - """Defines an Enpower dry contact binary_sensor entity.""" - - def __init__( - self, - coordinator: EnphaseUpdateCoordinator, - description: BinarySensorEntityDescription, - relay_id: str, - ) -> None: - """Init the Enpower base entity.""" - super().__init__(coordinator, description) - enpower = self.data.enpower - assert enpower is not None - self._relay_id = relay_id - self._attr_unique_id = f"{enpower.serial_number}_relay_{relay_id}" - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, relay_id)}, - manufacturer="Enphase", - model="Dry contact relay", - name=self.data.dry_contact_settings[relay_id].load_name, - sw_version=str(enpower.firmware_version), - via_device=(DOMAIN, enpower.serial_number), - ) - - @property - def is_on(self) -> bool: - """Return the state of the Enpower binary_sensor.""" - relay = self.data.dry_contact_status[self._relay_id] - return relay.status == DryContactStatus.CLOSED diff --git a/homeassistant/components/enphase_envoy/manifest.json b/homeassistant/components/enphase_envoy/manifest.json index 62f7c73ef76..540c121bb17 100644 --- a/homeassistant/components/enphase_envoy/manifest.json +++ b/homeassistant/components/enphase_envoy/manifest.json @@ -6,7 +6,7 @@ "documentation": "https://www.home-assistant.io/integrations/enphase_envoy", "iot_class": "local_polling", "loggers": ["pyenphase"], - "requirements": ["pyenphase==1.6.0"], + "requirements": ["pyenphase==1.8.1"], "zeroconf": [ { "type": "_enphase-envoy._tcp.local." diff --git a/homeassistant/components/enphase_envoy/strings.json b/homeassistant/components/enphase_envoy/strings.json index 477da2b3211..ae0ac31413c 100644 --- a/homeassistant/components/enphase_envoy/strings.json +++ b/homeassistant/components/enphase_envoy/strings.json @@ -31,9 +31,6 @@ }, "grid_status": { "name": "Grid status" - }, - "relay": { - "name": "Relay status" } }, "number": { diff --git a/homeassistant/components/enphase_envoy/switch.py b/homeassistant/components/enphase_envoy/switch.py index e0f211a1019..fb9e14406ac 100644 --- a/homeassistant/components/enphase_envoy/switch.py +++ b/homeassistant/components/enphase_envoy/switch.py @@ -6,7 +6,8 @@ from dataclasses import dataclass import logging from typing import Any -from pyenphase import Envoy, EnvoyEnpower +from pyenphase import Envoy, EnvoyDryContactStatus, EnvoyEnpower +from pyenphase.models.dry_contacts import DryContactStatus from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.config_entries import ConfigEntry @@ -37,6 +38,22 @@ class EnvoyEnpowerSwitchEntityDescription( """Describes an Envoy Enpower switch entity.""" +@dataclass +class EnvoyDryContactRequiredKeysMixin: + """Mixin for required keys.""" + + value_fn: Callable[[EnvoyDryContactStatus], bool] + turn_on_fn: Callable[[Envoy, str], Coroutine[Any, Any, dict[str, Any]]] + turn_off_fn: Callable[[Envoy, str], Coroutine[Any, Any, dict[str, Any]]] + + +@dataclass +class EnvoyDryContactSwitchEntityDescription( + SwitchEntityDescription, EnvoyDryContactRequiredKeysMixin +): + """Describes an Envoy Enpower dry contact switch entity.""" + + ENPOWER_GRID_SWITCH = EnvoyEnpowerSwitchEntityDescription( key="mains_admin_state", translation_key="grid_enabled", @@ -45,6 +62,13 @@ ENPOWER_GRID_SWITCH = EnvoyEnpowerSwitchEntityDescription( turn_off_fn=lambda envoy: envoy.go_off_grid(), ) +RELAY_STATE_SWITCH = EnvoyDryContactSwitchEntityDescription( + key="relay_status", + value_fn=lambda dry_contact: dry_contact.status == DryContactStatus.CLOSED, + turn_on_fn=lambda envoy, id: envoy.close_dry_contact(id), + turn_off_fn=lambda envoy, id: envoy.open_dry_contact(id), +) + async def async_setup_entry( hass: HomeAssistant, @@ -64,6 +88,13 @@ async def async_setup_entry( ) ] ) + + if envoy_data.dry_contact_status: + entities.extend( + EnvoyDryContactSwitchEntity(coordinator, RELAY_STATE_SWITCH, relay) + for relay in envoy_data.dry_contact_status + ) + async_add_entities(entities) @@ -109,3 +140,51 @@ class EnvoyEnpowerSwitchEntity(EnvoyBaseEntity, SwitchEntity): """Turn off the Enpower switch.""" await self.entity_description.turn_off_fn(self.envoy) await self.coordinator.async_request_refresh() + + +class EnvoyDryContactSwitchEntity(EnvoyBaseEntity, SwitchEntity): + """Representation of an Enphase dry contact switch entity.""" + + entity_description: EnvoyDryContactSwitchEntityDescription + _attr_name = None + + def __init__( + self, + coordinator: EnphaseUpdateCoordinator, + description: EnvoyDryContactSwitchEntityDescription, + relay_id: str, + ) -> None: + """Initialize the Enphase dry contact switch entity.""" + super().__init__(coordinator, description) + self.envoy = coordinator.envoy + enpower = self.data.enpower + assert enpower is not None + self.relay_id = relay_id + serial_number = enpower.serial_number + self._attr_unique_id = f"{serial_number}_relay_{relay_id}_{description.key}" + relay = self.data.dry_contact_settings[relay_id] + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, relay_id)}, + manufacturer="Enphase", + model="Dry contact relay", + name=relay.load_name, + sw_version=str(enpower.firmware_version), + via_device=(DOMAIN, enpower.serial_number), + ) + + @property + def is_on(self) -> bool: + """Return the state of the dry contact.""" + relay = self.data.dry_contact_status[self.relay_id] + assert relay is not None + return self.entity_description.value_fn(relay) + + async def async_turn_on(self): + """Turn on (close) the dry contact.""" + if await self.entity_description.turn_on_fn(self.envoy, self.relay_id): + self.async_write_ha_state() + + async def async_turn_off(self): + """Turn off (open) the dry contact.""" + if await self.entity_description.turn_off_fn(self.envoy, self.relay_id): + self.async_write_ha_state() diff --git a/requirements_all.txt b/requirements_all.txt index 55b7de79dfb..4a9592a5a1e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1665,7 +1665,7 @@ pyedimax==0.2.1 pyefergy==22.1.1 # homeassistant.components.enphase_envoy -pyenphase==1.6.0 +pyenphase==1.8.1 # homeassistant.components.envisalink pyenvisalink==4.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1a6220971c4..bfdca770319 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1229,7 +1229,7 @@ pyeconet==0.1.20 pyefergy==22.1.1 # homeassistant.components.enphase_envoy -pyenphase==1.6.0 +pyenphase==1.8.1 # homeassistant.components.everlights pyeverlights==0.1.0