Skip to content

Commit

Permalink
Extended battery support
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentwolsink committed Apr 16, 2024
1 parent f001868 commit 9785525
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 69 deletions.
38 changes: 30 additions & 8 deletions custom_components/enphase_envoy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,16 @@ def get_model_name(model, hardware_id):
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
),
SensorEntityDescription(
key="batteries_power",
name="Power",
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER,
),
SensorEntityDescription(
key="batteries_percentFull",
name="Percentage Full",
name="Charged",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.BATTERY,
Expand All @@ -193,35 +200,44 @@ def get_model_name(model, hardware_id):
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="batteries_encharge_capacity_current",
name="Current Capacity",
key="batteries_encharge_available_energy",
name="Available Energy",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="avg_batteries_percentFull",
name="Batteries Percentage Full",
key="agg_batteries_soc",
name="Batteries Charged",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.BATTERY,
),
SensorEntityDescription(
key="total_batteries_encharge_capacity",
key="agg_batteries_capacity",
name="Batteries Capacity",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="total_batteries_encharge_capacity_current",
name="Batteries Current Capacity",
key="agg_batteries_available_energy",
name="Batteries Available Energy",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="agg_batteries_power",
name="Batteries Power",
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER,
),
SensorEntityDescription(
key="voltage",
name="Current Voltage",
Expand Down Expand Up @@ -420,6 +436,12 @@ def get_model_name(model, hardware_id):
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
),
BinarySensorEntityDescription(
key="batteries_dc_switch_off",
name="DC Switch",
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
),
)

PRODUCION_POWER_SWITCH = SwitchEntityDescription(
Expand Down
23 changes: 20 additions & 3 deletions custom_components/enphase_envoy/envoy_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

# Battery endpoints
ENDPOINT_URL_ENSEMBLE_INVENTORY = "https://{}/ivp/ensemble/inventory"
ENDPOINT_URL_ENSEMBLE_SECCTRL = "https://{}/ivp/ensemble/secctrl"
ENDPOINT_URL_ENSEMBLE_POWER = "https://{}/ivp/ensemble/power"

# Inverter endpoints
ENDPOINT_URL_INVENTORY = "https://{}/inventory.json"
Expand Down Expand Up @@ -509,14 +511,27 @@ def batteries(self):
"%Y-%m-%d %H:%M:%S", time.localtime(item["last_rpt_date"])
)
if "encharge_capacity" in item and "percentFull" in item:
item["encharge_capacity_current"] = item["encharge_capacity"] * (
item["encharge_available_energy"] = item["encharge_capacity"] * (
item["percentFull"] / 100
)

battery_dict[item["serial_num"]] = item

return battery_dict

@envoy_property(required_endpoint="endpoint_ensemble_power")
def batteries_power(self):
return self._path_to_dict("endpoint_ensemble_power.devices:", "serial_num")

@envoy_property(required_endpoint="endpoint_ensemble_power")
def agg_batteries_power(self):
batteries_data = self._resolve_path("endpoint_ensemble_power.devices:")
if batteries_data:
return int(sum(batt['real_power_mw'] for batt in batteries_data) / 1000)

agg_batteries_capacity_value = "endpoint_ensemble_secctrl.Enc_max_available_capacity"
agg_batteries_soc_value = "endpoint_ensemble_secctrl.ENC_agg_soc"
agg_batteries_available_energy_value = "endpoint_ensemble_secctrl.ENC_agg_avail_energy"


class EnvoyMetered(EnvoyStandard):
"""
Expand Down Expand Up @@ -667,7 +682,9 @@ def url(endpoint, *a, **kw):
url("production_json", ENDPOINT_URL_PRODUCTION_JSON, cache=0)
url("production_v1", ENDPOINT_URL_PRODUCTION_V1, cache=20)
url("production_inverters", ENDPOINT_URL_PRODUCTION_INVERTERS, cache=20)
url("ensemble_inventory", ENDPOINT_URL_ENSEMBLE_INVENTORY)
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)
# cache for home_json will be set based on grid_status availability
url("home_json", ENDPOINT_URL_HOME_JSON)
iurl("devstatus", ENDPOINT_URL_DEVSTATUS, cache=20)
Expand Down
73 changes: 15 additions & 58 deletions custom_components/enphase_envoy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,26 +96,11 @@ async def async_setup_entry(
)
)

elif sensor_description.key.startswith("total_batteries_"):
elif sensor_description.key.startswith("agg_batteries_"):
if coordinator.data.get("batteries") is not None:
entity_name = f"{name} {sensor_description.name}"
entities.append(
TotalBatteriesEntity(
sensor_description,
entity_name,
name,
config_entry.unique_id,
None,
coordinator,
config_entry.data[CONF_HOST],
)
)

elif sensor_description.key.startswith("avg_batteries_"):
if coordinator.data.get("batteries") is not None:
entity_name = f"{name} {sensor_description.name}"
entities.append(
AvgBatteriesEntity(
CoordinatedEnvoyEntity(
sensor_description,
entity_name,
name,
Expand Down Expand Up @@ -395,11 +380,19 @@ def unique_id(self):
def native_value(self):
"""Return the state of the sensor."""
if self.coordinator.data.get("batteries") is not None:
return (
self.coordinator.data.get("batteries")
.get(self._device_serial_number)
.get(self.entity_description.key[10:])
)
if self.entity_description.key == "batteries_power":
return int(
self.coordinator.data.get("batteries_power")
.get(self._device_serial_number)
.get("real_power_mw")
/ 1000
)
else:
return (
self.coordinator.data.get("batteries")
.get(self._device_serial_number)
.get(self.entity_description.key[10:])
)

return None

Expand Down Expand Up @@ -445,39 +438,3 @@ def device_info(self) -> DeviceInfo | None:
sw_version=sw_version,
hw_version=resolve_hardware_id(hw_version),
)


class AvgBatteriesEntity(CoordinatedEnvoyEntity):
@property
def native_value(self):
"""Return the state of the sensor."""
if self.coordinator.data.get("batteries") is not None:
avg_value = 0
for battery in self.coordinator.data.get("batteries").keys():
avg_value += (
self.coordinator.data.get("batteries")
.get(battery)
.get(self.entity_description.key[14:])
)

return avg_value / len(self.coordinator.data.get("batteries"))

return None


class TotalBatteriesEntity(CoordinatedEnvoyEntity):
@property
def native_value(self):
"""Return the state of the sensor."""
if self.coordinator.data.get("batteries") is not None:
total_value = 0
for battery in self.coordinator.data.get("batteries").keys():
total_value += (
self.coordinator.data.get("batteries")
.get(battery)
.get(self.entity_description.key[16:])
)

return total_value

return None
22 changes: 22 additions & 0 deletions test_data/envoy_metered/endpoint_ensemble_power.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"devices:": [
{
"serial_num": "122323085065",
"real_power_mw": 179000,
"apparent_power_mva": 179000,
"soc": 89
},
{
"serial_num": "122323085067",
"real_power_mw": 190000,
"apparent_power_mva": 190000,
"soc": 89
},
{
"serial_num": "122323085069",
"real_power_mw": 183000,
"apparent_power_mva": 183000,
"soc": 89
}
]
}
27 changes: 27 additions & 0 deletions test_data/envoy_metered/endpoint_ensemble_secctrl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"shutdown": false,
"freq_bias_hz": 0.19460000097751618,
"voltage_bias_v": 2.365999937057495,
"freq_bias_hz_q8": 313,
"voltage_bias_v_q5": 75,
"freq_bias_hz_phaseb": 0.0,
"voltage_bias_v_phaseb": 0.0,
"freq_bias_hz_q8_phaseb": 0,
"voltage_bias_v_q5_phaseb": 0,
"freq_bias_hz_phasec": 0.0,
"voltage_bias_v_phasec": 0.0,
"freq_bias_hz_q8_phasec": 0,
"voltage_bias_v_q5_phasec": 0,
"configured_backup_soc": 0,
"adjusted_backup_soc": 0,
"agg_soc": 89,
"Max_energy": 10500,
"ENC_agg_soc": 89,
"ENC_agg_soh": 100,
"ENC_agg_backup_energy": 0,
"ENC_agg_avail_energy": 9345,
"Enc_commissioned_capacity": 10500,
"Enc_max_available_capacity": 10500,
"ACB_agg_soc": 0,
"ACB_agg_energy": 0
}

0 comments on commit 9785525

Please sign in to comment.