mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Add Envoy enpower sensors (#98086)
This commit is contained in:
parent
d975e93abc
commit
ce6b759b70
@ -8,7 +8,9 @@ import logging
|
||||
from pyenphase import (
|
||||
EnvoyData,
|
||||
EnvoyEncharge,
|
||||
EnvoyEnpower,
|
||||
)
|
||||
from pyenphase.models.dry_contacts import DryContactStatus
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
@ -66,6 +68,47 @@ ENCHARGE_SENSORS = (
|
||||
),
|
||||
)
|
||||
|
||||
RELAY_STATUS_SENSOR = BinarySensorEntityDescription(
|
||||
key="relay_status", icon="mdi:power-plug", has_entity_name=True
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnvoyEnpowerRequiredKeysMixin:
|
||||
"""Mixin for required keys."""
|
||||
|
||||
value_fn: Callable[[EnvoyEnpower], bool]
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnvoyEnpowerBinarySensorEntityDescription(
|
||||
BinarySensorEntityDescription, EnvoyEnpowerRequiredKeysMixin
|
||||
):
|
||||
"""Describes an Envoy Enpower binary sensor entity."""
|
||||
|
||||
|
||||
ENPOWER_SENSORS = (
|
||||
EnvoyEnpowerBinarySensorEntityDescription(
|
||||
key="communicating",
|
||||
translation_key="communicating",
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=lambda enpower: enpower.communicating,
|
||||
),
|
||||
EnvoyEnpowerBinarySensorEntityDescription(
|
||||
key="operating",
|
||||
translation_key="operating",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=lambda enpower: enpower.operating,
|
||||
),
|
||||
EnvoyEnpowerBinarySensorEntityDescription(
|
||||
key="mains_oper_state",
|
||||
translation_key="grid_status",
|
||||
icon="mdi:transmission-tower",
|
||||
value_fn=lambda enpower: enpower.mains_oper_state == "closed",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@ -86,15 +129,50 @@ async def async_setup_entry(
|
||||
for encharge in envoy_data.encharge_inventory
|
||||
)
|
||||
|
||||
if envoy_data.enpower:
|
||||
entities.extend(
|
||||
EnvoyEnpowerBinarySensorEntity(coordinator, description)
|
||||
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)
|
||||
|
||||
|
||||
class EnvoyEnchargeBinarySensorEntity(
|
||||
class EnvoyBaseBinarySensorEntity(
|
||||
CoordinatorEntity[EnphaseUpdateCoordinator], BinarySensorEntity
|
||||
):
|
||||
"""Defines a base envoy binary_sensor entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: EnphaseUpdateCoordinator,
|
||||
description: BinarySensorEntityDescription,
|
||||
) -> None:
|
||||
"""Init the Enphase base binary_sensor entity."""
|
||||
self.entity_description = description
|
||||
serial_number = coordinator.envoy.serial_number
|
||||
assert serial_number is not None
|
||||
self.envoy_serial_num = serial_number
|
||||
super().__init__(coordinator)
|
||||
|
||||
@property
|
||||
def data(self) -> EnvoyData:
|
||||
"""Return envoy data."""
|
||||
data = self.coordinator.envoy.data
|
||||
assert data is not None
|
||||
return data
|
||||
|
||||
|
||||
class EnvoyEnchargeBinarySensorEntity(EnvoyBaseBinarySensorEntity):
|
||||
"""Defines an Encharge binary_sensor entity."""
|
||||
|
||||
entity_description: EnvoyEnchargeBinarySensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
@ -104,13 +182,7 @@ class EnvoyEnchargeBinarySensorEntity(
|
||||
serial_number: str,
|
||||
) -> None:
|
||||
"""Init the Encharge base entity."""
|
||||
self.entity_description = description
|
||||
self.coordinator = coordinator
|
||||
assert serial_number is not None
|
||||
|
||||
self.envoy_serial_num = coordinator.envoy.serial_number
|
||||
assert self.envoy_serial_num is not None
|
||||
|
||||
super().__init__(coordinator, description)
|
||||
self._serial_number = serial_number
|
||||
self._attr_unique_id = f"{serial_number}_{description.key}"
|
||||
encharge_inventory = self.data.encharge_inventory
|
||||
@ -124,18 +196,76 @@ class EnvoyEnchargeBinarySensorEntity(
|
||||
via_device=(DOMAIN, self.envoy_serial_num),
|
||||
)
|
||||
|
||||
super().__init__(coordinator)
|
||||
|
||||
@property
|
||||
def data(self) -> EnvoyData:
|
||||
"""Return envoy data."""
|
||||
data = self.coordinator.envoy.data
|
||||
assert data is not None
|
||||
return data
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the Encharge binary_sensor."""
|
||||
encharge_inventory = self.data.encharge_inventory
|
||||
assert encharge_inventory is not None
|
||||
return self.entity_description.value_fn(encharge_inventory[self._serial_number])
|
||||
|
||||
|
||||
class EnvoyEnpowerBinarySensorEntity(EnvoyBaseBinarySensorEntity):
|
||||
"""Defines an Enpower binary_sensor entity."""
|
||||
|
||||
entity_description: EnvoyEnpowerBinarySensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: EnphaseUpdateCoordinator,
|
||||
description: EnvoyEnpowerBinarySensorEntityDescription,
|
||||
) -> None:
|
||||
"""Init the Enpower base entity."""
|
||||
super().__init__(coordinator, description)
|
||||
enpower = self.data.enpower
|
||||
assert enpower is not None
|
||||
self._serial_number = enpower.serial_number
|
||||
self._attr_unique_id = f"{self._serial_number}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._serial_number)},
|
||||
manufacturer="Enphase",
|
||||
model="Enpower",
|
||||
name=f"Enpower {self._serial_number}",
|
||||
sw_version=str(enpower.firmware_version),
|
||||
via_device=(DOMAIN, self.envoy_serial_num),
|
||||
)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the Enpower binary_sensor."""
|
||||
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: str,
|
||||
) -> None:
|
||||
"""Init the Enpower base entity."""
|
||||
super().__init__(coordinator, description)
|
||||
enpower = self.data.enpower
|
||||
assert enpower is not None
|
||||
self.relay = self.data.dry_contact_status[relay]
|
||||
self._serial_number = enpower.serial_number
|
||||
self._attr_unique_id = f"{self._serial_number}_relay_{self.relay.id}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._serial_number)},
|
||||
manufacturer="Enphase",
|
||||
model="Enpower",
|
||||
name=f"Enpower {self._serial_number}",
|
||||
sw_version=str(enpower.firmware_version),
|
||||
via_device=(DOMAIN, self.envoy_serial_num),
|
||||
)
|
||||
self._attr_name = (
|
||||
f"{self.data.dry_contact_settings[self.relay.id].load_name} Relay"
|
||||
)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the Enpower binary_sensor."""
|
||||
return self.relay.status == DryContactStatus.CLOSED
|
||||
|
@ -6,7 +6,7 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pyenphase"],
|
||||
"requirements": ["pyenphase==1.1.3"],
|
||||
"requirements": ["pyenphase==1.2.1"],
|
||||
"zeroconf": [
|
||||
{
|
||||
"type": "_enphase-envoy._tcp.local."
|
||||
|
@ -10,6 +10,7 @@ from pyenphase import (
|
||||
EnvoyData,
|
||||
EnvoyEncharge,
|
||||
EnvoyEnchargePower,
|
||||
EnvoyEnpower,
|
||||
EnvoyInverter,
|
||||
EnvoySystemConsumption,
|
||||
EnvoySystemProduction,
|
||||
@ -259,6 +260,36 @@ ENCHARGE_POWER_SENSORS = (
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnvoyEnpowerRequiredKeysMixin:
|
||||
"""Mixin for required keys."""
|
||||
|
||||
value_fn: Callable[[EnvoyEnpower], datetime.datetime | int | float]
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnvoyEnpowerSensorEntityDescription(
|
||||
SensorEntityDescription, EnvoyEnpowerRequiredKeysMixin
|
||||
):
|
||||
"""Describes an Envoy Encharge sensor entity."""
|
||||
|
||||
|
||||
ENPOWER_SENSORS = (
|
||||
EnvoyEnpowerSensorEntityDescription(
|
||||
key="temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
value_fn=lambda enpower: enpower.temperature,
|
||||
),
|
||||
EnvoyEnpowerSensorEntityDescription(
|
||||
key=LAST_REPORTED_KEY,
|
||||
translation_key=LAST_REPORTED_KEY,
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=lambda enpower: dt_util.utc_from_timestamp(enpower.last_report_date),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
@ -300,6 +331,11 @@ async def async_setup_entry(
|
||||
for description in ENCHARGE_POWER_SENSORS
|
||||
for encharge in envoy_data.encharge_power
|
||||
)
|
||||
if envoy_data.enpower:
|
||||
entities.extend(
|
||||
EnvoyEnpowerEntity(coordinator, description)
|
||||
for description in ENPOWER_SENSORS
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
@ -469,3 +505,38 @@ class EnvoyEnchargePowerEntity(EnvoyEnchargeEntity):
|
||||
encharge_power = self.data.encharge_power
|
||||
assert encharge_power is not None
|
||||
return self.entity_description.value_fn(encharge_power[self._serial_number])
|
||||
|
||||
|
||||
class EnvoyEnpowerEntity(EnvoyBaseEntity, SensorEntity):
|
||||
"""Envoy Enpower sensor entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: EnvoyEnpowerSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: EnphaseUpdateCoordinator,
|
||||
description: EnvoyEnpowerSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize Enpower entity."""
|
||||
super().__init__(coordinator, description)
|
||||
assert coordinator.envoy.data is not None
|
||||
enpower_data = coordinator.envoy.data.enpower
|
||||
assert enpower_data is not None
|
||||
self._serial_number = enpower_data.serial_number
|
||||
self._attr_unique_id = f"{self._serial_number}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._serial_number)},
|
||||
manufacturer="Enphase",
|
||||
model="Enpower",
|
||||
name=f"Enpower {self._serial_number}",
|
||||
sw_version=str(enpower_data.firmware_version),
|
||||
via_device=(DOMAIN, self.envoy_serial_num),
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self) -> datetime.datetime | int | float | None:
|
||||
"""Return the state of the power sensors."""
|
||||
enpower = self.data.enpower
|
||||
assert enpower is not None
|
||||
return self.entity_description.value_fn(enpower)
|
||||
|
@ -27,10 +27,13 @@
|
||||
"name": "Communicating"
|
||||
},
|
||||
"dc_switch": {
|
||||
"name": "DC Switch"
|
||||
"name": "DC switch"
|
||||
},
|
||||
"operating": {
|
||||
"name": "Operating"
|
||||
},
|
||||
"grid_status": {
|
||||
"name": "Grid status"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
|
@ -1662,7 +1662,7 @@ pyedimax==0.2.1
|
||||
pyefergy==22.1.1
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
pyenphase==1.1.3
|
||||
pyenphase==1.2.1
|
||||
|
||||
# homeassistant.components.envisalink
|
||||
pyenvisalink==4.6
|
||||
|
@ -1229,7 +1229,7 @@ pyeconet==0.1.20
|
||||
pyefergy==22.1.1
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
pyenphase==1.1.3
|
||||
pyenphase==1.2.1
|
||||
|
||||
# homeassistant.components.everlights
|
||||
pyeverlights==0.1.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user