From c51170ef6de4c58c065b2db060bafc5cecba053e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 27 Aug 2018 15:05:36 +0200 Subject: [PATCH] Add Volkszaehler sensor (#16188) * Add Volkszaehler sensor * Update icons * Improve code --- .coveragerc | 1 + .../components/sensor/volkszaehler.py | 138 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 142 insertions(+) create mode 100644 homeassistant/components/sensor/volkszaehler.py diff --git a/.coveragerc b/.coveragerc index bb0be2d9433..449883265f6 100644 --- a/.coveragerc +++ b/.coveragerc @@ -759,6 +759,7 @@ omit = homeassistant/components/sensor/uscis.py homeassistant/components/sensor/vasttrafik.py homeassistant/components/sensor/viaggiatreno.py + homeassistant/components/sensor/volkszaehler.py homeassistant/components/sensor/waqi.py homeassistant/components/sensor/waze_travel_time.py homeassistant/components/sensor/whois.py diff --git a/homeassistant/components/sensor/volkszaehler.py b/homeassistant/components/sensor/volkszaehler.py new file mode 100644 index 00000000000..47aa580e3d4 --- /dev/null +++ b/homeassistant/components/sensor/volkszaehler.py @@ -0,0 +1,138 @@ +""" +Support for consuming values for the Volkszaehler API. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.volkszaehler/ +""" +from datetime import timedelta +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_HOST, CONF_NAME, CONF_PORT, CONF_MONITORED_CONDITIONS) +from homeassistant.exceptions import PlatformNotReady +from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +REQUIREMENTS = ['volkszaehler==0.1.2'] + +_LOGGER = logging.getLogger(__name__) + +CONF_UUID = 'uuid' + +DEFAULT_HOST = 'localhost' +DEFAULT_NAME = 'Volkszaehler' +DEFAULT_PORT = 80 + +MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1) + +SENSOR_TYPES = { + 'average': ['Average', 'W', 'mdi:power-off'], + 'consumption': ['Consumption', 'Wh', 'mdi:power-plug'], + 'max': ['Max', 'W', 'mdi:arrow-up'], + 'min': ['Min', 'W', 'mdi:arrow-down'], +} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_UUID): cv.string, + vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_MONITORED_CONDITIONS, default=['average']): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), +}) + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the Volkszaehler sensors.""" + from volkszaehler import Volkszaehler + + host = config[CONF_HOST] + name = config[CONF_NAME] + port = config[CONF_PORT] + uuid = config[CONF_UUID] + conditions = config[CONF_MONITORED_CONDITIONS] + + session = async_get_clientsession(hass) + vz_api = VolkszaehlerData( + Volkszaehler(hass.loop, session, uuid, host=host, port=port)) + + await vz_api.async_update() + + if vz_api.api.data is None: + raise PlatformNotReady + + dev = [] + for condition in conditions: + dev.append(VolkszaehlerSensor(vz_api, name, condition)) + + async_add_entities(dev, True) + + +class VolkszaehlerSensor(Entity): + """Implementation of a Volkszaehler sensor.""" + + def __init__(self, vz_api, name, sensor_type): + """Initialize the Volkszaehler sensor.""" + self.vz_api = vz_api + self._name = name + self.type = sensor_type + self._state = None + + @property + def name(self): + """Return the name of the sensor.""" + return '{} {}'.format(self._name, SENSOR_TYPES[self.type][0]) + + @property + def icon(self): + """Icon to use in the frontend, if any.""" + return SENSOR_TYPES[self.type][2] + + @property + def unit_of_measurement(self): + """Return the unit the value is expressed in.""" + return SENSOR_TYPES[self.type][1] + + @property + def available(self): + """Could the device be accessed during the last update call.""" + return self.vz_api.available + + @property + def state(self): + """Return the state of the resources.""" + return self._state + + async def async_update(self): + """Get the latest data from REST API.""" + await self.vz_api.async_update() + + if self.vz_api.api.data is not None: + self._state = round(getattr(self.vz_api.api, self.type), 2) + + +class VolkszaehlerData: + """The class for handling the data retrieval from the Volkszaehler API.""" + + def __init__(self, api): + """Initialize the data object.""" + self.api = api + self.available = True + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + async def async_update(self): + """Get the latest data from the Volkszaehler REST API.""" + from volkszaehler.exceptions import VolkszaehlerApiConnectionError + + try: + await self.api.get_data() + self.available = True + except VolkszaehlerApiConnectionError: + _LOGGER.error("Unable to fetch data from the Volkszaehler API") + self.available = False diff --git a/requirements_all.txt b/requirements_all.txt index 2a238a933b8..81de7219b60 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1431,6 +1431,9 @@ uvcclient==0.10.1 # homeassistant.components.climate.venstar venstarcolortouch==0.6 +# homeassistant.components.sensor.volkszaehler +volkszaehler==0.1.2 + # homeassistant.components.config.config_entries voluptuous-serialize==2.0.0