Convert solarlog to coordinator (#55345)

pull/55497/head
Paulus Schoutsen 2021-08-27 14:59:55 -07:00 committed by GitHub
parent eb458fb1d5
commit 46d0523f98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 117 deletions

View File

@ -1,12 +1,28 @@
"""Solar-Log integration."""
from datetime import timedelta
import logging
from urllib.parse import ParseResult, urlparse
from requests.exceptions import HTTPError, Timeout
from sunwatcher.solarlog.solarlog import SolarLog
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.helpers import update_coordinator
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor"]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry for solarlog."""
coordinator = SolarlogData(hass, entry)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
@ -14,3 +30,73 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass, entry):
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
class SolarlogData(update_coordinator.DataUpdateCoordinator):
"""Get and update the latest data."""
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Initialize the data object."""
super().__init__(
hass, _LOGGER, name="SolarLog", update_interval=timedelta(seconds=60)
)
host_entry = entry.data[CONF_HOST]
url = urlparse(host_entry, "http")
netloc = url.netloc or url.path
path = url.path if url.netloc else ""
url = ParseResult("http", netloc, path, *url[3:])
self.unique_id = entry.entry_id
self.name = entry.title
self.host = url.geturl()
async def _async_update_data(self):
"""Update the data from the SolarLog device."""
try:
api = await self.hass.async_add_executor_job(SolarLog, self.host)
except (OSError, Timeout, HTTPError) as err:
raise update_coordinator.UpdateFailed(err)
if api.time.year == 1999:
raise update_coordinator.UpdateFailed(
"Invalid data returned (can happen after Solarlog restart)."
)
self.logger.debug(
"Connection to Solarlog successful. Retrieving latest Solarlog update of %s",
api.time,
)
data = {}
try:
data["TIME"] = api.time
data["powerAC"] = api.power_ac
data["powerDC"] = api.power_dc
data["voltageAC"] = api.voltage_ac
data["voltageDC"] = api.voltage_dc
data["yieldDAY"] = api.yield_day / 1000
data["yieldYESTERDAY"] = api.yield_yesterday / 1000
data["yieldMONTH"] = api.yield_month / 1000
data["yieldYEAR"] = api.yield_year / 1000
data["yieldTOTAL"] = api.yield_total / 1000
data["consumptionAC"] = api.consumption_ac
data["consumptionDAY"] = api.consumption_day / 1000
data["consumptionYESTERDAY"] = api.consumption_yesterday / 1000
data["consumptionMONTH"] = api.consumption_month / 1000
data["consumptionYEAR"] = api.consumption_year / 1000
data["consumptionTOTAL"] = api.consumption_total / 1000
data["totalPOWER"] = api.total_power
data["alternatorLOSS"] = api.alternator_loss
data["CAPACITY"] = round(api.capacity * 100, 0)
data["EFFICIENCY"] = round(api.efficiency * 100, 0)
data["powerAVAILABLE"] = api.power_available
data["USAGE"] = round(api.usage * 100, 0)
except AttributeError as err:
raise update_coordinator.UpdateFailed(
f"Missing details data in Solarlog response: {err}"
) from err
_LOGGER.debug("Updated Solarlog overview data: %s", data)
return data

View File

@ -2,7 +2,6 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
from homeassistant.components.sensor import (
STATE_CLASS_MEASUREMENT,
@ -23,13 +22,10 @@ from homeassistant.const import (
DOMAIN = "solarlog"
"""Default config for solarlog."""
# Default config for solarlog.
DEFAULT_HOST = "http://solar-log"
DEFAULT_NAME = "solarlog"
"""Fixed constants."""
SCAN_INTERVAL = timedelta(seconds=60)
@dataclass
class SolarlogRequiredKeysMixin:

View File

@ -1,133 +1,42 @@
"""Platform for solarlog sensors."""
import logging
from urllib.parse import ParseResult, urlparse
from requests.exceptions import HTTPError, Timeout
from sunwatcher.solarlog.solarlog import SolarLog
from homeassistant.components.sensor import SensorEntity
from homeassistant.const import CONF_HOST
from homeassistant.util import Throttle
from homeassistant.helpers import update_coordinator
from homeassistant.helpers.entity import StateType
from .const import DOMAIN, SCAN_INTERVAL, SENSOR_TYPES, SolarLogSensorEntityDescription
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the solarlog platform."""
_LOGGER.warning(
"Configuration of the solarlog platform in configuration.yaml is deprecated "
"in Home Assistant 0.119. Please remove entry from your configuration"
)
from . import SolarlogData
from .const import DOMAIN, SENSOR_TYPES, SolarLogSensorEntityDescription
async def async_setup_entry(hass, entry, async_add_entities):
"""Add solarlog entry."""
host_entry = entry.data[CONF_HOST]
device_name = entry.title
url = urlparse(host_entry, "http")
netloc = url.netloc or url.path
path = url.path if url.netloc else ""
url = ParseResult("http", netloc, path, *url[3:])
host = url.geturl()
try:
api = await hass.async_add_executor_job(SolarLog, host)
_LOGGER.debug("Connected to Solar-Log device, setting up entries")
except (OSError, HTTPError, Timeout):
_LOGGER.error(
"Could not connect to Solar-Log device at %s, check host ip address", host
)
return
# Create solarlog data service which will retrieve and update the data.
data = await hass.async_add_executor_job(SolarlogData, hass, api, host)
# Create a new sensor for each sensor type.
entities = [
SolarlogSensor(entry.entry_id, device_name, data, description)
for description in SENSOR_TYPES
]
async_add_entities(entities, True)
return True
coordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities(
SolarlogSensor(coordinator, description) for description in SENSOR_TYPES
)
class SolarlogData:
"""Get and update the latest data."""
def __init__(self, hass, api, host):
"""Initialize the data object."""
self.api = api
self.hass = hass
self.host = host
self.update = Throttle(SCAN_INTERVAL)(self._update)
self.data = {}
def _update(self):
"""Update the data from the SolarLog device."""
try:
self.api = SolarLog(self.host)
response = self.api.time
_LOGGER.debug(
"Connection to Solarlog successful. Retrieving latest Solarlog update of %s",
response,
)
except (OSError, Timeout, HTTPError):
_LOGGER.error("Connection error, Could not retrieve data, skipping update")
return
try:
self.data["TIME"] = self.api.time
self.data["powerAC"] = self.api.power_ac
self.data["powerDC"] = self.api.power_dc
self.data["voltageAC"] = self.api.voltage_ac
self.data["voltageDC"] = self.api.voltage_dc
self.data["yieldDAY"] = self.api.yield_day / 1000
self.data["yieldYESTERDAY"] = self.api.yield_yesterday / 1000
self.data["yieldMONTH"] = self.api.yield_month / 1000
self.data["yieldYEAR"] = self.api.yield_year / 1000
self.data["yieldTOTAL"] = self.api.yield_total / 1000
self.data["consumptionAC"] = self.api.consumption_ac
self.data["consumptionDAY"] = self.api.consumption_day / 1000
self.data["consumptionYESTERDAY"] = self.api.consumption_yesterday / 1000
self.data["consumptionMONTH"] = self.api.consumption_month / 1000
self.data["consumptionYEAR"] = self.api.consumption_year / 1000
self.data["consumptionTOTAL"] = self.api.consumption_total / 1000
self.data["totalPOWER"] = self.api.total_power
self.data["alternatorLOSS"] = self.api.alternator_loss
self.data["CAPACITY"] = round(self.api.capacity * 100, 0)
self.data["EFFICIENCY"] = round(self.api.efficiency * 100, 0)
self.data["powerAVAILABLE"] = self.api.power_available
self.data["USAGE"] = round(self.api.usage * 100, 0)
_LOGGER.debug("Updated Solarlog overview data: %s", self.data)
except AttributeError:
_LOGGER.error("Missing details data in Solarlog response")
class SolarlogSensor(SensorEntity):
class SolarlogSensor(update_coordinator.CoordinatorEntity, SensorEntity):
"""Representation of a Sensor."""
entity_description: SolarLogSensorEntityDescription
def __init__(
self,
entry_id: str,
device_name: str,
data: SolarlogData,
coordinator: SolarlogData,
description: SolarLogSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.entity_description = description
self.data = data
self._attr_name = f"{device_name} {description.name}"
self._attr_unique_id = f"{entry_id}_{description.key}"
self._attr_name = f"{coordinator.name} {description.name}"
self._attr_unique_id = f"{coordinator.unique_id}_{description.key}"
self._attr_device_info = {
"identifiers": {(DOMAIN, entry_id)},
"name": device_name,
"identifiers": {(DOMAIN, coordinator.unique_id)},
"name": coordinator.name,
"manufacturer": "Solar-Log",
}
def update(self):
"""Get the latest data from the sensor and update the state."""
self.data.update()
self._attr_native_value = self.data.data[self.entity_description.json_key]
@property
def native_value(self) -> StateType:
"""Return the native sensor value."""
return self.coordinator.data[self.entity_description.json_key]