From 9ab4b4e7e88679dd7596e9d3a08d63852505d7c6 Mon Sep 17 00:00:00 2001 From: Vincent Wolsink Date: Thu, 2 Nov 2023 19:06:10 +0100 Subject: [PATCH] Add get_grid_profiles and set_grid_profile services --- custom_components/enphase_envoy/__init__.py | 34 ++++++++++++++++++- .../enphase_envoy/envoy_reader.py | 17 ++++++++++ custom_components/enphase_envoy/services.yaml | 8 +++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 custom_components/enphase_envoy/services.yaml diff --git a/custom_components/enphase_envoy/__init__.py b/custom_components/enphase_envoy/__init__.py index 5090c4f..5ae109a 100644 --- a/custom_components/enphase_envoy/__init__.py +++ b/custom_components/enphase_envoy/__init__.py @@ -20,7 +20,15 @@ CONF_USERNAME, EVENT_HOMEASSISTANT_STOP, ) -from homeassistant.core import HomeAssistant, callback, CoreState, Event +from homeassistant.core import ( + HomeAssistant, + callback, + CoreState, + Event, + ServiceCall, + ServiceResponse, + SupportsResponse, +) from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.storage import Store @@ -137,6 +145,30 @@ async def async_update_data(): ), ) + async def get_grid_profiles(call: ServiceCall) -> ServiceResponse: + return { + "selected_profile": coordinator.data.get("grid_profile"), + "available_profiles": [ + k["profile_id"] for k in coordinator.data.get("grid_profiles_available") + ], + } + + hass.services.async_register( + DOMAIN, + "get_grid_profiles", + get_grid_profiles, + supports_response=SupportsResponse.ONLY, + ) + + async def set_grid_profile(call: ServiceCall): + await envoy_reader.set_grid_profile(call.data["profile"]) + + hass.services.async_register( + DOMAIN, + "set_grid_profile", + set_grid_profile, + ) + @Throttle(time_between_realtime_updates) def update_production_meters(streamdata: StreamData): new_data = {} diff --git a/custom_components/enphase_envoy/envoy_reader.py b/custom_components/enphase_envoy/envoy_reader.py index 9663eed..8ee3f2f 100644 --- a/custom_components/enphase_envoy/envoy_reader.py +++ b/custom_components/enphase_envoy/envoy_reader.py @@ -36,6 +36,7 @@ ENDPOINT_URL_PRODUCTION_REPORT = "https://{}/ivp/meters/reports/production" ENDPOINT_URL_PDM_ENERGY = "https://{}/ivp/pdm/energy" ENDPOINT_URL_INSTALLER_AGF = "https://{}/installer/agf/index.json" +ENDPOINT_URL_INSTALLER_AGF_SET_PROFILE = "https://{}/installer/agf/set_profile.json" ENVOY_MODEL_M = "Metered" ENVOY_MODEL_S = "Standard" @@ -377,6 +378,7 @@ class EnvoyStandard(EnvoyData): envoy_update_status_value = "endpoint_home_json_results.update_status" serial_number_value = "endpoint_info_results.envoy_info.device.sn" grid_profile_value = "endpoint_installer_agf.selected_profile" + grid_profiles_available_value = "endpoint_installer_agf.profiles" @envoy_property() def envoy_info(self): @@ -1331,6 +1333,21 @@ async def set_production_power(self, power_on): # Make sure the next poll will update the endpoint. self._clear_endpoint_cache("endpoint_production_power") + async def set_grid_profile(self, profile_id): + if self.endpoint_installer_agf is not None: + formatted_url = ENDPOINT_URL_INSTALLER_AGF_SET_PROFILE.format(self.host) + resp = await self._async_put( + formatted_url, data={"selected_profile": profile_id} + ) + + if "accepted" not in resp.text: + raise EnvoyError( + f"Failed setting grid profile: {resp.json().get('message')} - {resp.json().get('reason')}" + ) + + self._clear_endpoint_cache("endpoint_installer_agf") + return resp + def run_stream(self): print("Reading stream...") loop = asyncio.get_event_loop() diff --git a/custom_components/enphase_envoy/services.yaml b/custom_components/enphase_envoy/services.yaml new file mode 100644 index 0000000..550864c --- /dev/null +++ b/custom_components/enphase_envoy/services.yaml @@ -0,0 +1,8 @@ +get_grid_profiles: +set_grid_profile: + fields: + profile: + required: true + example: "EN 50549-1:2019 RfG E02 Netherlands:1.2.4" + selector: + text: