mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 18:27:09 +00:00
Optimize api calls between envoy_reader and Home Assistant (#42857)
* Updating sensor to use single API call * Updated comment Updated comment to reflect that polling is needed. * Reduced calls to single API call * Added except handling and increased async timeout * Cleaned up some comments * Added error handling * Added last_reported date for inverters * Added message during failed update * Added retries to update function * Updated update function * Reformatted sensor.py with black * Increased default scan period * fixed timedelta typo * importing CoordinatorEntity * Check during setup else raise PlatformNotReady * Removed async_update and override state * using SCAN_INTERVAL constant * fixed typo * removed unused constant * Removed retry logic * Changed to catching exceptions rather than strings * shortened string split line * Replace requests_async with httpx * Bump envoy_reader version to 0.17.2 * Resolving comments from PR requested changes * Fixed typo in scan_interval * Removed period from logging messages * Bumping envoy_reader to 0.18.0 * Incorporating suggested changes * Removing no longer used try/except * Fail setup if authentication fails * Bump envoy_reader to 0.18.2
This commit is contained in:
parent
0d8ed9061c
commit
a5cd4efd83
@ -2,7 +2,7 @@
|
|||||||
"domain": "enphase_envoy",
|
"domain": "enphase_envoy",
|
||||||
"name": "Enphase Envoy",
|
"name": "Enphase Envoy",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
|
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
|
||||||
"requirements": ["envoy_reader==0.17.3"],
|
"requirements": ["envoy_reader==0.18.2"],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
"@gtdiehl"
|
"@gtdiehl"
|
||||||
]
|
]
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
"""Support for Enphase Envoy solar energy monitor."""
|
"""Support for Enphase Envoy solar energy monitor."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import async_timeout
|
||||||
from envoy_reader.envoy_reader import EnvoyReader
|
from envoy_reader.envoy_reader import EnvoyReader
|
||||||
import requests
|
import httpx
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
@ -15,8 +18,13 @@ from homeassistant.const import (
|
|||||||
ENERGY_WATT_HOUR,
|
ENERGY_WATT_HOUR,
|
||||||
POWER_WATT,
|
POWER_WATT,
|
||||||
)
|
)
|
||||||
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.update_coordinator import (
|
||||||
|
CoordinatorEntity,
|
||||||
|
DataUpdateCoordinator,
|
||||||
|
UpdateFailed,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -38,10 +46,11 @@ SENSORS = {
|
|||||||
"inverters": ("Envoy Inverter", POWER_WATT),
|
"inverters": ("Envoy Inverter", POWER_WATT),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ICON = "mdi:flash"
|
ICON = "mdi:flash"
|
||||||
CONST_DEFAULT_HOST = "envoy"
|
CONST_DEFAULT_HOST = "envoy"
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=60)
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_IP_ADDRESS, default=CONST_DEFAULT_HOST): cv.string,
|
vol.Optional(CONF_IP_ADDRESS, default=CONST_DEFAULT_HOST): cv.string,
|
||||||
@ -55,7 +64,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
async def async_setup_platform(
|
||||||
|
homeassistant, config, async_add_entities, discovery_info=None
|
||||||
|
):
|
||||||
"""Set up the Enphase Envoy sensor."""
|
"""Set up the Enphase Envoy sensor."""
|
||||||
ip_address = config[CONF_IP_ADDRESS]
|
ip_address = config[CONF_IP_ADDRESS]
|
||||||
monitored_conditions = config[CONF_MONITORED_CONDITIONS]
|
monitored_conditions = config[CONF_MONITORED_CONDITIONS]
|
||||||
@ -63,55 +74,99 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
username = config[CONF_USERNAME]
|
username = config[CONF_USERNAME]
|
||||||
password = config[CONF_PASSWORD]
|
password = config[CONF_PASSWORD]
|
||||||
|
|
||||||
envoy_reader = EnvoyReader(ip_address, username, password)
|
if "inverters" in monitored_conditions:
|
||||||
|
envoy_reader = EnvoyReader(ip_address, username, password, inverters=True)
|
||||||
|
else:
|
||||||
|
envoy_reader = EnvoyReader(ip_address, username, password)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await envoy_reader.getData()
|
||||||
|
except httpx.HTTPStatusError as err:
|
||||||
|
_LOGGER.error("Authentication failure during setup: %s", err)
|
||||||
|
return
|
||||||
|
except httpx.HTTPError as err:
|
||||||
|
raise PlatformNotReady from err
|
||||||
|
|
||||||
|
async def async_update_data():
|
||||||
|
"""Fetch data from API endpoint."""
|
||||||
|
data = {}
|
||||||
|
async with async_timeout.timeout(30):
|
||||||
|
try:
|
||||||
|
await envoy_reader.getData()
|
||||||
|
except httpx.HTTPError as err:
|
||||||
|
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
||||||
|
|
||||||
|
for condition in monitored_conditions:
|
||||||
|
if condition != "inverters":
|
||||||
|
data[condition] = await getattr(envoy_reader, condition)()
|
||||||
|
else:
|
||||||
|
data["inverters_production"] = await getattr(
|
||||||
|
envoy_reader, "inverters_production"
|
||||||
|
)()
|
||||||
|
|
||||||
|
_LOGGER.debug("Retrieved data from API: %s", data)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
coordinator = DataUpdateCoordinator(
|
||||||
|
homeassistant,
|
||||||
|
_LOGGER,
|
||||||
|
name="sensor",
|
||||||
|
update_method=async_update_data,
|
||||||
|
update_interval=SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
await coordinator.async_refresh()
|
||||||
|
|
||||||
|
if coordinator.data is None:
|
||||||
|
raise PlatformNotReady
|
||||||
|
|
||||||
entities = []
|
entities = []
|
||||||
# Iterate through the list of sensors
|
|
||||||
for condition in monitored_conditions:
|
for condition in monitored_conditions:
|
||||||
if condition == "inverters":
|
entity_name = ""
|
||||||
try:
|
if (
|
||||||
inverters = await envoy_reader.inverters_production()
|
condition == "inverters"
|
||||||
except requests.exceptions.HTTPError:
|
and coordinator.data.get("inverters_production") is not None
|
||||||
_LOGGER.warning(
|
):
|
||||||
"Authentication for Inverter data failed during setup: %s",
|
for inverter in coordinator.data["inverters_production"]:
|
||||||
ip_address,
|
entity_name = f"{name}{SENSORS[condition][0]} {inverter}"
|
||||||
)
|
split_name = entity_name.split(" ")
|
||||||
continue
|
serial_number = split_name[-1]
|
||||||
|
entities.append(
|
||||||
if isinstance(inverters, dict):
|
Envoy(
|
||||||
for inverter in inverters:
|
condition,
|
||||||
entities.append(
|
entity_name,
|
||||||
Envoy(
|
serial_number,
|
||||||
envoy_reader,
|
SENSORS[condition][1],
|
||||||
condition,
|
coordinator,
|
||||||
f"{name}{SENSORS[condition][0]} {inverter}",
|
|
||||||
SENSORS[condition][1],
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
else:
|
elif condition != "inverters":
|
||||||
|
entity_name = f"{name}{SENSORS[condition][0]}"
|
||||||
entities.append(
|
entities.append(
|
||||||
Envoy(
|
Envoy(
|
||||||
envoy_reader,
|
|
||||||
condition,
|
condition,
|
||||||
f"{name}{SENSORS[condition][0]}",
|
entity_name,
|
||||||
|
None,
|
||||||
SENSORS[condition][1],
|
SENSORS[condition][1],
|
||||||
|
coordinator,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class Envoy(Entity):
|
class Envoy(CoordinatorEntity):
|
||||||
"""Implementation of the Enphase Envoy sensors."""
|
"""Envoy entity."""
|
||||||
|
|
||||||
def __init__(self, envoy_reader, sensor_type, name, unit):
|
def __init__(self, sensor_type, name, serial_number, unit, coordinator):
|
||||||
"""Initialize the sensor."""
|
"""Initialize Envoy entity."""
|
||||||
self._envoy_reader = envoy_reader
|
|
||||||
self._type = sensor_type
|
self._type = sensor_type
|
||||||
self._name = name
|
self._name = name
|
||||||
|
self._serial_number = serial_number
|
||||||
self._unit_of_measurement = unit
|
self._unit_of_measurement = unit
|
||||||
self._state = None
|
|
||||||
self._last_reported = None
|
super().__init__(coordinator)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -121,7 +176,20 @@ class Envoy(Entity):
|
|||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return self._state
|
if self._type != "inverters":
|
||||||
|
value = self.coordinator.data.get(self._type)
|
||||||
|
|
||||||
|
elif (
|
||||||
|
self._type == "inverters"
|
||||||
|
and self.coordinator.data.get("inverters_production") is not None
|
||||||
|
):
|
||||||
|
value = self.coordinator.data.get("inverters_production").get(
|
||||||
|
self._serial_number
|
||||||
|
)[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_of_measurement(self):
|
def unit_of_measurement(self):
|
||||||
@ -136,33 +204,13 @@ class Envoy(Entity):
|
|||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes."""
|
"""Return the state attributes."""
|
||||||
if self._type == "inverters":
|
if (
|
||||||
return {"last_reported": self._last_reported}
|
self._type == "inverters"
|
||||||
|
and self.coordinator.data.get("inverters_production") is not None
|
||||||
|
):
|
||||||
|
value = self.coordinator.data.get("inverters_production").get(
|
||||||
|
self._serial_number
|
||||||
|
)[1]
|
||||||
|
return {"last_reported": value}
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def async_update(self):
|
|
||||||
"""Get the energy production data from the Enphase Envoy."""
|
|
||||||
if self._type != "inverters":
|
|
||||||
_state = await getattr(self._envoy_reader, self._type)()
|
|
||||||
if isinstance(_state, int):
|
|
||||||
self._state = _state
|
|
||||||
else:
|
|
||||||
_LOGGER.error(_state)
|
|
||||||
self._state = None
|
|
||||||
|
|
||||||
elif self._type == "inverters":
|
|
||||||
try:
|
|
||||||
inverters = await (self._envoy_reader.inverters_production())
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
_LOGGER.warning(
|
|
||||||
"Authentication for Inverter data failed during update: %s",
|
|
||||||
self._envoy_reader.host,
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(inverters, dict):
|
|
||||||
serial_number = self._name.split(" ")[2]
|
|
||||||
self._state = inverters[serial_number][0]
|
|
||||||
self._last_reported = inverters[serial_number][1]
|
|
||||||
else:
|
|
||||||
self._state = None
|
|
||||||
|
@ -556,7 +556,7 @@ env_canada==0.2.4
|
|||||||
# envirophat==0.0.6
|
# envirophat==0.0.6
|
||||||
|
|
||||||
# homeassistant.components.enphase_envoy
|
# homeassistant.components.enphase_envoy
|
||||||
envoy_reader==0.17.3
|
envoy_reader==0.18.2
|
||||||
|
|
||||||
# homeassistant.components.season
|
# homeassistant.components.season
|
||||||
ephem==3.7.7.0
|
ephem==3.7.7.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user