From 64c594a709cb97a94cb6f75ef7d574f1025cadce Mon Sep 17 00:00:00 2001 From: Arie Catsman Date: Wed, 20 Sep 2023 14:55:46 +0200 Subject: [PATCH 1/5] Prepare for v0.0.19-Beta-1 test cycle. --- custom_components/enphase_envoy_custom/manifest.json | 4 ++-- hacs.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/enphase_envoy_custom/manifest.json b/custom_components/enphase_envoy_custom/manifest.json index 55054ac..7329f26 100644 --- a/custom_components/enphase_envoy_custom/manifest.json +++ b/custom_components/enphase_envoy_custom/manifest.json @@ -1,6 +1,6 @@ { "domain": "enphase_envoy", - "name": "Enphase Envoy (DEV)", + "name": "Enphase Envoy (DEV-TEST)", "documentation": "https://github.com/briancmpbll/home_assistant_custom_envoy#readme", "requirements": [ "pyjwt", @@ -11,5 +11,5 @@ "codeowners": ["@briancmpbll"], "config_flow": true, "iot_class": "local_polling", - "version": "0.0.18" + "version": "0.0.19-BETA-1" } diff --git a/hacs.json b/hacs.json index 2abb720..a3dd78c 100644 --- a/hacs.json +++ b/hacs.json @@ -1,5 +1,5 @@ { - "name": "Enphase Envoy (DEV)", + "name": "Enphase Envoy (DEV-TEST)", "render_readme": true, "content_in_root": false } From 33afd4e3415ed083d9d460930e4c4faff47c48aa Mon Sep 17 00:00:00 2001 From: Arie Catsman Date: Sun, 24 Sep 2023 11:21:58 +0200 Subject: [PATCH 2/5] Net consumption/production from meters readings Firmware d7.6.x is reporting net production as negative net consumption on ivp/meters/readings. Change to read these from ivp/meters/readings --- .../enphase_envoy_custom/envoy_reader.py | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/custom_components/enphase_envoy_custom/envoy_reader.py b/custom_components/enphase_envoy_custom/envoy_reader.py index cacd049..a74dab1 100644 --- a/custom_components/enphase_envoy_custom/envoy_reader.py +++ b/custom_components/enphase_envoy_custom/envoy_reader.py @@ -40,6 +40,7 @@ ENDPOINT_URL_INFO_XML = "http{}://{}/info" ENDPOINT_URL_METERS = "http{}://{}/ivp/meters" ENDPOINT_URL_METERS_REPORTS = "http{}://{}/ivp/meters/reports" +ENDPOINT_URL_METERS_READINGS = "http{}://{}/ivp/meters/readings" # pylint: disable=pointless-string-statement @@ -179,6 +180,7 @@ def __init__( # pylint: disable=too-many-arguments self.has_grid_status = True self.serial_number_last_six = None self.endpoint_meters_reports_json_results = None + self.endpoint_meters_readings_json_results = None self.endpoint_production_json_results = None self.endpoint_production_v1_results = None self.endpoint_production_inverters = None @@ -268,6 +270,14 @@ async def _update_from_meters_reports_endpoint(self): "endpoint_meters_reports_json_results", ENDPOINT_URL_METERS_REPORTS ) + async def _update_from_meters_readings_endpoint(self): + """Update from ivp/meters/readings endpoint.""" + if self.endpoint_type == ENVOY_MODEL_S: + #only touch meters reports if confirmed envoy model S, other type choke up on this request + await self._update_endpoint( + "endpoint_meters_readings_json_results", ENDPOINT_URL_METERS_READINGS + ) + async def _update_from_pc_endpoint(self,detectmode=False): """Update from PC endpoint.""" if not self._do_not_use_production_json or detectmode: @@ -352,6 +362,7 @@ async def _update_meters_endpoint(self): self.info_refresh_buffer_seconds, ) await self._update_from_meters_reports_endpoint() + await self._update_from_meters_readings_endpoint() async def _update_endpoint(self, attr, url): """Update a property from an endpoint.""" @@ -726,9 +737,46 @@ def create_json_errormessage(self): + "support the requested metric." ) + async def _meters_readings_value(self,field,report="net-consumption",phase=None): + """Extract value from meters readings json""" + report_map = {"production": 0, "net-consumption": 1, "total-consumption": 1} + + phase_map = {"l1": 0, "l2": 1, "l3": 2} + #meters readings is only available for ENVOY Metered with CT configured + if (self.endpoint_type == ENVOY_MODEL_S) and ( + #net-consumption requires consumption CT installed is Solar power included mode + (report == "net-consumption" + and self.isConsumptionMeteringEnabled + and self.net_consumption_meters_type) + # production data requires production CT installed + or (report == "production" and self.isProductionMeteringEnabled ) + #if at least consumption CT is installed total-consumption will be available even in Load only mode install + or (report == "total-consumption" + and self.isConsumptionMeteringEnabled + and not self.net_consumption_meters_type ) + ): + if self.endpoint_meters_readings_json_results: + raw_json = self.endpoint_meters_readings_json_results.json() + if phase == None: + try: + jsondata = raw_json[report_map[report]][field] + return jsondata + except (KeyError, IndexError): + return None + + #if production data requested and multiple phases are configured and requested phase is in count of configured phases return data or + #if consumption data requested and multiple phases are configured and requested phase is in count of configured phases return date + if ((self.production_meters_phase_count > 1 and phase_map[phase] < self.production_meters_phase_count and report=="production") + or (self.consumption_meters_phase_count > 1 and phase_map[phase] < self.consumption_meters_phase_count and report!="production")): + try: + jsondata = raw_json[report_map[report]]["channels"][phase_map[phase]][field] + return jsondata + except (KeyError, IndexError): + return None + return None - async def _meters_report_value(self,field,report="net-consumption",phase=None): - """Extract value from meters reports json if net-consumption meter is available""" + async def _meters_report_value(self,field,report="production",phase=None): + """Extract value from meters reports json if consumption meter is available""" report_map = {"production": 0, "net-consumption": 1, "total-consumption": 2} phase_map = {"l1": 0, "l2": 1, "l3": 2} #meters reports is only available for ENVOY Metered with CT configured @@ -804,7 +852,7 @@ async def consumption(self,phase=None): async def net_consumption(self,phase=None): """Report cumulative or phase Power consumption (to/from grid) from consumption CT meters report""" - jsondata = await self._meters_report_value("currW",report="net-consumption",phase=phase) + jsondata = await self._meters_readings_value("instantaneousDemand",report="net-consumption",phase=phase) if jsondata is None: return self.message_consumption_not_available if phase is None else None return int(jsondata) @@ -979,7 +1027,7 @@ async def lifetime_production_phase(self, phase): async def lifetime_net_production(self,phase=None): """Report cumulative or phase lifetime net production (exported to grid) from consumption CT meters report""" - jsondata = await self._meters_report_value("whRcvdCum",report="net-consumption",phase=phase) + jsondata = await self._meters_readings_value("actEnergyRcvd",report="net-consumption",phase=phase) if jsondata is None: return self.message_consumption_not_available if phase is None else None return int(jsondata) @@ -993,7 +1041,7 @@ async def lifetime_consumption(self,phase=None): async def lifetime_net_consumption(self,phase=None): """Report cumulative or phase lifetime net-consumption from consumption CT meters report""" - jsondata = await self._meters_report_value("whDlvdCum",report="net-consumption",phase=phase) + jsondata = await self._meters_readings_value("actEnergyDlvd",report="net-consumption",phase=phase) if jsondata is None: return self.message_consumption_not_available if phase is None else None return int(jsondata) From 4ec6d6c1b75c6d47e82bfda00e50f5ded64b62a3 Mon Sep 17 00:00:00 2001 From: Arie Catsman Date: Tue, 26 Sep 2023 12:05:32 +0200 Subject: [PATCH 3/5] Change lifetime Net production to TOTAL_INCREASING --- custom_components/enphase_envoy_custom/const.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/enphase_envoy_custom/const.py b/custom_components/enphase_envoy_custom/const.py index 65b43a6..5a02bff 100644 --- a/custom_components/enphase_envoy_custom/const.py +++ b/custom_components/enphase_envoy_custom/const.py @@ -66,7 +66,7 @@ key="lifetime_net_production", name="Lifetime Net Energy Production", native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL, + state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( @@ -204,7 +204,7 @@ key="lifetime_net_production_l1", name="Lifetime Net Energy Production L1", native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL, + state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( @@ -231,7 +231,7 @@ key="lifetime_net_production_l2", name="Lifetime Net Energy Production L2", native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL, + state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( @@ -258,7 +258,7 @@ key="lifetime_net_production_l3", name="Lifetime Net Energy Production L3", native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL, + state_class=SensorStateClass.TOTAL_INCREASING, device_class=SensorDeviceClass.ENERGY, ), SensorEntityDescription( From 5eca7aeff3fa33ccea1ddc67650ef0ac6df0f40a Mon Sep 17 00:00:00 2001 From: Arie Catsman Date: Tue, 26 Sep 2023 12:09:03 +0200 Subject: [PATCH 4/5] Add meters-readings to envoy info diagnostics In test section rename current Production/Consumption to production/consumption_Current --- .../enphase_envoy_custom/envoy_reader.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/custom_components/enphase_envoy_custom/envoy_reader.py b/custom_components/enphase_envoy_custom/envoy_reader.py index a74dab1..8236a7e 100644 --- a/custom_components/enphase_envoy_custom/envoy_reader.py +++ b/custom_components/enphase_envoy_custom/envoy_reader.py @@ -1166,6 +1166,10 @@ async def envoy_info(self): device_data["Endpoint-meters"] = self.endpoint_meters_json_results.text else: device_data["Endpoint-meters"] = self.endpoint_meters_json_results + if self.endpoint_meters_readings_json_results: + device_data["Endpoint-meters-readings"] = self.endpoint_meters_readings_json_results.text + else: + device_data["Endpoint-meters-readings"] = self.endpoint_meters_readings_json_results if self.endpoint_meters_reports_json_results: device_data["Endpoint-meters-reports"] = self.endpoint_meters_reports_json_results.text else: @@ -1245,8 +1249,8 @@ def run_in_console(self, dumpraw=False,loopcount=1,waittime=1): self.pf(), self.voltage(), self.frequency(), - self.current_consumption(), - self.current_production(), + self.consumption_Current(), + self.production_Current(), #get values for phase L2 self.production_phase("l2"), self.consumption("l2"), @@ -1260,8 +1264,8 @@ def run_in_console(self, dumpraw=False,loopcount=1,waittime=1): self.pf("l2"), self.voltage("l2"), self.frequency("l2"), - self.current_consumption("l2"), - self.current_production("l2"), + self.consumption_Current("l2"), + self.production_Current("l2"), return_exceptions=False, ) ) @@ -1282,8 +1286,8 @@ def run_in_console(self, dumpraw=False,loopcount=1,waittime=1): print(f"pf: {results[14]}") print(f"voltage: {results[15]}") print(f"frequency: {results[16]}") - print(f"current_consumption: {results[17]}") - print(f"current_production: {results[18]}") + print(f"consumption_Current: {results[17]}") + print(f"production_Current: {results[18]}") print("--Phase L2 values--") print(f"production: {results[19]}") print(f"consumption: {results[20]}") @@ -1297,8 +1301,8 @@ def run_in_console(self, dumpraw=False,loopcount=1,waittime=1): print(f"pf: {results[28]}") print(f"voltage: {results[29]}") print(f"frequency: {results[30]}") - print(f"current_consumption: {results[31]}") - print(f"current_production: {results[32]}") + print(f"consumption_Current: {results[31]}") + print(f"production_Current: {results[32]}") if "401" in str(data_results): print( "inverters_production: Unable to retrieve inverter data - Authentication failure" From 569e75be85391aff8199449931e595f2b3eac091 Mon Sep 17 00:00:00 2001 From: Arie Catsman Date: Tue, 26 Sep 2023 12:14:28 +0200 Subject: [PATCH 5/5] mark as 0.0.19 release --- custom_components/enphase_envoy_custom/manifest.json | 4 ++-- hacs.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/enphase_envoy_custom/manifest.json b/custom_components/enphase_envoy_custom/manifest.json index 7329f26..6c59c79 100644 --- a/custom_components/enphase_envoy_custom/manifest.json +++ b/custom_components/enphase_envoy_custom/manifest.json @@ -1,6 +1,6 @@ { "domain": "enphase_envoy", - "name": "Enphase Envoy (DEV-TEST)", + "name": "Enphase Envoy (DEV)", "documentation": "https://github.com/briancmpbll/home_assistant_custom_envoy#readme", "requirements": [ "pyjwt", @@ -11,5 +11,5 @@ "codeowners": ["@briancmpbll"], "config_flow": true, "iot_class": "local_polling", - "version": "0.0.19-BETA-1" + "version": "0.0.19" } diff --git a/hacs.json b/hacs.json index a3dd78c..2abb720 100644 --- a/hacs.json +++ b/hacs.json @@ -1,5 +1,5 @@ { - "name": "Enphase Envoy (DEV-TEST)", + "name": "Enphase Envoy (DEV)", "render_readme": true, "content_in_root": false }