Convert solarlog to coordinator (#55345)
parent
eb458fb1d5
commit
46d0523f98
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue