Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The Coinbase integration."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 
8 from coinbase.rest import RESTClient
9 from coinbase.rest.rest_base import HTTPError
10 from coinbase.wallet.client import Client as LegacyClient
11 from coinbase.wallet.error import AuthenticationError
12 
13 from homeassistant.config_entries import ConfigEntry
14 from homeassistant.const import CONF_API_KEY, CONF_API_TOKEN, Platform
15 from homeassistant.core import HomeAssistant
16 from homeassistant.helpers import entity_registry as er
17 from homeassistant.util import Throttle
18 
19 from .const import (
20  ACCOUNT_IS_VAULT,
21  API_ACCOUNT_AMOUNT,
22  API_ACCOUNT_AVALIABLE,
23  API_ACCOUNT_BALANCE,
24  API_ACCOUNT_CURRENCY,
25  API_ACCOUNT_CURRENCY_CODE,
26  API_ACCOUNT_HOLD,
27  API_ACCOUNT_ID,
28  API_ACCOUNT_NAME,
29  API_ACCOUNT_VALUE,
30  API_ACCOUNTS,
31  API_DATA,
32  API_RATES_CURRENCY,
33  API_RESOURCE_TYPE,
34  API_TYPE_VAULT,
35  API_V3_ACCOUNT_ID,
36  API_V3_TYPE_VAULT,
37  CONF_CURRENCIES,
38  CONF_EXCHANGE_BASE,
39  CONF_EXCHANGE_RATES,
40  DOMAIN,
41 )
42 
43 _LOGGER = logging.getLogger(__name__)
44 
45 PLATFORMS = [Platform.SENSOR]
46 MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
47 
48 
49 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
50  """Set up Coinbase from a config entry."""
51 
52  instance = await hass.async_add_executor_job(create_and_update_instance, entry)
53 
54  entry.async_on_unload(entry.add_update_listener(update_listener))
55 
56  hass.data.setdefault(DOMAIN, {})
57 
58  hass.data[DOMAIN][entry.entry_id] = instance
59 
60  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
61 
62  return True
63 
64 
65 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
66  """Unload a config entry."""
67  unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
68  if unload_ok:
69  hass.data[DOMAIN].pop(entry.entry_id)
70 
71  return unload_ok
72 
73 
74 def create_and_update_instance(entry: ConfigEntry) -> CoinbaseData:
75  """Create and update a Coinbase Data instance."""
76  if "organizations" not in entry.data[CONF_API_KEY]:
77  client = LegacyClient(entry.data[CONF_API_KEY], entry.data[CONF_API_TOKEN])
78  version = "v2"
79  else:
80  client = RESTClient(
81  api_key=entry.data[CONF_API_KEY], api_secret=entry.data[CONF_API_TOKEN]
82  )
83  version = "v3"
84  base_rate = entry.options.get(CONF_EXCHANGE_BASE, "USD")
85  instance = CoinbaseData(client, base_rate, version)
86  instance.update()
87  return instance
88 
89 
90 async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
91  """Handle options update."""
92 
93  await hass.config_entries.async_reload(config_entry.entry_id)
94 
95  registry = er.async_get(hass)
96  entities = er.async_entries_for_config_entry(registry, config_entry.entry_id)
97 
98  # Remove orphaned entities
99  for entity in entities:
100  currency = entity.unique_id.split("-")[-1]
101  if (
102  "xe" in entity.unique_id
103  and currency not in config_entry.options.get(CONF_EXCHANGE_RATES, [])
104  or "wallet" in entity.unique_id
105  and currency not in config_entry.options.get(CONF_CURRENCIES, [])
106  ):
107  registry.async_remove(entity.entity_id)
108 
109 
110 def get_accounts(client, version):
111  """Handle paginated accounts."""
112  response = client.get_accounts()
113  if version == "v2":
114  accounts = response[API_DATA]
115  next_starting_after = response.pagination.next_starting_after
116 
117  while next_starting_after:
118  response = client.get_accounts(starting_after=next_starting_after)
119  accounts += response[API_DATA]
120  next_starting_after = response.pagination.next_starting_after
121 
122  return [
123  {
124  API_ACCOUNT_ID: account[API_ACCOUNT_ID],
125  API_ACCOUNT_NAME: account[API_ACCOUNT_NAME],
126  API_ACCOUNT_CURRENCY: account[API_ACCOUNT_CURRENCY][
127  API_ACCOUNT_CURRENCY_CODE
128  ],
129  API_ACCOUNT_AMOUNT: account[API_ACCOUNT_BALANCE][API_ACCOUNT_AMOUNT],
130  ACCOUNT_IS_VAULT: account[API_RESOURCE_TYPE] == API_TYPE_VAULT,
131  }
132  for account in accounts
133  ]
134 
135  accounts = response[API_ACCOUNTS]
136  while response["has_next"]:
137  response = client.get_accounts(cursor=response["cursor"])
138  accounts += response["accounts"]
139 
140  return [
141  {
142  API_ACCOUNT_ID: account[API_V3_ACCOUNT_ID],
143  API_ACCOUNT_NAME: account[API_ACCOUNT_NAME],
144  API_ACCOUNT_CURRENCY: account[API_ACCOUNT_CURRENCY],
145  API_ACCOUNT_AMOUNT: account[API_ACCOUNT_AVALIABLE][API_ACCOUNT_VALUE]
146  + account[API_ACCOUNT_HOLD][API_ACCOUNT_VALUE],
147  ACCOUNT_IS_VAULT: account[API_RESOURCE_TYPE] == API_V3_TYPE_VAULT,
148  }
149  for account in accounts
150  ]
151 
152 
154  """Get the latest data and update the states."""
155 
156  def __init__(self, client, exchange_base, version):
157  """Init the coinbase data object."""
158 
159  self.clientclient = client
160  self.accountsaccounts = None
161  self.exchange_baseexchange_base = exchange_base
162  self.exchange_ratesexchange_rates = None
163  if version == "v2":
164  self.user_iduser_id = self.clientclient.get_current_user()[API_ACCOUNT_ID]
165  else:
166  self.user_iduser_id = (
167  "v3_" + client.get_portfolios()["portfolios"][0][API_V3_ACCOUNT_ID]
168  )
169  self.api_versionapi_version = version
170 
171  @Throttle(MIN_TIME_BETWEEN_UPDATES)
172  def update(self):
173  """Get the latest data from coinbase."""
174 
175  try:
176  self.accountsaccounts = get_accounts(self.clientclient, self.api_versionapi_version)
177  if self.api_versionapi_version == "v2":
178  self.exchange_ratesexchange_rates = self.clientclient.get_exchange_rates(
179  currency=self.exchange_baseexchange_base
180  )
181  else:
182  self.exchange_ratesexchange_rates = self.clientclient.get(
183  "/v2/exchange-rates",
184  params={API_RATES_CURRENCY: self.exchange_baseexchange_base},
185  )[API_DATA]
186  except (AuthenticationError, HTTPError) as coinbase_error:
187  _LOGGER.error(
188  "Authentication error connecting to coinbase: %s", coinbase_error
189  )
def __init__(self, client, exchange_base, version)
Definition: __init__.py:156
None update_listener(HomeAssistant hass, ConfigEntry config_entry)
Definition: __init__.py:90
def get_accounts(client, version)
Definition: __init__.py:110
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:65
CoinbaseData create_and_update_instance(ConfigEntry entry)
Definition: __init__.py:74
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:49
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88