From 52b1a416d97aea84505ecd8e04311e6a17f297db Mon Sep 17 00:00:00 2001 From: Gleb Sinyavskiy Date: Sun, 9 May 2021 00:58:23 +0200 Subject: [PATCH] Remove the N26 integration (#50292) --- .coveragerc | 1 - homeassistant/components/n26/__init__.py | 164 -------------- homeassistant/components/n26/const.py | 7 - homeassistant/components/n26/manifest.json | 8 - homeassistant/components/n26/sensor.py | 244 --------------------- homeassistant/components/n26/switch.py | 61 ------ mypy.ini | 3 - requirements_all.txt | 3 - script/hassfest/mypy_config.py | 1 - 9 files changed, 492 deletions(-) delete mode 100644 homeassistant/components/n26/__init__.py delete mode 100644 homeassistant/components/n26/const.py delete mode 100644 homeassistant/components/n26/manifest.json delete mode 100644 homeassistant/components/n26/sensor.py delete mode 100644 homeassistant/components/n26/switch.py diff --git a/.coveragerc b/.coveragerc index 1f5327274272..95d699be69f3 100644 --- a/.coveragerc +++ b/.coveragerc @@ -658,7 +658,6 @@ omit = homeassistant/components/mystrom/light.py homeassistant/components/mystrom/switch.py homeassistant/components/myq/__init__.py - homeassistant/components/n26/* homeassistant/components/nad/media_player.py homeassistant/components/nanoleaf/light.py homeassistant/components/neato/__init__.py diff --git a/homeassistant/components/n26/__init__.py b/homeassistant/components/n26/__init__.py deleted file mode 100644 index b1e83cd53113..000000000000 --- a/homeassistant/components/n26/__init__.py +++ /dev/null @@ -1,164 +0,0 @@ -"""Support for N26 bank accounts.""" -from datetime import datetime, timedelta, timezone -import logging - -from n26 import api as n26_api, config as n26_config -from requests import HTTPError -import voluptuous as vol - -from homeassistant.const import CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_USERNAME -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.discovery import load_platform -from homeassistant.util import Throttle - -from .const import DATA, DOMAIN - -_LOGGER = logging.getLogger(__name__) - -DEFAULT_SCAN_INTERVAL = timedelta(minutes=30) - -# define configuration parameters -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.All( - cv.ensure_list, - [ - { - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional( - CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL - ): cv.time_period, - } - ], - ) - }, - extra=vol.ALLOW_EXTRA, -) - -PLATFORMS = ["sensor", "switch"] - - -def setup(hass, config): - """Set up N26 Component.""" - acc_list = config[DOMAIN] - - api_data_list = [] - - for acc in acc_list: - user = acc[CONF_USERNAME] - password = acc[CONF_PASSWORD] - - api = n26_api.Api(n26_config.Config(user, password)) - - try: - api.get_token() - except HTTPError as err: - _LOGGER.error(str(err)) - return False - - api_data = N26Data(api) - api_data.update() - - api_data_list.append(api_data) - - hass.data[DOMAIN] = {} - hass.data[DOMAIN][DATA] = api_data_list - - # Load platforms for supported devices - for platform in PLATFORMS: - load_platform(hass, platform, DOMAIN, {}, config) - - return True - - -def timestamp_ms_to_date(epoch_ms) -> datetime or None: - """Convert millisecond timestamp to datetime.""" - if epoch_ms: - return datetime.fromtimestamp(epoch_ms / 1000, timezone.utc) - - -class N26Data: - """Handle N26 API object and limit updates.""" - - def __init__(self, api): - """Initialize the data object.""" - self._api = api - - self._account_info = {} - self._balance = {} - self._limits = {} - self._account_statuses = {} - - self._cards = {} - self._spaces = {} - - @property - def api(self): - """Return N26 api client.""" - return self._api - - @property - def account_info(self): - """Return N26 account info.""" - return self._account_info - - @property - def balance(self): - """Return N26 account balance.""" - return self._balance - - @property - def limits(self): - """Return N26 account limits.""" - return self._limits - - @property - def account_statuses(self): - """Return N26 account statuses.""" - return self._account_statuses - - @property - def cards(self): - """Return N26 cards.""" - return self._cards - - def card(self, card_id: str, default: dict = None): - """Return a card by its id or the given default.""" - return next((card for card in self.cards if card["id"] == card_id), default) - - @property - def spaces(self): - """Return N26 spaces.""" - return self._spaces - - def space(self, space_id: str, default: dict = None): - """Return a space by its id or the given default.""" - return next( - (space for space in self.spaces["spaces"] if space["id"] == space_id), - default, - ) - - @Throttle(min_time=DEFAULT_SCAN_INTERVAL * 0.8) - def update_account(self): - """Get the latest account data from N26.""" - self._account_info = self._api.get_account_info() - self._balance = self._api.get_balance() - self._limits = self._api.get_account_limits() - self._account_statuses = self._api.get_account_statuses() - - @Throttle(min_time=DEFAULT_SCAN_INTERVAL * 0.8) - def update_cards(self): - """Get the latest cards data from N26.""" - self._cards = self._api.get_cards() - - @Throttle(min_time=DEFAULT_SCAN_INTERVAL * 0.8) - def update_spaces(self): - """Get the latest spaces data from N26.""" - self._spaces = self._api.get_spaces() - - def update(self): - """Get the latest data from N26.""" - self.update_account() - self.update_cards() - self.update_spaces() diff --git a/homeassistant/components/n26/const.py b/homeassistant/components/n26/const.py deleted file mode 100644 index 0a640d0f34e4..000000000000 --- a/homeassistant/components/n26/const.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Provides the constants needed for component.""" -DOMAIN = "n26" - -DATA = "data" - -CARD_STATE_ACTIVE = "M_ACTIVE" -CARD_STATE_BLOCKED = "M_DISABLED" diff --git a/homeassistant/components/n26/manifest.json b/homeassistant/components/n26/manifest.json deleted file mode 100644 index a73f4742fae1..000000000000 --- a/homeassistant/components/n26/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "domain": "n26", - "name": "N26", - "documentation": "https://www.home-assistant.io/integrations/n26", - "requirements": ["n26==0.2.7"], - "codeowners": [], - "iot_class": "cloud_polling" -} diff --git a/homeassistant/components/n26/sensor.py b/homeassistant/components/n26/sensor.py deleted file mode 100644 index 98d86194b86e..000000000000 --- a/homeassistant/components/n26/sensor.py +++ /dev/null @@ -1,244 +0,0 @@ -"""Support for N26 bank account sensors.""" -from homeassistant.components.sensor import SensorEntity - -from . import DEFAULT_SCAN_INTERVAL, DOMAIN, timestamp_ms_to_date -from .const import DATA - -SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL - -ATTR_IBAN = "account" -ATTR_USABLE_BALANCE = "usable_balance" -ATTR_BANK_BALANCE = "bank_balance" - -ATTR_ACC_OWNER_TITLE = "owner_title" -ATTR_ACC_OWNER_FIRST_NAME = "owner_first_name" -ATTR_ACC_OWNER_LAST_NAME = "owner_last_name" -ATTR_ACC_OWNER_GENDER = "owner_gender" -ATTR_ACC_OWNER_BIRTH_DATE = "owner_birth_date" -ATTR_ACC_OWNER_EMAIL = "owner_email" -ATTR_ACC_OWNER_PHONE_NUMBER = "owner_phone_number" - -ICON_ACCOUNT = "mdi:currency-eur" -ICON_CARD = "mdi:credit-card" -ICON_SPACE = "mdi:crop-square" - - -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the N26 sensor platform.""" - if discovery_info is None: - return - - api_list = hass.data[DOMAIN][DATA] - - sensor_entities = [] - for api_data in api_list: - sensor_entities.append(N26Account(api_data)) - - for card in api_data.cards: - sensor_entities.append(N26Card(api_data, card)) - - for space in api_data.spaces["spaces"]: - sensor_entities.append(N26Space(api_data, space)) - - add_entities(sensor_entities) - - -class N26Account(SensorEntity): - """Sensor for a N26 balance account. - - A balance account contains an amount of money (=balance). The amount may - also be negative. - """ - - def __init__(self, api_data) -> None: - """Initialize a N26 balance account.""" - self._data = api_data - self._iban = self._data.balance["iban"] - - def update(self) -> None: - """Get the current balance and currency for the account.""" - self._data.update_account() - - @property - def unique_id(self): - """Return the unique ID of the entity.""" - return self._iban[-4:] - - @property - def name(self) -> str: - """Friendly name of the sensor.""" - return f"n26_{self._iban[-4:]}" - - @property - def state(self) -> float: - """Return the balance of the account as state.""" - if self._data.balance is None: - return None - - return self._data.balance.get("availableBalance") - - @property - def unit_of_measurement(self) -> str: - """Use the currency as unit of measurement.""" - if self._data.balance is None: - return None - - return self._data.balance.get("currency") - - @property - def extra_state_attributes(self) -> dict: - """Additional attributes of the sensor.""" - attributes = { - ATTR_IBAN: self._data.balance.get("iban"), - ATTR_BANK_BALANCE: self._data.balance.get("bankBalance"), - ATTR_USABLE_BALANCE: self._data.balance.get("usableBalance"), - ATTR_ACC_OWNER_TITLE: self._data.account_info.get("title"), - ATTR_ACC_OWNER_FIRST_NAME: self._data.account_info.get("kycFirstName"), - ATTR_ACC_OWNER_LAST_NAME: self._data.account_info.get("kycLastName"), - ATTR_ACC_OWNER_GENDER: self._data.account_info.get("gender"), - ATTR_ACC_OWNER_BIRTH_DATE: timestamp_ms_to_date( - self._data.account_info.get("birthDate") - ), - ATTR_ACC_OWNER_EMAIL: self._data.account_info.get("email"), - ATTR_ACC_OWNER_PHONE_NUMBER: self._data.account_info.get( - "mobilePhoneNumber" - ), - } - - for limit in self._data.limits: - limit_attr_name = f"limit_{limit['limit'].lower()}" - attributes[limit_attr_name] = limit["amount"] - - return attributes - - @property - def icon(self) -> str: - """Set the icon for the sensor.""" - return ICON_ACCOUNT - - -class N26Card(SensorEntity): - """Sensor for a N26 card.""" - - def __init__(self, api_data, card) -> None: - """Initialize a N26 card.""" - self._data = api_data - self._account_name = api_data.balance["iban"][-4:] - self._card = card - - def update(self) -> None: - """Get the current balance and currency for the account.""" - self._data.update_cards() - self._card = self._data.card(self._card["id"], self._card) - - @property - def unique_id(self): - """Return the unique ID of the entity.""" - return self._card["id"] - - @property - def name(self) -> str: - """Friendly name of the sensor.""" - return f"{self._account_name.lower()}_card_{self._card['id']}" - - @property - def state(self) -> float: - """Return the balance of the account as state.""" - return self._card["status"] - - @property - def extra_state_attributes(self) -> dict: - """Additional attributes of the sensor.""" - attributes = { - "apple_pay_eligible": self._card.get("applePayEligible"), - "card_activated": timestamp_ms_to_date(self._card.get("cardActivated")), - "card_product": self._card.get("cardProduct"), - "card_product_type": self._card.get("cardProductType"), - "card_settings_id": self._card.get("cardSettingsId"), - "card_Type": self._card.get("cardType"), - "design": self._card.get("design"), - "exceet_actual_delivery_date": self._card.get("exceetActualDeliveryDate"), - "exceet_card_status": self._card.get("exceetCardStatus"), - "exceet_expected_delivery_date": self._card.get( - "exceetExpectedDeliveryDate" - ), - "exceet_express_card_delivery": self._card.get("exceetExpressCardDelivery"), - "exceet_express_card_delivery_email_sent": self._card.get( - "exceetExpressCardDeliveryEmailSent" - ), - "exceet_express_card_delivery_tracking_id": self._card.get( - "exceetExpressCardDeliveryTrackingId" - ), - "expiration_date": timestamp_ms_to_date(self._card.get("expirationDate")), - "google_pay_eligible": self._card.get("googlePayEligible"), - "masked_pan": self._card.get("maskedPan"), - "membership": self._card.get("membership"), - "mpts_card": self._card.get("mptsCard"), - "pan": self._card.get("pan"), - "pin_defined": timestamp_ms_to_date(self._card.get("pinDefined")), - "username_on_card": self._card.get("usernameOnCard"), - } - return attributes - - @property - def icon(self) -> str: - """Set the icon for the sensor.""" - return ICON_CARD - - -class N26Space(SensorEntity): - """Sensor for a N26 space.""" - - def __init__(self, api_data, space) -> None: - """Initialize a N26 space.""" - self._data = api_data - self._space = space - - def update(self) -> None: - """Get the current balance and currency for the account.""" - self._data.update_spaces() - self._space = self._data.space(self._space["id"], self._space) - - @property - def unique_id(self): - """Return the unique ID of the entity.""" - return f"space_{self._data.balance['iban'][-4:]}_{self._space['name'].lower()}" - - @property - def name(self) -> str: - """Friendly name of the sensor.""" - return self._space["name"] - - @property - def state(self) -> float: - """Return the balance of the account as state.""" - return self._space["balance"]["availableBalance"] - - @property - def unit_of_measurement(self) -> str: - """Use the currency as unit of measurement.""" - return self._space["balance"]["currency"] - - @property - def extra_state_attributes(self) -> dict: - """Additional attributes of the sensor.""" - goal_value = "" - if "goal" in self._space: - goal_value = self._space.get("goal").get("amount") - - attributes = { - "name": self._space.get("name"), - "goal": goal_value, - "background_image_url": self._space.get("backgroundImageUrl"), - "image_url": self._space.get("imageUrl"), - "is_card_attached": self._space.get("isCardAttached"), - "is_hidden_from_balance": self._space.get("isHiddenFromBalance"), - "is_locked": self._space.get("isLocked"), - "is_primary": self._space.get("isPrimary"), - } - return attributes - - @property - def icon(self) -> str: - """Set the icon for the sensor.""" - return ICON_SPACE diff --git a/homeassistant/components/n26/switch.py b/homeassistant/components/n26/switch.py deleted file mode 100644 index 910aa96ca492..000000000000 --- a/homeassistant/components/n26/switch.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Support for N26 switches.""" -from homeassistant.components.switch import SwitchEntity - -from . import DEFAULT_SCAN_INTERVAL, DOMAIN -from .const import CARD_STATE_ACTIVE, CARD_STATE_BLOCKED, DATA - -SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL - - -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the N26 switch platform.""" - if discovery_info is None: - return - - api_list = hass.data[DOMAIN][DATA] - - switch_entities = [] - for api_data in api_list: - for card in api_data.cards: - switch_entities.append(N26CardSwitch(api_data, card)) - - add_entities(switch_entities) - - -class N26CardSwitch(SwitchEntity): - """Representation of a N26 card block/unblock switch.""" - - def __init__(self, api_data, card: dict): - """Initialize the N26 card block/unblock switch.""" - self._data = api_data - self._card = card - - @property - def unique_id(self): - """Return the unique ID of the entity.""" - return self._card["id"] - - @property - def name(self) -> str: - """Friendly name of the sensor.""" - return f"card_{self._card['id']}" - - @property - def is_on(self): - """Return true if switch is on.""" - return self._card["status"] == CARD_STATE_ACTIVE - - def turn_on(self, **kwargs): - """Block the card.""" - self._data.api.unblock_card(self._card["id"]) - self._card["status"] = CARD_STATE_ACTIVE - - def turn_off(self, **kwargs): - """Unblock the card.""" - self._data.api.block_card(self._card["id"]) - self._card["status"] = CARD_STATE_BLOCKED - - def update(self): - """Update the switch state.""" - self._data.update_cards() - self._card = self._data.card(self._card["id"], self._card) diff --git a/mypy.ini b/mypy.ini index 7637ffc4d6a4..4624aab9dd8d 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1028,9 +1028,6 @@ ignore_errors = true [mypy-homeassistant.components.mysensors.*] ignore_errors = true -[mypy-homeassistant.components.n26.*] -ignore_errors = true - [mypy-homeassistant.components.neato.*] ignore_errors = true diff --git a/requirements_all.txt b/requirements_all.txt index aeafcf74f359..63f7d857f89b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -971,9 +971,6 @@ mychevy==2.1.1 # homeassistant.components.mycroft mycroftapi==2.0 -# homeassistant.components.n26 -n26==0.2.7 - # homeassistant.components.nad nad_receiver==0.0.12 diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index f7472d791f38..f041ba03c5b6 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -137,7 +137,6 @@ IGNORED_MODULES: Final[list[str]] = [ "homeassistant.components.mqtt.*", "homeassistant.components.mullvad.*", "homeassistant.components.mysensors.*", - "homeassistant.components.n26.*", "homeassistant.components.neato.*", "homeassistant.components.ness_alarm.*", "homeassistant.components.nest.*",