hass-core/homeassistant/components/solax/sensor.py
Ville Skyttä b4bac0f7a0
Exception chaining and wrapping improvements (#39320)
* Remove unnecessary exception re-wraps

* Preserve exception chains on re-raise

We slap "from cause" to almost all possible cases here. In some cases it
could conceivably be better to do "from None" if we really want to hide
the cause. However those should be in the minority, and "from cause"
should be an improvement over the corresponding raise without a "from"
in all cases anyway.

The only case where we raise from None here is in plex, where the
exception for an original invalid SSL cert is not the root cause for
failure to validate a newly fetched one.

Follow local convention on exception variable names if there is a
consistent one, otherwise `err` to match with majority of codebase.

* Fix mistaken re-wrap in homematicip_cloud/hap.py

Missed the difference between HmipConnectionError and
HmipcConnectionError.

* Do not hide original error on plex new cert validation error

Original is not the cause for the new one, but showing old in the
traceback is useful nevertheless.
2020-08-28 13:50:32 +02:00

113 lines
3.2 KiB
Python

"""Support for Solax inverter via local API."""
import asyncio
from datetime import timedelta
import logging
from solax import real_time_api
from solax.inverter import InverterError
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, TEMP_CELSIUS
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
_LOGGER = logging.getLogger(__name__)
DEFAULT_PORT = 80
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_IP_ADDRESS): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
)
SCAN_INTERVAL = timedelta(seconds=30)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Platform setup."""
api = await real_time_api(config[CONF_IP_ADDRESS], config[CONF_PORT])
endpoint = RealTimeDataEndpoint(hass, api)
resp = await api.get_data()
serial = resp.serial_number
hass.async_add_job(endpoint.async_refresh)
async_track_time_interval(hass, endpoint.async_refresh, SCAN_INTERVAL)
devices = []
for sensor, (idx, unit) in api.inverter.sensor_map().items():
if unit == "C":
unit = TEMP_CELSIUS
uid = f"{serial}-{idx}"
devices.append(Inverter(uid, serial, sensor, unit))
endpoint.sensors = devices
async_add_entities(devices)
class RealTimeDataEndpoint:
"""Representation of a Sensor."""
def __init__(self, hass, api):
"""Initialize the sensor."""
self.hass = hass
self.api = api
self.ready = asyncio.Event()
self.sensors = []
async def async_refresh(self, now=None):
"""Fetch new state data for the sensor.
This is the only method that should fetch new data for Home Assistant.
"""
try:
api_response = await self.api.get_data()
self.ready.set()
except InverterError as err:
if now is not None:
self.ready.clear()
return
raise PlatformNotReady from err
data = api_response.data
for sensor in self.sensors:
if sensor.key in data:
sensor.value = data[sensor.key]
sensor.async_schedule_update_ha_state()
class Inverter(Entity):
"""Class for a sensor."""
def __init__(self, uid, serial, key, unit):
"""Initialize an inverter sensor."""
self.uid = uid
self.serial = serial
self.key = key
self.value = None
self.unit = unit
@property
def state(self):
"""State of this inverter attribute."""
return self.value
@property
def unique_id(self):
"""Return unique id."""
return self.uid
@property
def name(self):
"""Name of this inverter attribute."""
return f"Solax {self.serial} {self.key}"
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self.unit
@property
def should_poll(self):
"""No polling needed."""
return False