Skip to content

Commit 7b5ec23

Browse files
Add firmware version sensors for all devices
1 parent 1dbbf01 commit 7b5ec23

File tree

3 files changed

+159
-67
lines changed

3 files changed

+159
-67
lines changed

custom_components/enphase_envoy/const.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,30 @@ def get_model_name(model, hardware_id):
290290
suggested_display_precision=0,
291291
entity_category=EntityCategory.DIAGNOSTIC,
292292
),
293+
SensorEntityDescription(
294+
key="envoy_software",
295+
name="Firmware Version",
296+
icon="mdi:memory",
297+
entity_category=EntityCategory.DIAGNOSTIC,
298+
),
299+
SensorEntityDescription(
300+
key="inverters_software",
301+
name="Firmware Version",
302+
icon="mdi:memory",
303+
entity_category=EntityCategory.DIAGNOSTIC,
304+
),
305+
SensorEntityDescription(
306+
key="relays_software",
307+
name="Firmware Version",
308+
icon="mdi:memory",
309+
entity_category=EntityCategory.DIAGNOSTIC,
310+
),
311+
SensorEntityDescription(
312+
key="batteries_software",
313+
name="Firmware Version",
314+
icon="mdi:memory",
315+
entity_category=EntityCategory.DIAGNOSTIC,
316+
),
293317
)
294318
ADDITIONAL_METRICS.extend(
295319
[

custom_components/enphase_envoy/envoy_endpoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Generic endpoints
33
"info": {
44
"url": "https://{}/info.xml",
5-
"cache": 86400,
5+
"cache": 3600,
66
"installer_required": False,
77
"optional": False,
88
},

custom_components/enphase_envoy/sensor.py

Lines changed: 134 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,38 @@ async def async_setup_entry(
6161
)
6262
)
6363

64+
elif sensor_description.key == "inverters_software":
65+
if coordinator.data.get("inverters_info") is not None:
66+
for serial_number in coordinator.data["inverters_production"]:
67+
device_name = f"Inverter {serial_number}"
68+
entities.append(
69+
EnvoyInverterFirmwareEntity(
70+
description=sensor_description,
71+
name=f"{device_name} {sensor_description.name}",
72+
device_name=device_name,
73+
device_serial_number=serial_number,
74+
serial_number=None,
75+
coordinator=coordinator,
76+
parent_device=config_entry.unique_id,
77+
)
78+
)
79+
80+
elif sensor_description.key == "relays_software":
81+
if coordinator.data.get("relays") != None:
82+
for serial_number in coordinator.data["relays"].keys():
83+
device_name = f"Relay {serial_number}"
84+
entities.append(
85+
EnvoyRelayFirmwareEntity(
86+
description=sensor_description,
87+
name=f"{device_name} {sensor_description.name}",
88+
device_name=device_name,
89+
device_serial_number=serial_number,
90+
serial_number=None,
91+
coordinator=coordinator,
92+
parent_device=config_entry.unique_id,
93+
)
94+
)
95+
6496
elif sensor_description.key == "inverters_communication_level":
6597
if coordinator.data.get("pcu_availability") is not None:
6698
for serial_number in coordinator.data["inverters_production"]:
@@ -114,6 +146,24 @@ async def async_setup_entry(
114146
)
115147
)
116148

149+
elif sensor_description.key == "batteries_software":
150+
if coordinator.data.get("batteries") is not None:
151+
for battery in coordinator.data["batteries"].keys():
152+
device_name = f"Battery {battery}"
153+
entity_name = f"{device_name} {sensor_description.name}"
154+
serial_number = battery
155+
entities.append(
156+
EnvoyBatteryFirmwareEntity(
157+
description=sensor_description,
158+
name=f"{device_name} {sensor_description.name}",
159+
device_name=device_name,
160+
device_serial_number=serial_number,
161+
serial_number=None,
162+
coordinator=coordinator,
163+
parent_device=config_entry.unique_id,
164+
)
165+
)
166+
117167
elif sensor_description.key.startswith("batteries_"):
118168
if coordinator.data.get("batteries") is not None:
119169
for battery in coordinator.data["batteries"].keys():
@@ -271,10 +321,7 @@ def device_info(self) -> DeviceInfo | None:
271321
)
272322

273323

274-
class EnvoyInverterEntity(CoordinatorEntity, SensorEntity):
275-
"""Envoy inverter entity."""
276-
277-
MODEL = "Inverter"
324+
class EnvoyDeviceEntity(CoordinatorEntity, SensorEntity):
278325

279326
def __init__(
280327
self,
@@ -307,6 +354,9 @@ def unique_id(self):
307354
if self._device_serial_number:
308355
return f"{self._device_serial_number}_{self.entity_description.key}"
309356

357+
358+
class EnvoyInverterEntity(EnvoyDeviceEntity):
359+
310360
@property
311361
def native_value(self):
312362
"""Return the state of the sensor."""
@@ -356,38 +406,47 @@ def device_info(self) -> DeviceInfo | None:
356406
if self._parent_device:
357407
device_info_kw["via_device"] = (DOMAIN, self._parent_device)
358408

359-
model_name = self.MODEL
360-
if self.MODEL == "Envoy":
361-
model = self.coordinator.data.get("envoy_info", {}).get("model", "Standard")
362-
model_name = f"Envoy-S {model}"
363-
364-
elif self.MODEL == "Inverter":
365-
if self.coordinator.data.get(
366-
"inverters_info"
367-
) and self.coordinator.data.get("inverters_info").get(
368-
self._device_serial_number
369-
):
370-
device_info_kw["sw_version"] = (
371-
self.coordinator.data.get("inverters_info")
372-
.get(self._device_serial_number)
373-
.get("img_pnum_running")
374-
)
375-
device_info_kw["hw_version"] = (
376-
self.coordinator.data.get("inverters_info")
377-
.get(self._device_serial_number)
378-
.get("part_num")
379-
)
380-
model_name = (get_model_name("Inverter", device_info_kw["hw_version"]),)
381-
382-
elif self.MODEL == "Relay":
383-
info = self.coordinator.data.get("relay_info", {}).get(
384-
self._device_serial_number, {}
409+
if self.coordinator.data.get("inverters_info") and self.coordinator.data.get(
410+
"inverters_info"
411+
).get(self._device_serial_number):
412+
device_info_kw["sw_version"] = (
413+
self.coordinator.data.get("inverters_info")
414+
.get(self._device_serial_number)
415+
.get("img_pnum_running")
385416
)
386-
device_info_kw["sw_version"] = info.get("img_pnum_running", None)
387-
device_info_kw["hw_version"] = resolve_hardware_id(
388-
info.get("part_num", None)
417+
device_info_kw["hw_version"] = (
418+
self.coordinator.data.get("inverters_info")
419+
.get(self._device_serial_number)
420+
.get("part_num")
389421
)
390-
model_name = get_model_name(model_name, info.get("part_num", None))
422+
model_name = (get_model_name("Inverter", device_info_kw["hw_version"]),)
423+
424+
return DeviceInfo(
425+
identifiers={(DOMAIN, str(self._device_serial_number))},
426+
manufacturer="Enphase",
427+
model=model_name,
428+
name=self._device_name,
429+
**device_info_kw,
430+
)
431+
432+
433+
class EnvoyRelayEntity(EnvoyDeviceEntity):
434+
435+
@property
436+
def device_info(self) -> DeviceInfo | None:
437+
"""Return the device_info of the device."""
438+
if not self._device_serial_number:
439+
return None
440+
device_info_kw = {}
441+
if self._parent_device:
442+
device_info_kw["via_device"] = (DOMAIN, self._parent_device)
443+
444+
info = self.coordinator.data.get("relay_info", {}).get(
445+
self._device_serial_number, {}
446+
)
447+
device_info_kw["sw_version"] = info.get("img_pnum_running", None)
448+
device_info_kw["hw_version"] = resolve_hardware_id(info.get("part_num", None))
449+
model_name = get_model_name("Relay", info.get("part_num", None))
391450

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

400459

401-
class EnvoyInverterSignalEntity(EnvoyInverterEntity):
460+
class EnvoySignalEntity(EnvoyDeviceEntity):
402461

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

426485

427-
class EnvoyRelaySignalEntity(EnvoyInverterSignalEntity):
428-
MODEL = "Relay"
486+
class EnvoyInverterFirmwareEntity(EnvoyInverterEntity):
429487

488+
@property
489+
def native_value(self) -> str:
490+
return (
491+
self.coordinator.data.get("inverters_info")
492+
.get(self._device_serial_number)
493+
.get("img_pnum_running")
494+
)
430495

431-
class EnvoyBatteryEntity(CoordinatorEntity, SensorEntity):
432-
"""Envoy battery entity."""
433496

434-
def __init__(
435-
self,
436-
description,
437-
name,
438-
device_name,
439-
device_serial_number,
440-
serial_number,
441-
coordinator,
442-
parent_device,
443-
):
444-
self.entity_description = description
445-
self._name = name
446-
self._serial_number = serial_number
447-
self._device_name = device_name
448-
self._device_serial_number = device_serial_number
449-
self._parent_device = parent_device
450-
CoordinatorEntity.__init__(self, coordinator)
497+
class EnvoyRelayFirmwareEntity(EnvoyRelayEntity):
451498

452499
@property
453-
def name(self):
454-
"""Return the name of the sensor."""
455-
return self._name
500+
def native_value(self) -> str:
501+
return (
502+
self.coordinator.data.get("relay_info", {})
503+
.get(self._device_serial_number, {})
504+
.get("img_pnum_running", None)
505+
)
506+
507+
508+
class EnvoyInverterSignalEntity(EnvoySignalEntity, EnvoyInverterEntity):
509+
pass
456510

457-
@property
458-
def unique_id(self):
459-
"""Return the unique id of the sensor."""
460-
if self._serial_number:
461-
return self._serial_number
462-
if self._device_serial_number:
463-
return f"{self._device_serial_number}_{self.entity_description.key}"
511+
512+
class EnvoyRelaySignalEntity(EnvoySignalEntity, EnvoyRelayEntity):
513+
pass
514+
515+
516+
class EnvoyBatteryEntity(EnvoyDeviceEntity):
517+
"""Envoy battery entity."""
464518

465519
@property
466520
def native_value(self):
@@ -530,3 +584,17 @@ def device_info(self) -> DeviceInfo | None:
530584
sw_version=sw_version,
531585
hw_version=resolve_hardware_id(hw_version),
532586
)
587+
588+
589+
class EnvoyBatteryFirmwareEntity(EnvoyBatteryEntity):
590+
591+
@property
592+
def native_value(self) -> str:
593+
if self.coordinator.data.get("batteries") and self.coordinator.data.get(
594+
"batteries"
595+
).get(self._device_serial_number):
596+
return (
597+
self.coordinator.data.get("batteries")
598+
.get(self._device_serial_number)
599+
.get("img_pnum_running")
600+
)

0 commit comments

Comments
 (0)