Skip to content

Commit

Permalink
Allow to disabled the usage of certain endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentwolsink committed Sep 4, 2024
1 parent 0dd75ab commit a874774
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 94 deletions.
1 change: 1 addition & 0 deletions custom_components/enphase_envoy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
enlighten_serial_num=config[CONF_SERIAL],
store=store,
disable_negative_production=options.get("disable_negative_production", False),
disabled_endpoints=options.get("disabled_endpoints", []),
)
await envoy_reader._sync_store(load=True)

Expand Down
17 changes: 17 additions & 0 deletions custom_components/enphase_envoy/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_validation as cv
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.network import is_ipv4_address, is_ipv6_address

Expand All @@ -31,6 +32,8 @@
ENABLE_ADDITIONAL_METRICS,
DEFAULT_GETDATA_TIMEOUT,
)
from .envoy_endpoints import ENVOY_ENDPOINTS


_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -271,6 +274,20 @@ async def async_step_user(self, user_input=None):
ENABLE_ADDITIONAL_METRICS,
default=self.config_entry.options.get(ENABLE_ADDITIONAL_METRICS, False),
): bool,
vol.Optional(
"disabled_endpoints",
description={
"suggested_value": self.config_entry.options.get(
"disabled_endpoints"
)
},
): cv.multi_select(
{
f"endpoint_{key}": key
for key, endpoint in ENVOY_ENDPOINTS.items()
if endpoint["optional"]
}
),
}
return self.async_show_form(step_id="user", data_schema=vol.Schema(schema))

Expand Down
124 changes: 98 additions & 26 deletions custom_components/enphase_envoy/envoy_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,104 @@
# Generic endpoints
ENDPOINT_URL_HOME_JSON = "https://{}/home.json"
ENDPOINT_URL_INFO_XML = "https://{}/info.xml"
ENVOY_ENDPOINTS = {
# Generic endpoints
"info": {
"url": "https://{}/info.xml",
"cache": 20,
"installer_required": False,
"optional": False,
},
# Production/consumption endpoints
"production_json": {
"url": "https://{}/production.json?details=1",
"cache": 0,
"installer_required": False,
"optional": False,
},
"production_v1": {
"url": "https://{}/api/v1/production",
"cache": 20,
"installer_required": False,
"optional": False,
},
"production_inverters": {
"url": "https://{}/api/v1/production/inverters",
"cache": 20,
"installer_required": False,
"optional": False,
},
"production_report": {
"url": "https://{}/ivp/meters/reports/production",
"cache": 0,
"installer_required": False,
"optional": False,
},
"production_power": {
"url": "https://{}/ivp/mod/603980032/mode/power",
"cache": 20,
"installer_required": False,
"optional": True,
},
"pdm_energy": {
"url": "https://{}/ivp/pdm/energy",
"cache": 20,
"installer_required": True,
"optional": False,
},
# Battery endpoints
"ensemble_inventory": {
"url": "https://{}/ivp/ensemble/inventory",
"cache": 20,
"installer_required": False,
"optional": True,
},
"ensemble_secctrl": {
"url": "https://{}/ivp/ensemble/secctrl",
"cache": 20,
"installer_required": False,
"optional": True,
},
"ensemble_power": {
"url": "https://{}/ivp/ensemble/power",
"cache": 20,
"installer_required": False,
"optional": True,
},
# Inverter endpoints
"inventory": {
"url": "https://{}/inventory.json",
"cache": 300,
"installer_required": False,
"optional": False,
},
"devstatus": {
"url": "https://{}/ivp/peb/devstatus",
"cache": 20,
"installer_required": True,
"optional": False,
},
"pcu_comm_status": {
"url": "https://{}/installer/pcu_comm_check",
"cache": 90,
"installer_required": True,
"optional": True,
},
# Netprofile endpoints
"installer_agf": {
"url": "https://{}/installer/agf/index.json",
"cache": 10,
"installer_required": True,
"optional": True,
},
# Tariff endpoints
"admin_tariff": {
"url": "https://{}/admin/lib/tariff",
"cache": 10,
"installer_required": False,
"optional": True,
},
}

# Production/consumption endpoints
ENDPOINT_URL_PRODUCTION_JSON = "https://{}/production.json?details=1"
ENDPOINT_URL_PRODUCTION_V1 = "https://{}/api/v1/production"
ENDPOINT_URL_PRODUCTION_INVERTERS = "https://{}/api/v1/production/inverters"
ENDPOINT_URL_PRODUCTION_REPORT = "https://{}/ivp/meters/reports/production"
ENDPOINT_URL_PRODUCTION_POWER = "https://{}/ivp/mod/603980032/mode/power"
ENDPOINT_URL_PDM_ENERGY = "https://{}/ivp/pdm/energy"
ENDPOINT_URL_STREAM = "https://{}/stream/meter"

# 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"
ENDPOINT_URL_DEVSTATUS = "https://{}/ivp/peb/devstatus"
ENDPOINT_URL_COMM_STATUS = "https://{}/installer/pcu_comm_check"

# Netprofile endpoints
ENDPOINT_URL_INSTALLER_AGF = "https://{}/installer/agf/index.json"
ENDPOINT_URL_INSTALLER_AGF_SET_PROFILE = "https://{}/installer/agf/set_profile.json"
ENDPOINT_URL_INSTALLER_AGF_UPLOAD_PROFILE = (
"https://{}/installer/agf/upload_profile_package"
)

# Tariff endpoints
ENDPOINT_URL_ADMIN_TARIFF = "https://{}/admin/lib/tariff"
59 changes: 21 additions & 38 deletions custom_components/enphase_envoy/envoy_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,10 @@
from json.decoder import JSONDecodeError

from .envoy_endpoints import (
ENDPOINT_URL_INFO_XML,
ENDPOINT_URL_PRODUCTION_JSON,
ENDPOINT_URL_PRODUCTION_V1,
ENDPOINT_URL_PRODUCTION_INVERTERS,
ENDPOINT_URL_PRODUCTION_REPORT,
ENDPOINT_URL_PRODUCTION_POWER,
ENDPOINT_URL_PDM_ENERGY,
ENVOY_ENDPOINTS,
ENDPOINT_URL_STREAM,
ENDPOINT_URL_ENSEMBLE_INVENTORY,
ENDPOINT_URL_ENSEMBLE_SECCTRL,
ENDPOINT_URL_ENSEMBLE_POWER,
ENDPOINT_URL_INVENTORY,
ENDPOINT_URL_COMM_STATUS,
ENDPOINT_URL_DEVSTATUS,
ENDPOINT_URL_INSTALLER_AGF,
ENDPOINT_URL_INSTALLER_AGF_SET_PROFILE,
ENDPOINT_URL_INSTALLER_AGF_UPLOAD_PROFILE,
ENDPOINT_URL_ADMIN_TARIFF,
)

ENVOY_MODEL_M = "Metered"
Expand Down Expand Up @@ -423,6 +409,8 @@ def grid_status(self):
def inverters_production(self):
# We will use the endpoint based on the token_type, which is automatically resolved by the inverters_data property
data = self.get("inverters_data")
if not data:
return

def iter():
if (
Expand Down Expand Up @@ -645,6 +633,7 @@ def __init__(
token_refresh_buffer_seconds=0,
store=None,
disable_negative_production=False,
disabled_endpoints=[],
):
"""Init the EnvoyReader."""
self.host = host.lower()
Expand All @@ -668,29 +657,11 @@ def __init__(

self.data: EnvoyData = EnvoyStandard(self)
self.required_endpoints = set() # in case we would need it..

def url(endpoint, *a, **kw):
return self.register_url(f"endpoint_{endpoint}", *a, **kw)

# iurl is for registering endpoints that require a installer token
iurl = partial(url, installer_required="installer")
self.disabled_endpoints = disabled_endpoints

self.uri_registry = {}
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, cache=20)
url("ensemble_secctrl", ENDPOINT_URL_ENSEMBLE_SECCTRL, cache=20)
url("ensemble_power", ENDPOINT_URL_ENSEMBLE_POWER, cache=20)
iurl("pcu_comm_status", ENDPOINT_URL_COMM_STATUS, cache=90)
iurl("devstatus", ENDPOINT_URL_DEVSTATUS, cache=20)
iurl("production_power", ENDPOINT_URL_PRODUCTION_POWER, cache=20)
url("info", ENDPOINT_URL_INFO_XML, cache=86400)
url("inventory", ENDPOINT_URL_INVENTORY, cache=300)
url("production_report", ENDPOINT_URL_PRODUCTION_REPORT, cache=0)
iurl("pdm_energy", ENDPOINT_URL_PDM_ENERGY)
iurl("installer_agf", ENDPOINT_URL_INSTALLER_AGF)
url("admin_tariff", ENDPOINT_URL_ADMIN_TARIFF, cache=20)
for key, endpoint in ENVOY_ENDPOINTS.items():
self.register_url(f"endpoint_{key}", **endpoint)

# If IPv6 address then enclose host in brackets
try:
Expand All @@ -708,12 +679,15 @@ def url(endpoint, *a, **kw):
self._store_data = {}
self._store_update_pending = False

def register_url(self, attr, uri, cache=10, installer_required=False):
def register_url(
self, attr, url, cache=10, installer_required=False, optional=False
):
self.uri_registry[attr] = {
"url": uri,
"url": url,
"cache_time": cache,
"last_fetch": 0,
"installer_required": installer_required,
"optional": optional,
}
setattr(self, attr, None)
return self.uri_registry[attr]
Expand Down Expand Up @@ -1090,6 +1064,15 @@ async def update_endpoints(self, endpoints=None):
_LOGGER.info("Updating endpoints %s", endpoints)
for endpoint in endpoints:
endpoint_settings = self.uri_registry.get(endpoint)

if endpoint_settings["optional"] and endpoint in self.disabled_endpoints:
_LOGGER.info(
"Skipping update of disabled %s: %s",
endpoint,
endpoint_settings["url"],
)
continue

if endpoint_settings == None:
_LOGGER.error(f"No settings found for uri {endpoint}")
continue
Expand Down
Loading

0 comments on commit a874774

Please sign in to comment.