Add update listener to Coinbase (#52404)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
Tom Brien 2021-07-02 10:15:05 +01:00 committed by GitHub
parent 14d3286b21
commit b3a625593e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 12 deletions

View file

@ -11,6 +11,7 @@ import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.util import Throttle from homeassistant.util import Throttle
@ -70,6 +71,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
create_and_update_instance, entry.data[CONF_API_KEY], entry.data[CONF_API_TOKEN] create_and_update_instance, entry.data[CONF_API_KEY], entry.data[CONF_API_TOKEN]
) )
entry.async_on_unload(entry.add_update_listener(update_listener))
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = instance hass.data[DOMAIN][entry.entry_id] = instance
@ -96,6 +99,29 @@ def create_and_update_instance(api_key, api_token):
return instance return instance
async def update_listener(hass, config_entry):
"""Handle options update."""
await hass.config_entries.async_reload(config_entry.entry_id)
registry = entity_registry.async_get(hass)
entities = entity_registry.async_entries_for_config_entry(
registry, config_entry.entry_id
)
# Remove orphaned entities
for entity in entities:
currency = entity.unique_id.split("-")[-1]
if "xe" in entity.unique_id and currency not in config_entry.options.get(
CONF_EXCHANGE_RATES
):
registry.async_remove(entity.entity_id)
elif "wallet" in entity.unique_id and currency not in config_entry.options.get(
CONF_CURRENCIES
):
registry.async_remove(entity.entity_id)
def get_accounts(client): def get_accounts(client):
"""Handle paginated accounts.""" """Handle paginated accounts."""
response = client.get_accounts() response = client.get_accounts()

View file

@ -11,6 +11,7 @@ from .const import (
API_ACCOUNT_ID, API_ACCOUNT_ID,
API_ACCOUNT_NAME, API_ACCOUNT_NAME,
API_ACCOUNT_NATIVE_BALANCE, API_ACCOUNT_NATIVE_BALANCE,
API_RATES,
CONF_CURRENCIES, CONF_CURRENCIES,
CONF_EXCHANGE_RATES, CONF_EXCHANGE_RATES,
DOMAIN, DOMAIN,
@ -48,7 +49,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
if CONF_CURRENCIES in config_entry.options: if CONF_CURRENCIES in config_entry.options:
desired_currencies = config_entry.options[CONF_CURRENCIES] desired_currencies = config_entry.options[CONF_CURRENCIES]
exchange_native_currency = instance.exchange_rates.currency exchange_native_currency = instance.exchange_rates[API_ACCOUNT_CURRENCY]
for currency in desired_currencies: for currency in desired_currencies:
if currency not in provided_currencies: if currency not in provided_currencies:
@ -81,9 +82,12 @@ class AccountSensor(SensorEntity):
self._coinbase_data = coinbase_data self._coinbase_data = coinbase_data
self._currency = currency self._currency = currency
for account in coinbase_data.accounts: for account in coinbase_data.accounts:
if account.currency == currency: if account[API_ACCOUNT_CURRENCY] == currency:
self._name = f"Coinbase {account[API_ACCOUNT_NAME]}" self._name = f"Coinbase {account[API_ACCOUNT_NAME]}"
self._id = f"coinbase-{account[API_ACCOUNT_ID]}" self._id = (
f"coinbase-{account[API_ACCOUNT_ID]}-wallet-"
f"{account[API_ACCOUNT_CURRENCY]}"
)
self._state = account[API_ACCOUNT_BALANCE][API_ACCOUNT_AMOUNT] self._state = account[API_ACCOUNT_BALANCE][API_ACCOUNT_AMOUNT]
self._unit_of_measurement = account[API_ACCOUNT_CURRENCY] self._unit_of_measurement = account[API_ACCOUNT_CURRENCY]
self._native_balance = account[API_ACCOUNT_NATIVE_BALANCE][ self._native_balance = account[API_ACCOUNT_NATIVE_BALANCE][
@ -131,7 +135,7 @@ class AccountSensor(SensorEntity):
"""Get the latest state of the sensor.""" """Get the latest state of the sensor."""
self._coinbase_data.update() self._coinbase_data.update()
for account in self._coinbase_data.accounts: for account in self._coinbase_data.accounts:
if account.currency == self._currency: if account[API_ACCOUNT_CURRENCY] == self._currency:
self._state = account[API_ACCOUNT_BALANCE][API_ACCOUNT_AMOUNT] self._state = account[API_ACCOUNT_BALANCE][API_ACCOUNT_AMOUNT]
self._native_balance = account[API_ACCOUNT_NATIVE_BALANCE][ self._native_balance = account[API_ACCOUNT_NATIVE_BALANCE][
API_ACCOUNT_AMOUNT API_ACCOUNT_AMOUNT
@ -150,9 +154,9 @@ class ExchangeRateSensor(SensorEntity):
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_data.user_id}-xe-{exchange_currency}" self._id = f"coinbase-{coinbase_data.user_id}-xe-{exchange_currency}"
self._state = round( self._state = round(
1 / float(self._coinbase_data.exchange_rates.rates[self.currency]), 2 1 / float(self._coinbase_data.exchange_rates[API_RATES][self.currency]), 2
) )
self._unit_of_measurement = native_currency self._unit_of_measurement = native_currency

View file

@ -11,14 +11,14 @@ BAD_EXCHANGE_RATE = "ETH"
MOCK_ACCOUNTS_RESPONSE = [ MOCK_ACCOUNTS_RESPONSE = [
{ {
"balance": {"amount": "13.38", "currency": GOOD_CURRENCY_3}, "balance": {"amount": "13.38", "currency": GOOD_CURRENCY_3},
"currency": "BTC", "currency": GOOD_CURRENCY_3,
"id": "ABCDEF", "id": "ABCDEF",
"name": "BTC Wallet", "name": "BTC Wallet",
"native_balance": {"amount": "15.02", "currency": GOOD_CURRENCY_2}, "native_balance": {"amount": "15.02", "currency": GOOD_CURRENCY_2},
}, },
{ {
"balance": {"amount": "0.00001", "currency": GOOD_CURRENCY}, "balance": {"amount": "0.00001", "currency": GOOD_CURRENCY},
"currency": "BTC", "currency": GOOD_CURRENCY,
"id": "123456789", "id": "123456789",
"name": "BTC Wallet", "name": "BTC Wallet",
"native_balance": {"amount": "100.12", "currency": GOOD_CURRENCY_2}, "native_balance": {"amount": "100.12", "currency": GOOD_CURRENCY_2},

View file

@ -19,7 +19,14 @@ from .common import (
mock_get_exchange_rates, mock_get_exchange_rates,
mocked_get_accounts, mocked_get_accounts,
) )
from .const import BAD_CURRENCY, BAD_EXCHANGE_RATE, GOOD_CURRENCY, GOOD_EXCHNAGE_RATE from .const import (
BAD_CURRENCY,
BAD_EXCHANGE_RATE,
GOOD_CURRENCY,
GOOD_CURRENCY_2,
GOOD_EXCHNAGE_RATE,
GOOD_EXCHNAGE_RATE_2,
)
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -139,6 +146,18 @@ async def test_form_catch_all_exception(hass):
async def test_option_good_account_currency(hass): async def test_option_good_account_currency(hass):
"""Test we handle a good wallet currency option.""" """Test we handle a good wallet currency option."""
config_entry = MockConfigEntry(
domain=DOMAIN,
entry_id="abcde12345",
title="Test User",
data={CONF_API_KEY: "123456", CONF_API_TOKEN: "AbCDeF"},
options={
CONF_CURRENCIES: [GOOD_CURRENCY_2],
CONF_EXCHANGE_RATES: [],
},
)
config_entry.add_to_hass(hass)
with patch( with patch(
"coinbase.wallet.client.Client.get_current_user", "coinbase.wallet.client.Client.get_current_user",
return_value=mock_get_current_user(), return_value=mock_get_current_user(),
@ -148,7 +167,8 @@ async def test_option_good_account_currency(hass):
"coinbase.wallet.client.Client.get_exchange_rates", "coinbase.wallet.client.Client.get_exchange_rates",
return_value=mock_get_exchange_rates(), return_value=mock_get_exchange_rates(),
): ):
config_entry = await init_mock_coinbase(hass) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(config_entry.entry_id) result = await hass.config_entries.options.async_init(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
result2 = await hass.config_entries.options.async_configure( result2 = await hass.config_entries.options.async_configure(
@ -191,12 +211,12 @@ async def test_option_good_exchange_rate(hass):
"""Test we handle a good exchange rate option.""" """Test we handle a good exchange rate option."""
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
unique_id="abcde12345", entry_id="abcde12345",
title="Test User", title="Test User",
data={CONF_API_KEY: "123456", CONF_API_TOKEN: "AbCDeF"}, data={CONF_API_KEY: "123456", CONF_API_TOKEN: "AbCDeF"},
options={ options={
CONF_CURRENCIES: [], CONF_CURRENCIES: [],
CONF_EXCHANGE_RATES: [], CONF_EXCHANGE_RATES: [GOOD_EXCHNAGE_RATE_2],
}, },
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)