Add option for exchange rate sensor precision to Coinbase (#68792)

* Add user option for precision to exchange rate sensors

* Add tests

* Add strings
This commit is contained in:
Tom Brien 2022-03-30 12:51:44 +01:00 committed by GitHub
parent 259b069dd9
commit bb7593351b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 36 additions and 13 deletions

View file

@ -18,6 +18,8 @@ from .const import (
API_TYPE_VAULT, API_TYPE_VAULT,
CONF_CURRENCIES, CONF_CURRENCIES,
CONF_EXCHANGE_BASE, CONF_EXCHANGE_BASE,
CONF_EXCHANGE_PRECISION,
CONF_EXCHANGE_PRECISION_DEFAULT,
CONF_EXCHANGE_RATES, CONF_EXCHANGE_RATES,
CONF_OPTIONS, CONF_OPTIONS,
CONF_YAML_API_TOKEN, CONF_YAML_API_TOKEN,
@ -177,6 +179,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
default_currencies = self.config_entry.options.get(CONF_CURRENCIES, []) default_currencies = self.config_entry.options.get(CONF_CURRENCIES, [])
default_exchange_rates = self.config_entry.options.get(CONF_EXCHANGE_RATES, []) default_exchange_rates = self.config_entry.options.get(CONF_EXCHANGE_RATES, [])
default_exchange_base = self.config_entry.options.get(CONF_EXCHANGE_BASE, "USD") default_exchange_base = self.config_entry.options.get(CONF_EXCHANGE_BASE, "USD")
default_exchange_precision = self.config_entry.options.get(
CONF_EXCHANGE_PRECISION, CONF_EXCHANGE_PRECISION_DEFAULT
)
if user_input is not None: if user_input is not None:
# Pass back user selected options, even if bad # Pass back user selected options, even if bad
@ -189,6 +194,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
if CONF_EXCHANGE_RATES in user_input: if CONF_EXCHANGE_RATES in user_input:
default_exchange_base = user_input[CONF_EXCHANGE_BASE] default_exchange_base = user_input[CONF_EXCHANGE_BASE]
if CONF_EXCHANGE_PRECISION in user_input:
default_exchange_precision = user_input[CONF_EXCHANGE_PRECISION]
try: try:
await validate_options(self.hass, self.config_entry, user_input) await validate_options(self.hass, self.config_entry, user_input)
except CurrencyUnavailable: except CurrencyUnavailable:
@ -217,6 +225,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
CONF_EXCHANGE_BASE, CONF_EXCHANGE_BASE,
default=default_exchange_base, default=default_exchange_base,
): vol.In(WALLETS), ): vol.In(WALLETS),
vol.Optional(
CONF_EXCHANGE_PRECISION, default=default_exchange_precision
): int,
} }
), ),
errors=errors, errors=errors,

View file

@ -3,6 +3,8 @@
CONF_CURRENCIES = "account_balance_currencies" CONF_CURRENCIES = "account_balance_currencies"
CONF_EXCHANGE_BASE = "exchange_base" CONF_EXCHANGE_BASE = "exchange_base"
CONF_EXCHANGE_RATES = "exchange_rate_currencies" CONF_EXCHANGE_RATES = "exchange_rate_currencies"
CONF_EXCHANGE_PRECISION = "exchange_rate_precision"
CONF_EXCHANGE_PRECISION_DEFAULT = 2
CONF_OPTIONS = "options" CONF_OPTIONS = "options"
CONF_TITLE = "title" CONF_TITLE = "title"
DOMAIN = "coinbase" DOMAIN = "coinbase"

View file

@ -22,6 +22,8 @@ from .const import (
API_RESOURCE_TYPE, API_RESOURCE_TYPE,
API_TYPE_VAULT, API_TYPE_VAULT,
CONF_CURRENCIES, CONF_CURRENCIES,
CONF_EXCHANGE_PRECISION,
CONF_EXCHANGE_PRECISION_DEFAULT,
CONF_EXCHANGE_RATES, CONF_EXCHANGE_RATES,
DOMAIN, DOMAIN,
) )
@ -66,6 +68,10 @@ async def async_setup_entry(
exchange_base_currency = instance.exchange_rates[API_ACCOUNT_CURRENCY] exchange_base_currency = instance.exchange_rates[API_ACCOUNT_CURRENCY]
exchange_precision = config_entry.options.get(
CONF_EXCHANGE_PRECISION, CONF_EXCHANGE_PRECISION_DEFAULT
)
for currency in desired_currencies: for currency in desired_currencies:
if currency not in provided_currencies: if currency not in provided_currencies:
_LOGGER.warning( _LOGGER.warning(
@ -80,9 +86,7 @@ async def async_setup_entry(
for rate in config_entry.options[CONF_EXCHANGE_RATES]: for rate in config_entry.options[CONF_EXCHANGE_RATES]:
entities.append( entities.append(
ExchangeRateSensor( ExchangeRateSensor(
instance, instance, rate, exchange_base_currency, exchange_precision
rate,
exchange_base_currency,
) )
) )
@ -178,14 +182,16 @@ class AccountSensor(SensorEntity):
class ExchangeRateSensor(SensorEntity): class ExchangeRateSensor(SensorEntity):
"""Representation of a Coinbase.com sensor.""" """Representation of a Coinbase.com sensor."""
def __init__(self, coinbase_data, exchange_currency, exchange_base): def __init__(self, coinbase_data, exchange_currency, exchange_base, precision):
"""Initialize the sensor.""" """Initialize the sensor."""
self._coinbase_data = coinbase_data self._coinbase_data = coinbase_data
self.currency = exchange_currency self.currency = exchange_currency
self._name = f"{exchange_currency} Exchange Rate" self._name = f"{exchange_currency} Exchange Rate"
self._id = f"coinbase-{coinbase_data.user_id}-xe-{exchange_currency}" self._id = f"coinbase-{coinbase_data.user_id}-xe-{exchange_currency}"
self._precision = precision
self._state = round( self._state = round(
1 / float(self._coinbase_data.exchange_rates[API_RATES][self.currency]), 2 1 / float(self._coinbase_data.exchange_rates[API_RATES][self.currency]),
self._precision,
) )
self._unit_of_measurement = exchange_base self._unit_of_measurement = exchange_base
self._attr_state_class = SensorStateClass.MEASUREMENT self._attr_state_class = SensorStateClass.MEASUREMENT
@ -231,5 +237,6 @@ class ExchangeRateSensor(SensorEntity):
"""Get the latest state of the sensor.""" """Get the latest state of the sensor."""
self._coinbase_data.update() self._coinbase_data.update()
self._state = round( self._state = round(
1 / float(self._coinbase_data.exchange_rates.rates[self.currency]), 2 1 / float(self._coinbase_data.exchange_rates.rates[self.currency]),
self._precision,
) )

View file

@ -28,7 +28,8 @@
"data": { "data": {
"account_balance_currencies": "Wallet balances to report.", "account_balance_currencies": "Wallet balances to report.",
"exchange_rate_currencies": "Exchange rates to report.", "exchange_rate_currencies": "Exchange rates to report.",
"exchange_base": "Base currency for exchange rate sensors." "exchange_base": "Base currency for exchange rate sensors.",
"exchnage_rate_precision": "Number of decimal places for exchange rates."
} }
} }
}, },

View file

@ -14,9 +14,7 @@
"user": { "user": {
"data": { "data": {
"api_key": "API Key", "api_key": "API Key",
"api_token": "API Secret", "api_token": "API Secret"
"currencies": "Account Balance Currencies",
"exchange_rates": "Exchange Rates"
}, },
"description": "Please enter the details of your API key as provided by Coinbase.", "description": "Please enter the details of your API key as provided by Coinbase.",
"title": "Coinbase API Key Details" "title": "Coinbase API Key Details"
@ -26,9 +24,7 @@
"options": { "options": {
"error": { "error": {
"currency_unavailable": "One or more of the requested currency balances is not provided by your Coinbase API.", "currency_unavailable": "One or more of the requested currency balances is not provided by your Coinbase API.",
"currency_unavaliable": "One or more of the requested currency balances is not provided by your Coinbase API.",
"exchange_rate_unavailable": "One or more of the requested exchange rates is not provided by Coinbase.", "exchange_rate_unavailable": "One or more of the requested exchange rates is not provided by Coinbase.",
"exchange_rate_unavaliable": "One or more of the requested exchange rates is not provided by Coinbase.",
"unknown": "Unexpected error" "unknown": "Unexpected error"
}, },
"step": { "step": {
@ -36,7 +32,8 @@
"data": { "data": {
"account_balance_currencies": "Wallet balances to report.", "account_balance_currencies": "Wallet balances to report.",
"exchange_base": "Base currency for exchange rate sensors.", "exchange_base": "Base currency for exchange rate sensors.",
"exchange_rate_currencies": "Exchange rates to report." "exchange_rate_currencies": "Exchange rates to report.",
"exchnage_rate_precision": "Number of decimal places for exchange rates."
}, },
"description": "Adjust Coinbase Options" "description": "Adjust Coinbase Options"
} }

View file

@ -8,6 +8,7 @@ from requests.models import Response
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.coinbase.const import ( from homeassistant.components.coinbase.const import (
CONF_CURRENCIES, CONF_CURRENCIES,
CONF_EXCHANGE_PRECISION,
CONF_EXCHANGE_RATES, CONF_EXCHANGE_RATES,
CONF_YAML_API_TOKEN, CONF_YAML_API_TOKEN,
DOMAIN, DOMAIN,
@ -211,6 +212,7 @@ async def test_option_form(hass):
user_input={ user_input={
CONF_CURRENCIES: [GOOD_CURRENCY], CONF_CURRENCIES: [GOOD_CURRENCY],
CONF_EXCHANGE_RATES: [GOOD_EXCHANGE_RATE], CONF_EXCHANGE_RATES: [GOOD_EXCHANGE_RATE],
CONF_EXCHANGE_PRECISION: 5,
}, },
) )
assert result2["type"] == "create_entry" assert result2["type"] == "create_entry"
@ -237,6 +239,7 @@ async def test_form_bad_account_currency(hass):
user_input={ user_input={
CONF_CURRENCIES: [BAD_CURRENCY], CONF_CURRENCIES: [BAD_CURRENCY],
CONF_EXCHANGE_RATES: [], CONF_EXCHANGE_RATES: [],
CONF_EXCHANGE_PRECISION: 5,
}, },
) )
@ -263,6 +266,7 @@ async def test_form_bad_exchange_rate(hass):
user_input={ user_input={
CONF_CURRENCIES: [], CONF_CURRENCIES: [],
CONF_EXCHANGE_RATES: [BAD_EXCHANGE_RATE], CONF_EXCHANGE_RATES: [BAD_EXCHANGE_RATE],
CONF_EXCHANGE_PRECISION: 5,
}, },
) )
assert result2["type"] == "form" assert result2["type"] == "form"
@ -293,6 +297,7 @@ async def test_option_catch_all_exception(hass):
user_input={ user_input={
CONF_CURRENCIES: [], CONF_CURRENCIES: [],
CONF_EXCHANGE_RATES: ["ETH"], CONF_EXCHANGE_RATES: ["ETH"],
CONF_EXCHANGE_PRECISION: 5,
}, },
) )