From dff36b808783820f11cd43ca14926b8a6046fbd1 Mon Sep 17 00:00:00 2001 From: ChristianKuehnel Date: Mon, 8 Jan 2018 16:58:34 +0100 Subject: [PATCH] Extension sensor alpha_vantage (#11427) * Extended sensor alpha_vantage * Improved error handling if symbol is not found. Now we add the symbols that were found, * Added option to give names and currencies to symbols. * Added support to read foreign exchange information. * Icons are selected based on the currency (where MDI has a matching icon) * added missing line at the end of the file... * renamed variable as required by pylint * Fix typos, ordering, and style --- .../components/sensor/alpha_vantage.py | 128 ++++++++++++++++-- 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/sensor/alpha_vantage.py b/homeassistant/components/sensor/alpha_vantage.py index 90957077c27..7987de7caf3 100644 --- a/homeassistant/components/sensor/alpha_vantage.py +++ b/homeassistant/components/sensor/alpha_vantage.py @@ -10,7 +10,8 @@ import logging import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY +from homeassistant.const import ( + ATTR_ATTRIBUTION, CONF_API_KEY, CONF_CURRENCY, CONF_NAME) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity @@ -23,25 +24,62 @@ ATTR_HIGH = 'high' ATTR_LOW = 'low' ATTR_VOLUME = 'volume' -CONF_ATTRIBUTION = "Stock market information provided by Alpha Vantage." +CONF_ATTRIBUTION = "Stock market information provided by Alpha Vantage" +CONF_FOREIGN_EXCHANGE = 'foreign_exchange' +CONF_FROM = 'from' +CONF_SYMBOL = 'symbol' CONF_SYMBOLS = 'symbols' +CONF_TO = 'to' -DEFAULT_SYMBOL = 'GOOGL' +DEFAULT_SYMBOL = { + CONF_CURRENCY: 'USD', + CONF_NAME: 'Google', + CONF_SYMBOL: 'GOOGL', +} -ICON = 'mdi:currency-usd' +DEFAULT_CURRENCY = { + CONF_FROM: 'BTC', + CONF_NAME: 'Bitcon', + CONF_TO: 'USD', +} + +ICONS = { + 'BTC': 'mdi:currency-btc', + 'EUR': 'mdi:currency-eur', + 'GBP': 'mdi:currency-gbp', + 'INR': 'mdi:currency-inr', + 'RUB': 'mdi:currency-rub', + 'TRY': 'mdi: currency-try', + 'USD': 'mdi:currency-usd', +} SCAN_INTERVAL = timedelta(minutes=5) +SYMBOL_SCHEMA = vol.Schema({ + vol.Required(CONF_SYMBOL): cv.string, + vol.Optional(CONF_CURRENCY): cv.string, + vol.Optional(CONF_NAME): cv.string, +}) + +CURRENCY_SCHEMA = vol.Schema({ + vol.Required(CONF_FROM): cv.string, + vol.Required(CONF_TO): cv.string, + vol.Optional(CONF_NAME): cv.string, +}) + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_KEY): cv.string, + vol.Optional(CONF_FOREIGN_EXCHANGE, default=[DEFAULT_CURRENCY]): + vol.All(cv.ensure_list, [CURRENCY_SCHEMA]), vol.Optional(CONF_SYMBOLS, default=[DEFAULT_SYMBOL]): - vol.All(cv.ensure_list, [cv.string]), + vol.All(cv.ensure_list, [SYMBOL_SCHEMA]), }) def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Alpha Vantage sensor.""" from alpha_vantage.timeseries import TimeSeries + from alpha_vantage.foreignexchange import ForeignExchange api_key = config.get(CONF_API_KEY) symbols = config.get(CONF_SYMBOLS) @@ -51,13 +89,26 @@ def setup_platform(hass, config, add_devices, discovery_info=None): dev = [] for symbol in symbols: try: - timeseries.get_intraday(symbol) + timeseries.get_intraday(symbol[CONF_SYMBOL]) except ValueError: _LOGGER.error( "API Key is not valid or symbol '%s' not known", symbol) - return dev.append(AlphaVantageSensor(timeseries, symbol)) + forex = ForeignExchange(key=api_key) + for conversion in config.get(CONF_FOREIGN_EXCHANGE): + from_cur = conversion.get(CONF_FROM) + to_cur = conversion.get(CONF_TO) + try: + forex.get_currency_exchange_rate( + from_currency=from_cur, to_currency=to_cur) + except ValueError as error: + _LOGGER.error( + "API Key is not valid or currencies '%s'/'%s' not known", + from_cur, to_cur) + _LOGGER.debug(str(error)) + dev.append(AlphaVantageForeignExchange(forex, conversion)) + add_devices(dev, True) @@ -66,11 +117,12 @@ class AlphaVantageSensor(Entity): def __init__(self, timeseries, symbol): """Initialize the sensor.""" - self._name = symbol + self._symbol = symbol[CONF_SYMBOL] + self._name = symbol.get(CONF_NAME, self._symbol) self._timeseries = timeseries - self._symbol = symbol self.values = None - self._unit_of_measurement = None + self._unit_of_measurement = symbol.get(CONF_CURRENCY, self._symbol) + self._icon = ICONS.get(symbol.get(CONF_CURRENCY, 'USD')) @property def name(self): @@ -80,7 +132,7 @@ class AlphaVantageSensor(Entity): @property def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" - return self._symbol + return self._unit_of_measurement @property def state(self): @@ -102,9 +154,61 @@ class AlphaVantageSensor(Entity): @property def icon(self): """Return the icon to use in the frontend, if any.""" - return ICON + return self._icon def update(self): """Get the latest data and updates the states.""" all_values, _ = self._timeseries.get_intraday(self._symbol) self.values = next(iter(all_values.values())) + + +class AlphaVantageForeignExchange(Entity): + """Sensor for foreign exchange rates.""" + + def __init__(self, foreign_exchange, config): + """Initialize the sensor.""" + self._foreign_exchange = foreign_exchange + self._from_currency = config.get(CONF_FROM) + self._to_currency = config.get(CONF_TO) + if CONF_NAME in config: + self._name = config.get(CONF_NAME) + else: + self._name = '{}/{}'.format(self._to_currency, self._from_currency) + self._unit_of_measurement = self._to_currency + self._icon = ICONS.get(self._from_currency, 'USD') + self.values = None + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def unit_of_measurement(self): + """Return the unit of measurement of this entity, if any.""" + return self._unit_of_measurement + + @property + def state(self): + """Return the state of the sensor.""" + return self.values['5. Exchange Rate'] + + @property + def icon(self): + """Return the icon to use in the frontend, if any.""" + return self._icon + + @property + def device_state_attributes(self): + """Return the state attributes.""" + if self.values is not None: + return { + ATTR_ATTRIBUTION: CONF_ATTRIBUTION, + CONF_FROM: self._from_currency, + CONF_TO: self._to_currency, + } + + def update(self): + """Get the latest data and updates the states.""" + self.values, _ = self._foreign_exchange.get_currency_exchange_rate( + from_currency=self._from_currency, to_currency=self._to_currency)