mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +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 (
|
from pyenphase import (
|
||||||
EnvoyData,
|
EnvoyData,
|
||||||
EnvoyEncharge,
|
EnvoyEncharge,
|
||||||
|
EnvoyEnpower,
|
||||||
)
|
)
|
||||||
|
from pyenphase.models.dry_contacts import DryContactStatus
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
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(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -86,15 +129,50 @@ async def async_setup_entry(
|
|||||||
for encharge in envoy_data.encharge_inventory
|
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)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class EnvoyEnchargeBinarySensorEntity(
|
class EnvoyBaseBinarySensorEntity(
|
||||||
CoordinatorEntity[EnphaseUpdateCoordinator], BinarySensorEntity
|
CoordinatorEntity[EnphaseUpdateCoordinator], BinarySensorEntity
|
||||||
):
|
):
|
||||||
"""Defines a base envoy binary_sensor entity."""
|
"""Defines a base envoy binary_sensor entity."""
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
_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
|
entity_description: EnvoyEnchargeBinarySensorEntityDescription
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -104,13 +182,7 @@ class EnvoyEnchargeBinarySensorEntity(
|
|||||||
serial_number: str,
|
serial_number: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Init the Encharge base entity."""
|
"""Init the Encharge base entity."""
|
||||||
self.entity_description = description
|
super().__init__(coordinator, 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
|
|
||||||
|
|
||||||
self._serial_number = serial_number
|
self._serial_number = serial_number
|
||||||
self._attr_unique_id = f"{serial_number}_{description.key}"
|
self._attr_unique_id = f"{serial_number}_{description.key}"
|
||||||
encharge_inventory = self.data.encharge_inventory
|
encharge_inventory = self.data.encharge_inventory
|
||||||
@ -124,18 +196,76 @@ class EnvoyEnchargeBinarySensorEntity(
|
|||||||
via_device=(DOMAIN, self.envoy_serial_num),
|
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
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return the state of the Encharge binary_sensor."""
|
"""Return the state of the Encharge binary_sensor."""
|
||||||
encharge_inventory = self.data.encharge_inventory
|
encharge_inventory = self.data.encharge_inventory
|
||||||
assert encharge_inventory is not None
|
assert encharge_inventory is not None
|
||||||
return self.entity_description.value_fn(encharge_inventory[self._serial_number])
|
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",
|
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["pyenphase"],
|
"loggers": ["pyenphase"],
|
||||||
"requirements": ["pyenphase==1.1.3"],
|
"requirements": ["pyenphase==1.2.1"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_enphase-envoy._tcp.local."
|
"type": "_enphase-envoy._tcp.local."
|
||||||
|
@ -10,6 +10,7 @@ from pyenphase import (
|
|||||||
EnvoyData,
|
EnvoyData,
|
||||||
EnvoyEncharge,
|
EnvoyEncharge,
|
||||||
EnvoyEnchargePower,
|
EnvoyEnchargePower,
|
||||||
|
EnvoyEnpower,
|
||||||
EnvoyInverter,
|
EnvoyInverter,
|
||||||
EnvoySystemConsumption,
|
EnvoySystemConsumption,
|
||||||
EnvoySystemProduction,
|
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(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: ConfigEntry,
|
config_entry: ConfigEntry,
|
||||||
@ -300,6 +331,11 @@ async def async_setup_entry(
|
|||||||
for description in ENCHARGE_POWER_SENSORS
|
for description in ENCHARGE_POWER_SENSORS
|
||||||
for encharge in envoy_data.encharge_power
|
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)
|
async_add_entities(entities)
|
||||||
|
|
||||||
@ -469,3 +505,38 @@ class EnvoyEnchargePowerEntity(EnvoyEnchargeEntity):
|
|||||||
encharge_power = self.data.encharge_power
|
encharge_power = self.data.encharge_power
|
||||||
assert encharge_power is not None
|
assert encharge_power is not None
|
||||||
return self.entity_description.value_fn(encharge_power[self._serial_number])
|
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"
|
"name": "Communicating"
|
||||||
},
|
},
|
||||||
"dc_switch": {
|
"dc_switch": {
|
||||||
"name": "DC Switch"
|
"name": "DC switch"
|
||||||
},
|
},
|
||||||
"operating": {
|
"operating": {
|
||||||
"name": "Operating"
|
"name": "Operating"
|
||||||
|
},
|
||||||
|
"grid_status": {
|
||||||
|
"name": "Grid status"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sensor": {
|
"sensor": {
|
||||||
|
@ -1662,7 +1662,7 @@ pyedimax==0.2.1
|
|||||||
pyefergy==22.1.1
|
pyefergy==22.1.1
|
||||||
|
|
||||||
# homeassistant.components.enphase_envoy
|
# homeassistant.components.enphase_envoy
|
||||||
pyenphase==1.1.3
|
pyenphase==1.2.1
|
||||||
|
|
||||||
# homeassistant.components.envisalink
|
# homeassistant.components.envisalink
|
||||||
pyenvisalink==4.6
|
pyenvisalink==4.6
|
||||||
|
@ -1229,7 +1229,7 @@ pyeconet==0.1.20
|
|||||||
pyefergy==22.1.1
|
pyefergy==22.1.1
|
||||||
|
|
||||||
# homeassistant.components.enphase_envoy
|
# homeassistant.components.enphase_envoy
|
||||||
pyenphase==1.1.3
|
pyenphase==1.2.1
|
||||||
|
|
||||||
# homeassistant.components.everlights
|
# homeassistant.components.everlights
|
||||||
pyeverlights==0.1.0
|
pyeverlights==0.1.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user