Skip to content

Commit

Permalink
Add extra sensors for relays
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentwolsink committed Nov 25, 2024
1 parent 49c8bb5 commit 18d18e2
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 9 deletions.
51 changes: 49 additions & 2 deletions custom_components/enphase_envoy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def get_model_name(model, hardware_id):
),
SensorEntityDescription(
key="inverter_data_ac_frequency",
name="Frequency",
name="AC Frequency",
native_unit_of_measurement=UnitOfFrequency.HERTZ,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.FREQUENCY,
Expand Down Expand Up @@ -232,7 +232,6 @@ def get_model_name(model, hardware_id):
device_class=SensorDeviceClass.ENERGY,
suggested_display_precision=0,
),
# This data is in attributes too, but seemed helpful to be in diagnostics
SensorEntityDescription(
key="inverter_data_last_reading",
name="Last Reading",
Expand All @@ -249,6 +248,7 @@ def get_model_name(model, hardware_id):
device_class=None,
suggested_display_precision=0,
entity_category=EntityCategory.DIAGNOSTIC,
icon="mdi:counter",
),
SensorEntityDescription(
key="inverter_data_conversion_error",
Expand Down Expand Up @@ -390,12 +390,59 @@ def get_model_name(model, hardware_id):
icon="mdi:memory",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="relay_data_state_change_count",
name="State Change Count",
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
icon="mdi:counter",
),
SensorEntityDescription(
key="relay_data_temperature",
name="Temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_display_precision=0,
),
SensorEntityDescription(
key="batteries_software",
name="Firmware Version",
icon="mdi:memory",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key=f"relay_data_voltage_l1",
name=f"Voltage L1",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.VOLTAGE,
suggested_display_precision=3,
),
SensorEntityDescription(
key=f"relay_data_voltage_l2",
name=f"Voltage L2",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.VOLTAGE,
suggested_display_precision=3,
),
SensorEntityDescription(
key=f"relay_data_voltage_l3",
name=f"Voltage L3",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.VOLTAGE,
suggested_display_precision=3,
),
SensorEntityDescription(
key="relay_data_last_reading",
name="Last Reading",
native_unit_of_measurement=None,
state_class=None,
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
ADDITIONAL_METRICS.extend(
[
Expand Down
32 changes: 28 additions & 4 deletions custom_components/enphase_envoy/envoy_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def parse_devicedata(data):
lifetime = channel["lifetime"]
last_reading = channel["lastReading"]
idd[value["sn"]] = {
"type": value["devName"],
"sn": value["sn"],
"active": value["active"],
"watts": channel["watts"]["now"],
Expand All @@ -94,6 +95,25 @@ def parse_devicedata(data):
"gone": value["modGone"],
"last_reading": last_reading["endDate"],
}
elif (
isinstance(value, dict)
and "devName" in value
and value["devName"] == "nsrb"
):
channel = value["channels"][0] # unclear when there might be a channel > 0
last_reading = channel["lastReading"]
idd[value["sn"]] = {
"type": value["devName"],
"sn": value["sn"],
"active": value["active"],
"temperature": last_reading["temperature"],
"voltage_l1": last_reading["VrmsL1N"] / 1000,
"voltage_l2": last_reading["VrmsL2N"] / 1000,
"voltage_l3": last_reading["VrmsL3N"] / 1000,
"state_change_count": last_reading["stateChngCnt"],
"gone": value["modGone"],
"last_reading": last_reading["endDate"],
}
return idd


Expand Down Expand Up @@ -418,6 +438,14 @@ def relay_info(self):
"serial_num",
)

@envoy_property(required_endpoint="endpoint_device_data")
def inverter_device_data(self):
return self._path_to_dict("endpoint_device_data.[?(@.type=='pcu')]", "sn")

@envoy_property(required_endpoint="endpoint_device_data")
def relay_device_data(self):
return self._path_to_dict("endpoint_device_data.[?(@.type=='nsrb')]", "sn")

@envoy_property(required_endpoint="endpoint_ensemble_inventory")
def batteries(self):
battery_data = self._resolve_path("endpoint_ensemble_inventory[0].devices")
Expand All @@ -441,10 +469,6 @@ def batteries(self):
def batteries_power(self):
return self._path_to_dict("endpoint_ensemble_power.devices:", "serial_num")

@envoy_property(required_endpoint="endpoint_device_data")
def inverter_device_data(self):
return self._resolve_path("endpoint_device_data")

@envoy_property(required_endpoint="endpoint_ensemble_power")
def agg_batteries_power(self):
batteries_data = self._resolve_path("endpoint_ensemble_power.devices:")
Expand Down
77 changes: 74 additions & 3 deletions custom_components/enphase_envoy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,30 @@ async def async_setup_entry(
)
)

elif sensor_description.key.startswith("relay_data_"):
_LOGGER.debug(f"Relay Data Sensor {sensor_description}")
if coordinator.data.get("relay_device_data"):
_LOGGER.debug(f"Relay Data Sensor DATA {sensor_description}")
for relay in coordinator.data["relay_device_data"].keys():
_LOGGER.debug(f"Relay Data Sensor DATA {relay}")
device_name = f"Relay {relay}"
serial_number = relay
entities.append(
EnvoyRelayEntity(
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("relay_info_"):
if coordinator.data.get("relay_info"):
for relay in coordinator.data["relay_info"].keys():
device_name = f"Relay {inverter}"
device_name = f"Relay {relay}"
serial_number = relay
entities.append(
EnvoyRelayEntity(
Expand Down Expand Up @@ -359,7 +379,7 @@ def native_value(self):
value = serial.get(self.entity_description.key[14:])
if self.entity_description.key.endswith("last_reading"):
return datetime.datetime.fromtimestamp(
value, tz=datetime.timezone.utc
int(value), tz=datetime.timezone.utc
)
if serial.get("gone", True):
return None
Expand Down Expand Up @@ -396,7 +416,7 @@ def extra_state_attributes(self):
value = serial.get("last_reading")
return {
"last_reported": datetime.datetime.fromtimestamp(
value, tz=datetime.timezone.utc
int(value), tz=datetime.timezone.utc
)
}

Expand Down Expand Up @@ -437,6 +457,57 @@ def device_info(self) -> DeviceInfo | None:

class EnvoyRelayEntity(EnvoyDeviceEntity):

@property
def native_value(self):
if self.entity_description.key.startswith("relay_data_"):
if self.coordinator.data.get("relay_device_data"):
serial = self.coordinator.data.get("relay_device_data").get(
self._device_serial_number
)
value = serial.get(self.entity_description.key[11:])
if self.entity_description.key.endswith("last_reading"):
return datetime.datetime.fromtimestamp(
int(value), tz=datetime.timezone.utc
)
if serial.get("gone", True):
return None
return value
elif self.entity_description.key.startswith("relay_info_"):
if self.coordinator.data.get("relay_info"):
return (
self.coordinator.data.get("relay_info")
.get(self._device_serial_number)
.get(self.entity_description.key[11:])
)

@property
def extra_state_attributes(self):
"""Return the state attributes."""
if self.entity_description.key.startswith("relay_data_"):
if self.coordinator.data.get("relay_device_data"):
value = (
self.coordinator.data.get("relay_device_data")
.get(self._device_serial_number)
.get("last_reading")
)
return {
"last_reported": datetime.datetime.fromtimestamp(
int(value), tz=datetime.timezone.utc
)
}
elif self.entity_description.key.startswith("relay_info_"):
if self.coordinator.data.get("relay_info"):
value = (
self.coordinator.data.get("relay_info")
.get(self._device_serial_number)
.get("last_rpt_date")
)
return {
"last_reported": datetime.datetime.fromtimestamp(
int(value), tz=datetime.timezone.utc
)
}

@property
def device_info(self) -> DeviceInfo | None:
"""Return the device_info of the device."""
Expand Down

0 comments on commit 18d18e2

Please sign in to comment.