mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Add display currency setting to CoinMarketCap sensor (#10093)
* Add support for different display currencies in CoinMarkerCap sensor. * Add test for CoinMarketCap sensor. * Add test dependency to gen_requirements_all. * Fix review comments: use string formatting and less string case chanes.
This commit is contained in:
parent
2561efe45d
commit
c2ef22bd08
@ -465,7 +465,6 @@ omit =
|
|||||||
homeassistant/components/sensor/broadlink.py
|
homeassistant/components/sensor/broadlink.py
|
||||||
homeassistant/components/sensor/buienradar.py
|
homeassistant/components/sensor/buienradar.py
|
||||||
homeassistant/components/sensor/citybikes.py
|
homeassistant/components/sensor/citybikes.py
|
||||||
homeassistant/components/sensor/coinmarketcap.py
|
|
||||||
homeassistant/components/sensor/cert_expiry.py
|
homeassistant/components/sensor/cert_expiry.py
|
||||||
homeassistant/components/sensor/comed_hourly_pricing.py
|
homeassistant/components/sensor/comed_hourly_pricing.py
|
||||||
homeassistant/components/sensor/cpuspeed.py
|
homeassistant/components/sensor/cpuspeed.py
|
||||||
|
@ -12,26 +12,28 @@ import voluptuous as vol
|
|||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_CURRENCY
|
from homeassistant.const import (
|
||||||
|
ATTR_ATTRIBUTION, CONF_CURRENCY, CONF_DISPLAY_CURRENCY)
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
REQUIREMENTS = ['coinmarketcap==4.1.1']
|
REQUIREMENTS = ['coinmarketcap==4.1.1']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTR_24H_VOLUME_USD = '24h_volume_usd'
|
ATTR_24H_VOLUME = '24h_volume'
|
||||||
ATTR_AVAILABLE_SUPPLY = 'available_supply'
|
ATTR_AVAILABLE_SUPPLY = 'available_supply'
|
||||||
ATTR_MARKET_CAP = 'market_cap_usd'
|
ATTR_MARKET_CAP = 'market_cap'
|
||||||
ATTR_NAME = 'name'
|
ATTR_NAME = 'name'
|
||||||
ATTR_PERCENT_CHANGE_24H = 'percent_change_24h'
|
ATTR_PERCENT_CHANGE_24H = 'percent_change_24h'
|
||||||
ATTR_PERCENT_CHANGE_7D = 'percent_change_7d'
|
ATTR_PERCENT_CHANGE_7D = 'percent_change_7d'
|
||||||
ATTR_PRICE = 'price_usd'
|
ATTR_PRICE = 'price'
|
||||||
ATTR_SYMBOL = 'symbol'
|
ATTR_SYMBOL = 'symbol'
|
||||||
ATTR_TOTAL_SUPPLY = 'total_supply'
|
ATTR_TOTAL_SUPPLY = 'total_supply'
|
||||||
|
|
||||||
CONF_ATTRIBUTION = "Data provided by CoinMarketCap"
|
CONF_ATTRIBUTION = "Data provided by CoinMarketCap"
|
||||||
|
|
||||||
DEFAULT_CURRENCY = 'bitcoin'
|
DEFAULT_CURRENCY = 'bitcoin'
|
||||||
|
DEFAULT_DISPLAY_CURRENCY = 'USD'
|
||||||
|
|
||||||
ICON = 'mdi:currency-usd'
|
ICON = 'mdi:currency-usd'
|
||||||
|
|
||||||
@ -39,21 +41,26 @@ SCAN_INTERVAL = timedelta(minutes=15)
|
|||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_CURRENCY, default=DEFAULT_CURRENCY): cv.string,
|
vol.Optional(CONF_CURRENCY, default=DEFAULT_CURRENCY): cv.string,
|
||||||
|
vol.Optional(CONF_DISPLAY_CURRENCY, default=DEFAULT_DISPLAY_CURRENCY):
|
||||||
|
cv.string,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Set up the CoinMarketCap sensor."""
|
"""Set up the CoinMarketCap sensor."""
|
||||||
currency = config.get(CONF_CURRENCY)
|
currency = config.get(CONF_CURRENCY)
|
||||||
|
display_currency = config.get(CONF_DISPLAY_CURRENCY).lower()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
CoinMarketCapData(currency).update()
|
CoinMarketCapData(currency, display_currency).update()
|
||||||
except HTTPError:
|
except HTTPError:
|
||||||
_LOGGER.warning("Currency %s is not available. Using bitcoin",
|
_LOGGER.warning("Currency %s or display currency %s is not available. "
|
||||||
currency)
|
"Using bitcoin and USD.", currency, display_currency)
|
||||||
currency = DEFAULT_CURRENCY
|
currency = DEFAULT_CURRENCY
|
||||||
|
display_currency = DEFAULT_DISPLAY_CURRENCY
|
||||||
|
|
||||||
add_devices([CoinMarketCapSensor(CoinMarketCapData(currency))], True)
|
add_devices([CoinMarketCapSensor(
|
||||||
|
CoinMarketCapData(currency, display_currency))], True)
|
||||||
|
|
||||||
|
|
||||||
class CoinMarketCapSensor(Entity):
|
class CoinMarketCapSensor(Entity):
|
||||||
@ -63,7 +70,7 @@ class CoinMarketCapSensor(Entity):
|
|||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
self.data = data
|
self.data = data
|
||||||
self._ticker = None
|
self._ticker = None
|
||||||
self._unit_of_measurement = 'USD'
|
self._unit_of_measurement = self.data.display_currency.upper()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -73,7 +80,8 @@ class CoinMarketCapSensor(Entity):
|
|||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return round(float(self._ticker.get('price_usd')), 2)
|
return round(float(self._ticker.get(
|
||||||
|
'price_{}'.format(self.data.display_currency))), 2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_of_measurement(self):
|
def unit_of_measurement(self):
|
||||||
@ -89,10 +97,12 @@ class CoinMarketCapSensor(Entity):
|
|||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes of the sensor."""
|
"""Return the state attributes of the sensor."""
|
||||||
return {
|
return {
|
||||||
ATTR_24H_VOLUME_USD: self._ticker.get('24h_volume_usd'),
|
ATTR_24H_VOLUME: self._ticker.get(
|
||||||
|
'24h_volume_{}'.format(self.data.display_currency)),
|
||||||
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
|
ATTR_ATTRIBUTION: CONF_ATTRIBUTION,
|
||||||
ATTR_AVAILABLE_SUPPLY: self._ticker.get('available_supply'),
|
ATTR_AVAILABLE_SUPPLY: self._ticker.get('available_supply'),
|
||||||
ATTR_MARKET_CAP: self._ticker.get('market_cap_usd'),
|
ATTR_MARKET_CAP: self._ticker.get(
|
||||||
|
'market_cap_{}'.format(self.data.display_currency)),
|
||||||
ATTR_PERCENT_CHANGE_24H: self._ticker.get('percent_change_24h'),
|
ATTR_PERCENT_CHANGE_24H: self._ticker.get('percent_change_24h'),
|
||||||
ATTR_PERCENT_CHANGE_7D: self._ticker.get('percent_change_7d'),
|
ATTR_PERCENT_CHANGE_7D: self._ticker.get('percent_change_7d'),
|
||||||
ATTR_SYMBOL: self._ticker.get('symbol'),
|
ATTR_SYMBOL: self._ticker.get('symbol'),
|
||||||
@ -108,12 +118,16 @@ class CoinMarketCapSensor(Entity):
|
|||||||
class CoinMarketCapData(object):
|
class CoinMarketCapData(object):
|
||||||
"""Get the latest data and update the states."""
|
"""Get the latest data and update the states."""
|
||||||
|
|
||||||
def __init__(self, currency):
|
def __init__(self, currency, display_currency):
|
||||||
"""Initialize the data object."""
|
"""Initialize the data object."""
|
||||||
self.currency = currency
|
self.currency = currency
|
||||||
|
self.display_currency = display_currency
|
||||||
self.ticker = None
|
self.ticker = None
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Get the latest data from blockchain.info."""
|
"""Get the latest data from blockchain.info."""
|
||||||
from coinmarketcap import Market
|
from coinmarketcap import Market
|
||||||
self.ticker = Market().ticker(self.currency, limit=1)
|
self.ticker = Market().ticker(
|
||||||
|
self.currency,
|
||||||
|
limit=1,
|
||||||
|
convert=self.display_currency)
|
||||||
|
@ -89,6 +89,7 @@ CONF_DEVICES = 'devices'
|
|||||||
CONF_DISARM_AFTER_TRIGGER = 'disarm_after_trigger'
|
CONF_DISARM_AFTER_TRIGGER = 'disarm_after_trigger'
|
||||||
CONF_DISCOVERY = 'discovery'
|
CONF_DISCOVERY = 'discovery'
|
||||||
CONF_DISPLAY_OPTIONS = 'display_options'
|
CONF_DISPLAY_OPTIONS = 'display_options'
|
||||||
|
CONF_DISPLAY_CURRENCY = 'display_currency'
|
||||||
CONF_DOMAIN = 'domain'
|
CONF_DOMAIN = 'domain'
|
||||||
CONF_DOMAINS = 'domains'
|
CONF_DOMAINS = 'domains'
|
||||||
CONF_EFFECT = 'effect'
|
CONF_EFFECT = 'effect'
|
||||||
|
@ -36,6 +36,9 @@ aiohttp_cors==0.5.3
|
|||||||
# homeassistant.components.notify.apns
|
# homeassistant.components.notify.apns
|
||||||
apns2==0.1.1
|
apns2==0.1.1
|
||||||
|
|
||||||
|
# homeassistant.components.sensor.coinmarketcap
|
||||||
|
coinmarketcap==4.1.1
|
||||||
|
|
||||||
# homeassistant.components.device_tracker.upc_connect
|
# homeassistant.components.device_tracker.upc_connect
|
||||||
defusedxml==0.5.0
|
defusedxml==0.5.0
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ TEST_REQUIREMENTS = (
|
|||||||
'aioautomatic',
|
'aioautomatic',
|
||||||
'aiohttp_cors',
|
'aiohttp_cors',
|
||||||
'apns2',
|
'apns2',
|
||||||
|
'coinmarketcap',
|
||||||
'defusedxml',
|
'defusedxml',
|
||||||
'dsmr_parser',
|
'dsmr_parser',
|
||||||
'ephem',
|
'ephem',
|
||||||
|
44
tests/components/sensor/test_coinmarketcap.py
Normal file
44
tests/components/sensor/test_coinmarketcap.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
"""Tests for the CoinMarketCap sensor platform."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import homeassistant.components.sensor as sensor
|
||||||
|
from homeassistant.setup import setup_component
|
||||||
|
from tests.common import (
|
||||||
|
get_test_home_assistant, load_fixture, assert_setup_component)
|
||||||
|
|
||||||
|
VALID_CONFIG = {
|
||||||
|
'platform': 'coinmarketcap',
|
||||||
|
'currency': 'ethereum',
|
||||||
|
'display_currency': 'EUR',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestCoinMarketCapSensor(unittest.TestCase):
|
||||||
|
"""Test the CoinMarketCap sensor."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up things to be run when tests are started."""
|
||||||
|
self.hass = get_test_home_assistant()
|
||||||
|
self.config = VALID_CONFIG
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Stop everything that was started."""
|
||||||
|
self.hass.stop()
|
||||||
|
|
||||||
|
@patch('coinmarketcap.Market.ticker',
|
||||||
|
return_value=json.loads(load_fixture('coinmarketcap.json')))
|
||||||
|
def test_setup(self, mock_request):
|
||||||
|
"""Test the setup with custom settings."""
|
||||||
|
with assert_setup_component(1, sensor.DOMAIN):
|
||||||
|
assert setup_component(self.hass, sensor.DOMAIN, {
|
||||||
|
'sensor': VALID_CONFIG})
|
||||||
|
|
||||||
|
state = self.hass.states.get('sensor.ethereum')
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
assert state.state == '240.47'
|
||||||
|
assert state.attributes.get('symbol') == 'ETH'
|
||||||
|
assert state.attributes.get('unit_of_measurement') == 'EUR'
|
21
tests/fixtures/coinmarketcap.json
vendored
Normal file
21
tests/fixtures/coinmarketcap.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "ethereum",
|
||||||
|
"name": "Ethereum",
|
||||||
|
"symbol": "ETH",
|
||||||
|
"rank": "2",
|
||||||
|
"price_usd": "282.423",
|
||||||
|
"price_btc": "0.048844",
|
||||||
|
"24h_volume_usd": "407024000.0",
|
||||||
|
"market_cap_usd": "26908205315.0",
|
||||||
|
"available_supply": "95276253.0",
|
||||||
|
"total_supply": "95276253.0",
|
||||||
|
"percent_change_1h": "0.06",
|
||||||
|
"percent_change_24h": "-4.57",
|
||||||
|
"percent_change_7d": "-16.39",
|
||||||
|
"last_updated": "1508776751",
|
||||||
|
"price_eur": "240.473299695",
|
||||||
|
"24h_volume_eur": "346566690.16",
|
||||||
|
"market_cap_eur": "22911395039.0"
|
||||||
|
}
|
||||||
|
]
|
Loading…
x
Reference in New Issue
Block a user