Skip to content

Commit

Permalink
:wqAdd communication signal level diagnostic for inverters and relays.
Browse files Browse the repository at this point in the history
  • Loading branch information
mnederlof committed Aug 5, 2024
1 parent 9490da8 commit 6193c32
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 17 deletions.
16 changes: 16 additions & 0 deletions custom_components/enphase_envoy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,22 @@ def get_model_name(model, hardware_id):
icon="mdi:transmission-tower",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="inverters_communication_level",
name="Communication Level",
device_class=SensorDeviceClass.POWER_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="relays_communication_level",
name="Communication Level",
device_class=SensorDeviceClass.POWER_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
ADDITIONAL_METRICS.extend(
[
Expand Down
1 change: 1 addition & 0 deletions custom_components/enphase_envoy/envoy_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Inverter endpoints
ENDPOINT_URL_INVENTORY = "https://{}/inventory.json"
ENDPOINT_URL_DEVSTATUS = "https://{}/ivp/peb/devstatus"
ENDPOINT_URL_COMM_STATUS = "https://{}/installer/pcu_comm_check"

# Netprofile endpoints
ENDPOINT_URL_INSTALLER_AGF = "https://{}/installer/agf/index.json"
Expand Down
4 changes: 4 additions & 0 deletions custom_components/enphase_envoy/envoy_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
ENDPOINT_URL_ENSEMBLE_SECCTRL,
ENDPOINT_URL_ENSEMBLE_POWER,
ENDPOINT_URL_INVENTORY,
ENDPOINT_URL_COMM_STATUS,
ENDPOINT_URL_DEVSTATUS,
ENDPOINT_URL_INSTALLER_AGF,
ENDPOINT_URL_INSTALLER_AGF_SET_PROFILE,
Expand Down Expand Up @@ -420,6 +421,8 @@ def grid_status(self):
installer="endpoint_devstatus.pcu[?(@.devType==1)]",
)

pcu_availability_value = "endpoint_pcu_comm_status"

@envoy_property
def inverters_production(self):
# We will use the endpoint based on the token_type, which is automatically resolved by the inverters_data property
Expand Down Expand Up @@ -684,6 +687,7 @@ def url(endpoint, *a, **kw):
url("ensemble_inventory", ENDPOINT_URL_ENSEMBLE_INVENTORY, cache=20)
url("ensemble_secctrl", ENDPOINT_URL_ENSEMBLE_SECCTRL, cache=20)
url("ensemble_power", ENDPOINT_URL_ENSEMBLE_POWER, cache=20)
iurl("pcu_comm_status", ENDPOINT_URL_COMM_STATUS, cache=90)
iurl("devstatus", ENDPOINT_URL_DEVSTATUS, cache=20)
iurl("production_power", ENDPOINT_URL_PRODUCTION_POWER, cache=20)
url("info", ENDPOINT_URL_INFO_XML, cache=86400)
Expand Down
122 changes: 105 additions & 17 deletions custom_components/enphase_envoy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,44 @@ async def async_setup_entry(
config_entry.unique_id,
)
)

elif sensor_description.key == "inverters_communication_level":
if coordinator.data.get("pcu_availability") is not None:
for inverter in coordinator.data["inverters_production"]:
device_name = f"Inverter {inverter}"
entity_name = f"{device_name} {sensor_description.name}"
serial_number = inverter
entities.append(
EnvoyInverterSignalEntity(
description=sensor_description,
name=f"{device_name} {sensor_description.name}",
device_name=device_name,
device_serial_number=serial_number,
serial_number=None,
coordinator=coordinator,
parent_device=config_entry.unique_id,
)
)

elif sensor_description.key == "relays_communication_level":
if (
coordinator.data.get("relays") != None
and coordinator.data.get("pcu_availability") is not None
):
for serial_number in coordinator.data["relays"].keys():
device_name = f"Relay {serial_number}"
entities.append(
EnvoyRelaySignalEntity(
description=sensor_description,
name=f"{device_name} {sensor_description.name}",
device_name=device_name,
device_serial_number=serial_number,
serial_number=None,
coordinator=coordinator,
parent_device=config_entry.unique_id,
)
)

elif sensor_description.key.startswith("inverters_"):
if coordinator.data.get("inverters_status") is not None:
for inverter in coordinator.data["inverters_status"].keys():
Expand Down Expand Up @@ -238,6 +276,8 @@ def device_info(self) -> DeviceInfo | None:
class EnvoyInverterEntity(CoordinatorEntity, SensorEntity):
"""Envoy inverter entity."""

MODEL = "Inverter"

def __init__(
self,
description,
Expand Down Expand Up @@ -314,34 +354,82 @@ def device_info(self) -> DeviceInfo | None:
"""Return the device_info of the device."""
if not self._device_serial_number:
return None
device_info_kw = {}
if self._parent_device:
device_info_kw["via_device"] = (DOMAIN, self._parent_device)

model_name = self.MODEL
if self.MODEL == "Envoy":
model = self.coordinator.data.get("envoy_info", {}).get("model", "Standard")
model_name = f"Envoy-S {model}"

elif self.MODEL == "Inverter":
if self.coordinator.data.get(
"inverters_info"
) and self.coordinator.data.get("inverters_info").get(
self._device_serial_number
):
device_info_kw["sw_version"] = (
self.coordinator.data.get("inverters_info")
.get(self._device_serial_number)
.get("img_pnum_running")
)
device_info_kw["hw_version"] = (
self.coordinator.data.get("inverters_info")
.get(self._device_serial_number)
.get("part_num")
)
model_name = (get_model_name("Inverter", device_info_kw["hw_version"]),)

sw_version = None
hw_version = None
if self.coordinator.data.get("inverters_info") and self.coordinator.data.get(
"inverters_info"
).get(self._device_serial_number):
sw_version = (
self.coordinator.data.get("inverters_info")
.get(self._device_serial_number)
.get("img_pnum_running")
elif self.MODEL == "Relay":
info = self.coordinator.data.get("relay_info", {}).get(
self._device_serial_number, {}
)
hw_version = (
self.coordinator.data.get("inverters_info")
.get(self._device_serial_number)
.get("part_num")
device_info_kw["sw_version"] = info.get("img_pnum_running", None)
device_info_kw["hw_version"] = resolve_hardware_id(
info.get("part_num", None)
)
model_name = get_model_name(model_name, info.get("part_num", None))

return DeviceInfo(
identifiers={(DOMAIN, str(self._device_serial_number))},
manufacturer="Enphase",
model=get_model_name("Inverter", hw_version),
model=model_name,
name=self._device_name,
via_device=(DOMAIN, self._parent_device),
sw_version=sw_version,
hw_version=resolve_hardware_id(hw_version),
**device_info_kw,
)


class EnvoyInverterSignalEntity(EnvoyInverterEntity):

@property
def icon(self):
return {
5: "mdi:wifi-strength-4",
4: "mdi:wifi-strength-3",
3: "mdi:wifi-strength-2",
2: "mdi:wifi-strength-1",
1: "mdi:wifi-strength-outline",
0: "mdi:wifi-strength-off-outline",
}.get(self.native_value)

@property
def extra_state_attributes(self):
return None

@property
def native_value(self) -> int:
"""Return the status of the requested attribute."""
data = self.coordinator.data.get("pcu_availability")
if data is None:
return 0
return int(data.get(self._device_serial_number, 0))


class EnvoyRelaySignalEntity(EnvoyInverterSignalEntity):
MODEL = "Relay"


class EnvoyBatteryEntity(CoordinatorEntity, SensorEntity):
"""Envoy battery entity."""

Expand Down
14 changes: 14 additions & 0 deletions test_data/envoy_metered/endpoint_pcu_comm_check.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{"999999913010": 5,
"999999913012": 5,
"999999912750": 5,
"999999912983": 5,
"999999908520": 5,
"999999909983": 5,
"999999908521": 3,
"999999912669": 4,
"999999913748": 5,
"999999909985": 5,
"999999915285": 5,
"999999915246": 5,
"999999912590": 5,
"999999910862": 5}

0 comments on commit 6193c32

Please sign in to comment.