Skip to content

Commit

Permalink
Merge pull request #148 from catsmanac/Dev
Browse files Browse the repository at this point in the history
Merged #137 by @cddu33 with V0.0.16 Adding Import/Export Index from ivp/meters/readings
  • Loading branch information
catsmanac authored Aug 28, 2023
2 parents 86d5f3a + ce8c819 commit 2e2a96e
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 6 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ What data is available depends on how many current transformer clamps (CT) are i
- Current power production and consumption, today's, last 7 days and lifetime energy production and consumption over all phases.
- Current power production and consumption, today's, last 7 days and lifetime energy production and consumption for each individual phase named L1, L2 and L3.
- Current power production for each connected inverter.
- Energy Import and Export Index over all phases and for each phase individually from meters readings

**Note** If you have CT clamps on a single phase / breaker circuit only, the L1 production and consumption phase sensors will show same data as the over all phases sensors.

Expand Down Expand Up @@ -128,14 +129,18 @@ A device `Envoy <serialnumber>` is created with sensor entities for accessible d
|Envoy \<sn\> Last Seven Days Energy Consumption|sensor.Envoy_\<sn\>_last_seven_days_energy_consumption|Wh|4,5|
|Envoy \<sn\> Lifetime Energy Consumption|sensor.Envoy_\<sn\>_lifetime_energy_consumption|Wh|4,5|
|Grid Status |binary_sensor.grid_status|On/Off|3|
Envoy \<sn\> Current Power Production L\<n\>|sensor.Envoy_\<sn\>_current_power_production_L\<n\>|W|4,5|
|Envoy \<sn\> Current Power Production L\<n\>|sensor.Envoy_\<sn\>_current_power_production_L\<n\>|W|4,5|
|Envoy \<sn\> Today's Energy production L\<n\>|sensor.Envoy_\<sn\>_todays_energy_production_L\<n\>|Wh|4,5|
|Envoy \<sn\> Last Seven Days Energy Production L\<n\>|sensor.Envoy_\<sn\>_last_seven_days_energy_production L\<n\>|Wh|4,5|
|Envoy \<sn\> Lifetime Energy Production L\<n\>|sensor.Envoy_\<sn\>_lifetime_energy_consumption_L\<n\>|Wh|4,5|
|Envoy \<sn\> Current Power Consumption L\<n\>|sensor.Envoy_\<sn\>_current_power_consumption_L\<n\>|W|4,5|
|Envoy \<sn\> Today's Energy Consumption L\<n\>|sensor.Envoy_\<sn\>_todays_energy_consumption_L\<n\>|Wh|4,5,6|
|Envoy \<sn\> Last Seven Days Energy Consumption L\<n\>|sensor.Envoy_\<sn\>_last_seven_days_energy_consumption L\<n\>|Wh|4,5,6|
|Envoy \<sn\> Lifetime Energy Consumption L\<n\>|sensor.Envoy_\<sn\>_lifetime_energy_consumption_L\<n\>|Wh|4,5,6|
|Index Import|sensor.Envoy_\<sn\>_index_import|Wh|4,5|
|Index Export|sensor.Envoy_\<sn\>_index_export|Wh|4,5|
|Index Import L\<n\>|sensor.Envoy_\<sn\>_index_import_L\<n\>|Wh|4,5|
|Index Export L\<n\>|sensor.Envoy_\<sn\>_index_export_L\<n\>|Wh|4,5|

1 Always zero for Envoy Metered without meters.
2 Reportedly resets to zero when reaching ~1.92MWh for Envoy Metered without meters.
Expand Down
8 changes: 8 additions & 0 deletions custom_components/enphase_envoy_custom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ async def async_update_data():
data[
description.key
] = await envoy_reader.lifetime_consumption_phase(description.key)
elif description.key.startswith("import_index_"):
data[
description.key
] = await envoy_reader.import_index_phase(description.key)
elif description.key.startswith("export_index_"):
data[
description.key
] = await envoy_reader.export_index_phase(description.key)

data["grid_status"] = await envoy_reader.grid_status()
data["envoy_info"] = await envoy_reader.envoy_info()
Expand Down
56 changes: 56 additions & 0 deletions custom_components/enphase_envoy_custom/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY
),
SensorEntityDescription(
key="import_index",
name="Index Import",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="export_index",
name="Index Export",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
)

BINARY_SENSORS = (
Expand Down Expand Up @@ -243,6 +257,48 @@
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="import_index_l1",
name="Index Import L1",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="export_index_l1",
name="Index Export L1",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="import_index_l2",
name="Index Import L2",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="export_index_l2",
name="Index Export L2",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="import_index_l3",
name="Index Import L3",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
SensorEntityDescription(
key="export_index_l3",
name="Index Export L3",
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
device_class=SensorDeviceClass.ENERGY,
),
)

BATTERY_ENERGY_DISCHARGED_SENSOR = SensorEntityDescription(
Expand Down
88 changes: 85 additions & 3 deletions custom_components/enphase_envoy_custom/envoy_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
ENDPOINT_URL_ENSEMBLE_INVENTORY = "http{}://{}/ivp/ensemble/inventory"
ENDPOINT_URL_HOME_JSON = "http{}://{}/home.json"
ENDPOINT_URL_INFO_XML = "http{}://{}/info"
ENDPOINT_URL_METERS = "http{}://{}/ivp/meters/readings"

# pylint: disable=pointless-string-statement

Expand Down Expand Up @@ -84,6 +85,10 @@ class EnvoyReader: # pylint: disable=too-many-instance-attributes
"Grid status not available for your Envoy device."
)

message_import_export_not_available = (
"Import Export data not available for your Envoy device."
)

def __init__( # pylint: disable=too-many-arguments
self,
host,
Expand Down Expand Up @@ -119,6 +124,7 @@ def __init__( # pylint: disable=too-many-arguments
self.endpoint_production_json_results = None
self.endpoint_production_v1_results = None
self.endpoint_production_inverters = None
self.endpoint_meters_json_results = None
self.endpoint_production_results = None
self.endpoint_ensemble_json_results = None
self.endpoint_home_json_results = None
Expand Down Expand Up @@ -196,6 +202,9 @@ async def _update_from_pc_endpoint(self):
await self._update_endpoint(
"endpoint_ensemble_json_results", ENDPOINT_URL_ENSEMBLE_INVENTORY
)
await self._update_endpoint(
"endpoint_meters_json_results", ENDPOINT_URL_METERS
)
if self.has_grid_status:
await self._update_endpoint(
"endpoint_home_json_results", ENDPOINT_URL_HOME_JSON
Expand Down Expand Up @@ -902,6 +911,71 @@ async def battery_storage(self):

return raw_json["storage"][0]

async def import_index(self):
"""import index"""
"""Running getData() beforehand will set self.enpoint_type and self.isDataRetrieved"""
"""so that this method will only read data from stored variables"""
if self.endpoint_type in [ENVOY_MODEL_C,ENVOY_MODEL_LEGACY]:
return self.message_import_export_not_available

raw_json = self.endpoint_meters_json_results.json()
index_imp = raw_json[1]["actEnergyDlvd"]
return int(index_imp)

async def import_index_phase(self, phase):
"""import index"""
"""Running getData() beforehand will set self.enpoint_type and self.isDataRetrieved"""
"""so that this method will only read data from stored variables"""
phase_map = {"import_index_l1": 0, "import_index_l2": 1, "import_index_l3": 2}
if self.endpoint_type in [ENVOY_MODEL_C,ENVOY_MODEL_LEGACY]:
return None

raw_json = self.endpoint_meters_json_results.json()
if raw_json[1]["channels"][1]["voltage"] < 50:
return None
try:
return int(
raw_json[1]["channels"][phase_map[phase]]["actEnergyDlvd"]
)
except (KeyError, IndexError):
return None

return None

async def export_index(self):
"""import export"""
"""Running getData() beforehand will set self.enpoint_type and self.isDataRetrieved"""
"""so that this method will only read data from stored variables"""

if self.endpoint_type in [ENVOY_MODEL_C,ENVOY_MODEL_LEGACY]:
return self.message_import_export_not_available

raw_json = self.endpoint_meters_json_results.json()
index_exp = raw_json[1]['actEnergyRcvd']
return int(index_exp)

async def export_index_phase(self, phase):
"""import export"""
"""Running getData() beforehand will set self.enpoint_type and self.isDataRetrieved"""
"""so that this method will only read data from stored variables"""
phase_map = {"export_index_l1": 0, "export_index_l2": 1, "export_index_l3": 2}

if self.endpoint_type in [ENVOY_MODEL_C,ENVOY_MODEL_LEGACY]:
return None

raw_json = self.endpoint_meters_json_results.json()
if raw_json[1]["channels"][1]["voltage"] < 50:
return None

try:
return int(
raw_json[1]["channels"][phase_map[phase]]["actEnergyRcvd"]
)
except (KeyError, IndexError):
return None

return None

async def grid_status(self):
"""Return grid status reported by Envoy"""
if self.has_grid_status and self.endpoint_home_json_results is not None:
Expand Down Expand Up @@ -954,6 +1028,10 @@ async def envoy_info(self):
device_data["Endpoint-production"] = self.endpoint_production_results.text
else:
device_data["Endpoint-production"] = self.endpoint_production_results
if self.endpoint_meters_json_results:
device_data["Endpoint-meters"] = self.endpoint_meters_json_results.text
else:
device_data["Endpoint-meters"] = self.endpoint_meters_json_results
if self.endpoint_production_inverters:
device_data[
"Endpoint-production_inverters"
Expand Down Expand Up @@ -998,6 +1076,8 @@ def run_in_console(self):
self.seven_days_consumption(),
self.lifetime_production(),
self.lifetime_consumption(),
self.import_index(),
self.export_index(),
self.inverters_production(),
self.battery_storage(),
return_exceptions=False,
Expand All @@ -1012,17 +1092,19 @@ def run_in_console(self):
print(f"seven_days_consumption: {results[5]}")
print(f"lifetime_production: {results[6]}")
print(f"lifetime_consumption: {results[7]}")
print(f"index_import: {results[8]}")
print(f"index_export: {results[9]}")
if "401" in str(data_results):
print(
"inverters_production: Unable to retrieve inverter data - Authentication failure"
)
elif results[8] is None:
elif results[10] is None:
print(
"inverters_production: Inverter data not available for your Envoy device."
)
else:
print(f"inverters_production: {results[8]}")
print(f"battery_storage: {results[9]}")
print(f"inverters_production: {results[10]}")
print(f"battery_storage: {results[11]}")


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion custom_components/enphase_envoy_custom/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"codeowners": ["@briancmpbll"],
"config_flow": true,
"iot_class": "local_polling",
"version": "0.0.16"
"version": "0.0.17"
}
15 changes: 14 additions & 1 deletion custom_components/enphase_envoy_custom/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,18 @@
}
}
}
}
},
"options": {
"step": {
"user": {
"title": "Options Envoy",
"data": {
"data_interval": "Temps entre 2 rafraichissement [s]"
},
"data_description": {
"data_interval": "Temps entre 2 rafraichissement, minimum 5 sec. Relancer apres un changement"
}
}
}
}
}

0 comments on commit 2e2a96e

Please sign in to comment.