Skip to content

Commit

Permalink
Add firmware version sensors for all devices
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentwolsink committed Oct 28, 2024
1 parent 1dbbf01 commit 7b5ec23
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 67 deletions.
24 changes: 24 additions & 0 deletions custom_components/enphase_envoy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,30 @@ def get_model_name(model, hardware_id):
suggested_display_precision=0,
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="envoy_software",
name="Firmware Version",
icon="mdi:memory",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="inverters_software",
name="Firmware Version",
icon="mdi:memory",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="relays_software",
name="Firmware Version",
icon="mdi:memory",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="batteries_software",
name="Firmware Version",
icon="mdi:memory",
entity_category=EntityCategory.DIAGNOSTIC,
),
)
ADDITIONAL_METRICS.extend(
[
Expand Down
2 changes: 1 addition & 1 deletion custom_components/enphase_envoy/envoy_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Generic endpoints
"info": {
"url": "https://{}/info.xml",
"cache": 86400,
"cache": 3600,
"installer_required": False,
"optional": False,
},
Expand Down
200 changes: 134 additions & 66 deletions custom_components/enphase_envoy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,38 @@ async def async_setup_entry(
)
)

elif sensor_description.key == "inverters_software":
if coordinator.data.get("inverters_info") is not None:
for serial_number in coordinator.data["inverters_production"]:
device_name = f"Inverter {serial_number}"
entities.append(
EnvoyInverterFirmwareEntity(
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_software":
if coordinator.data.get("relays") != None:
for serial_number in coordinator.data["relays"].keys():
device_name = f"Relay {serial_number}"
entities.append(
EnvoyRelayFirmwareEntity(
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 == "inverters_communication_level":
if coordinator.data.get("pcu_availability") is not None:
for serial_number in coordinator.data["inverters_production"]:
Expand Down Expand Up @@ -114,6 +146,24 @@ async def async_setup_entry(
)
)

elif sensor_description.key == "batteries_software":
if coordinator.data.get("batteries") is not None:
for battery in coordinator.data["batteries"].keys():
device_name = f"Battery {battery}"
entity_name = f"{device_name} {sensor_description.name}"
serial_number = battery
entities.append(
EnvoyBatteryFirmwareEntity(
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("batteries_"):
if coordinator.data.get("batteries") is not None:
for battery in coordinator.data["batteries"].keys():
Expand Down Expand Up @@ -271,10 +321,7 @@ def device_info(self) -> DeviceInfo | None:
)


class EnvoyInverterEntity(CoordinatorEntity, SensorEntity):
"""Envoy inverter entity."""

MODEL = "Inverter"
class EnvoyDeviceEntity(CoordinatorEntity, SensorEntity):

def __init__(
self,
Expand Down Expand Up @@ -307,6 +354,9 @@ def unique_id(self):
if self._device_serial_number:
return f"{self._device_serial_number}_{self.entity_description.key}"


class EnvoyInverterEntity(EnvoyDeviceEntity):

@property
def native_value(self):
"""Return the state of the sensor."""
Expand Down Expand Up @@ -356,38 +406,47 @@ def device_info(self) -> DeviceInfo | None:
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"]),)

elif self.MODEL == "Relay":
info = self.coordinator.data.get("relay_info", {}).get(
self._device_serial_number, {}
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["sw_version"] = info.get("img_pnum_running", None)
device_info_kw["hw_version"] = resolve_hardware_id(
info.get("part_num", None)
device_info_kw["hw_version"] = (
self.coordinator.data.get("inverters_info")
.get(self._device_serial_number)
.get("part_num")
)
model_name = get_model_name(model_name, info.get("part_num", None))
model_name = (get_model_name("Inverter", device_info_kw["hw_version"]),)

return DeviceInfo(
identifiers={(DOMAIN, str(self._device_serial_number))},
manufacturer="Enphase",
model=model_name,
name=self._device_name,
**device_info_kw,
)


class EnvoyRelayEntity(EnvoyDeviceEntity):

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

info = self.coordinator.data.get("relay_info", {}).get(
self._device_serial_number, {}
)
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("Relay", info.get("part_num", None))

return DeviceInfo(
identifiers={(DOMAIN, str(self._device_serial_number))},
Expand All @@ -398,7 +457,7 @@ def device_info(self) -> DeviceInfo | None:
)


class EnvoyInverterSignalEntity(EnvoyInverterEntity):
class EnvoySignalEntity(EnvoyDeviceEntity):

@property
def icon(self):
Expand All @@ -424,43 +483,38 @@ def native_value(self) -> int:
return int(data.get(self._device_serial_number, 0))


class EnvoyRelaySignalEntity(EnvoyInverterSignalEntity):
MODEL = "Relay"
class EnvoyInverterFirmwareEntity(EnvoyInverterEntity):

@property
def native_value(self) -> str:
return (
self.coordinator.data.get("inverters_info")
.get(self._device_serial_number)
.get("img_pnum_running")
)

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

def __init__(
self,
description,
name,
device_name,
device_serial_number,
serial_number,
coordinator,
parent_device,
):
self.entity_description = description
self._name = name
self._serial_number = serial_number
self._device_name = device_name
self._device_serial_number = device_serial_number
self._parent_device = parent_device
CoordinatorEntity.__init__(self, coordinator)
class EnvoyRelayFirmwareEntity(EnvoyRelayEntity):

@property
def name(self):
"""Return the name of the sensor."""
return self._name
def native_value(self) -> str:
return (
self.coordinator.data.get("relay_info", {})
.get(self._device_serial_number, {})
.get("img_pnum_running", None)
)


class EnvoyInverterSignalEntity(EnvoySignalEntity, EnvoyInverterEntity):
pass

@property
def unique_id(self):
"""Return the unique id of the sensor."""
if self._serial_number:
return self._serial_number
if self._device_serial_number:
return f"{self._device_serial_number}_{self.entity_description.key}"

class EnvoyRelaySignalEntity(EnvoySignalEntity, EnvoyRelayEntity):
pass


class EnvoyBatteryEntity(EnvoyDeviceEntity):
"""Envoy battery entity."""

@property
def native_value(self):
Expand Down Expand Up @@ -530,3 +584,17 @@ def device_info(self) -> DeviceInfo | None:
sw_version=sw_version,
hw_version=resolve_hardware_id(hw_version),
)


class EnvoyBatteryFirmwareEntity(EnvoyBatteryEntity):

@property
def native_value(self) -> str:
if self.coordinator.data.get("batteries") and self.coordinator.data.get(
"batteries"
).get(self._device_serial_number):
return (
self.coordinator.data.get("batteries")
.get(self._device_serial_number)
.get("img_pnum_running")
)

0 comments on commit 7b5ec23

Please sign in to comment.