diff --git a/CODEOWNERS b/CODEOWNERS index 40d41b28790a..0069ff34e96e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -143,6 +143,7 @@ homeassistant/components/life360/* @pnbruckner homeassistant/components/lifx/* @amelchio homeassistant/components/lifx_cloud/* @amelchio homeassistant/components/lifx_legacy/* @amelchio +homeassistant/components/linky/* @tiste @Quentame homeassistant/components/linux_battery/* @fabaff homeassistant/components/liveboxplaytv/* @pschmitt homeassistant/components/logger/* @home-assistant/core diff --git a/homeassistant/components/linky/manifest.json b/homeassistant/components/linky/manifest.json index 706962b5c4d3..cd4ac4665e28 100644 --- a/homeassistant/components/linky/manifest.json +++ b/homeassistant/components/linky/manifest.json @@ -6,5 +6,8 @@ "pylinky==0.3.3" ], "dependencies": [], - "codeowners": [] + "codeowners": [ + "@tiste", + "@Quentame" + ] } diff --git a/homeassistant/components/linky/sensor.py b/homeassistant/components/linky/sensor.py index 63f7aaf54233..263395ab9e77 100644 --- a/homeassistant/components/linky/sensor.py +++ b/homeassistant/components/linky/sensor.py @@ -1,21 +1,41 @@ """Support for Linky.""" -import logging -import json from datetime import timedelta +import json +import logging +from pylinky.client import DAILY, MONTHLY, YEARLY, LinkyClient, PyLinkyError import voluptuous as vol -from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT, - ENERGY_KILO_WATT_HOUR) from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.helpers.entity import Entity -from homeassistant.util import Throttle +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME, + ENERGY_KILO_WATT_HOUR) import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +from homeassistant.helpers.event import track_time_interval +import homeassistant.util.dt as dt_util _LOGGER = logging.getLogger(__name__) -SCAN_INTERVAL = timedelta(minutes=10) +SCAN_INTERVAL = timedelta(hours=4) +ICON_ENERGY = "mdi:flash" +CONSUMPTION = "conso" +TIME = "time" +INDEX_CURRENT = -1 +INDEX_LAST = -2 +ATTRIBUTION = "Data provided by Enedis" + DEFAULT_TIMEOUT = 10 +SENSORS = { + "yesterday": ("Linky yesterday", DAILY, INDEX_LAST), + "current_month": ("Linky current month", MONTHLY, INDEX_CURRENT), + "last_month": ("Linky last month", MONTHLY, INDEX_LAST), + "current_year": ("Linky current year", YEARLY, INDEX_CURRENT), + "last_year": ("Linky last year", YEARLY, INDEX_LAST) +} +SENSORS_INDEX_LABEL = 0 +SENSORS_INDEX_SCALE = 1 +SENSORS_INDEX_WHEN = 2 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_USERNAME): cv.string, @@ -30,28 +50,73 @@ def setup_platform(hass, config, add_entities, discovery_info=None): password = config[CONF_PASSWORD] timeout = config[CONF_TIMEOUT] - from pylinky.client import LinkyClient, PyLinkyError - client = LinkyClient(username, password, None, timeout) - try: - client.login() - client.fetch_data() - except PyLinkyError as exp: - _LOGGER.error(exp) - client.close_session() - return + account = LinkyAccount(hass, add_entities, username, password, timeout) + add_entities(account.sensors, True) - devices = [LinkySensor('Linky', client)] - add_entities(devices, True) + +class LinkyAccount: + """Representation of a Linky account.""" + + def __init__(self, hass, add_entities, username, password, timeout): + """Initialise the Linky account.""" + self._username = username + self.__password = password + self._timeout = timeout + self._data = None + self.sensors = [] + + self.update_linky_data(dt_util.utcnow()) + + self.sensors.append( + LinkySensor("Linky yesterday", self, DAILY, INDEX_LAST)) + self.sensors.append( + LinkySensor("Linky current month", self, MONTHLY, INDEX_CURRENT)) + self.sensors.append( + LinkySensor("Linky last month", self, MONTHLY, INDEX_LAST)) + self.sensors.append( + LinkySensor("Linky current year", self, YEARLY, INDEX_CURRENT)) + self.sensors.append( + LinkySensor("Linky last year", self, YEARLY, INDEX_LAST)) + + track_time_interval(hass, self.update_linky_data, SCAN_INTERVAL) + + def update_linky_data(self, event_time): + """Fetch new state data for the sensor.""" + client = LinkyClient(self._username, self.__password, None, + self._timeout) + try: + client.login() + client.fetch_data() + self._data = client.get_data() + _LOGGER.debug(json.dumps(self._data, indent=2)) + except PyLinkyError as exp: + _LOGGER.error(exp) + finally: + client.close_session() + + @property + def username(self): + """Return the username.""" + return self._username + + @property + def data(self): + """Return the data.""" + return self._data class LinkySensor(Entity): """Representation of a sensor entity for Linky.""" - def __init__(self, name, client): + def __init__(self, name, account: LinkyAccount, scale, when): """Initialize the sensor.""" self._name = name - self._client = client - self._state = None + self.__account = account + self._scale = scale + self.__when = when + self._username = account.username + self.__time = None + self.__consumption = None @property def name(self): @@ -61,28 +126,35 @@ class LinkySensor(Entity): @property def state(self): """Return the state of the sensor.""" - return self._state + return self.__consumption @property def unit_of_measurement(self): """Return the unit of measurement.""" return ENERGY_KILO_WATT_HOUR - @Throttle(SCAN_INTERVAL) + @property + def icon(self): + """Return the icon of the sensor.""" + return ICON_ENERGY + + @property + def device_state_attributes(self): + """Return the state attributes of the sensor.""" + return { + ATTR_ATTRIBUTION: ATTRIBUTION, + 'time': self.__time, + CONF_USERNAME: self._username + } + def update(self): - """Fetch new state data for the sensor.""" - from pylinky.client import PyLinkyError - try: - self._client.fetch_data() - except PyLinkyError as exp: - _LOGGER.error(exp) - self._client.close_session() - return + """Retreive the new data for the sensor.""" + data = self.__account.data[self._scale][self.__when] + self.__consumption = data[CONSUMPTION] + self.__time = data[TIME] - _LOGGER.debug(json.dumps(self._client.get_data(), indent=2)) - - if self._client.get_data(): - # get the last past day data - self._state = self._client.get_data()['daily'][-2]['conso'] - else: - self._state = None + if self._scale is not YEARLY: + year_index = INDEX_CURRENT + if self.__time.endswith("Dec"): + year_index = INDEX_LAST + self.__time += ' ' + self.__account.data[YEARLY][year_index][TIME]