mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 01:37:08 +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",
|
||||
"name": "Enphase Envoy",
|
||||
"documentation": "https://www.home-assistant.io/integrations/enphase_envoy",
|
||||
"requirements": ["envoy_reader==0.17.3"],
|
||||
"requirements": ["envoy_reader==0.18.2"],
|
||||
"codeowners": [
|
||||
"@gtdiehl"
|
||||
]
|
||||
|
@ -1,8 +1,11 @@
|
||||
"""Support for Enphase Envoy solar energy monitor."""
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import async_timeout
|
||||
from envoy_reader.envoy_reader import EnvoyReader
|
||||
import requests
|
||||
import httpx
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
@ -15,8 +18,13 @@ from homeassistant.const import (
|
||||
ENERGY_WATT_HOUR,
|
||||
POWER_WATT,
|
||||
)
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
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__)
|
||||
|
||||
@ -38,10 +46,11 @@ SENSORS = {
|
||||
"inverters": ("Envoy Inverter", POWER_WATT),
|
||||
}
|
||||
|
||||
|
||||
ICON = "mdi:flash"
|
||||
CONST_DEFAULT_HOST = "envoy"
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=60)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
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."""
|
||||
ip_address = config[CONF_IP_ADDRESS]
|
||||
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]
|
||||
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 = []
|
||||
# Iterate through the list of sensors
|
||||
for condition in monitored_conditions:
|
||||
if condition == "inverters":
|
||||
try:
|
||||
inverters = await envoy_reader.inverters_production()
|
||||
except requests.exceptions.HTTPError:
|
||||
_LOGGER.warning(
|
||||
"Authentication for Inverter data failed during setup: %s",
|
||||
ip_address,
|
||||
)
|
||||
continue
|
||||
|
||||
if isinstance(inverters, dict):
|
||||
for inverter in inverters:
|
||||
entities.append(
|
||||
Envoy(
|
||||
envoy_reader,
|
||||
condition,
|
||||
f"{name}{SENSORS[condition][0]} {inverter}",
|
||||
SENSORS[condition][1],
|
||||
)
|
||||
entity_name = ""
|
||||
if (
|
||||
condition == "inverters"
|
||||
and coordinator.data.get("inverters_production") is not None
|
||||
):
|
||||
for inverter in coordinator.data["inverters_production"]:
|
||||
entity_name = f"{name}{SENSORS[condition][0]} {inverter}"
|
||||
split_name = entity_name.split(" ")
|
||||
serial_number = split_name[-1]
|
||||
entities.append(
|
||||
Envoy(
|
||||
condition,
|
||||
entity_name,
|
||||
serial_number,
|
||||
SENSORS[condition][1],
|
||||
coordinator,
|
||||
)
|
||||
|
||||
else:
|
||||
)
|
||||
elif condition != "inverters":
|
||||
entity_name = f"{name}{SENSORS[condition][0]}"
|
||||
entities.append(
|
||||
Envoy(
|
||||
envoy_reader,
|
||||
condition,
|
||||
f"{name}{SENSORS[condition][0]}",
|
||||
entity_name,
|
||||
None,
|
||||
SENSORS[condition][1],
|
||||
coordinator,
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class Envoy(Entity):
|
||||
"""Implementation of the Enphase Envoy sensors."""
|
||||
class Envoy(CoordinatorEntity):
|
||||
"""Envoy entity."""
|
||||
|
||||
def __init__(self, envoy_reader, sensor_type, name, unit):
|
||||
"""Initialize the sensor."""
|
||||
self._envoy_reader = envoy_reader
|
||||
def __init__(self, sensor_type, name, serial_number, unit, coordinator):
|
||||
"""Initialize Envoy entity."""
|
||||
self._type = sensor_type
|
||||
self._name = name
|
||||
self._serial_number = serial_number
|
||||
self._unit_of_measurement = unit
|
||||
self._state = None
|
||||
self._last_reported = None
|
||||
|
||||
super().__init__(coordinator)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -121,7 +176,20 @@ class Envoy(Entity):
|
||||
@property
|
||||
def state(self):
|
||||
"""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
|
||||
def unit_of_measurement(self):
|
||||
@ -136,33 +204,13 @@ class Envoy(Entity):
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
if self._type == "inverters":
|
||||
return {"last_reported": self._last_reported}
|
||||
if (
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# homeassistant.components.enphase_envoy
|
||||
envoy_reader==0.17.3
|
||||
envoy_reader==0.18.2
|
||||
|
||||
# homeassistant.components.season
|
||||
ephem==3.7.7.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user