Allow fints config as fallback for account type (#75680)

This commit is contained in:
Benjamin Richter 2022-10-16 04:01:54 +02:00 committed by GitHub
parent 3c48ce9ee7
commit e1cf261379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3,10 +3,12 @@ from __future__ import annotations
from collections import namedtuple from collections import namedtuple
from datetime import timedelta from datetime import timedelta
from functools import cached_property
import logging import logging
from typing import Any from typing import Any
from fints.client import FinTS3PinTanClient from fints.client import FinTS3PinTanClient
from fints.models import SEPAAccount
import voluptuous as vol import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
@ -77,7 +79,7 @@ def setup_platform(
acc[CONF_ACCOUNT]: acc[CONF_NAME] for acc in config[CONF_HOLDINGS] acc[CONF_ACCOUNT]: acc[CONF_NAME] for acc in config[CONF_HOLDINGS]
} }
client = FinTsClient(credentials, fints_name) client = FinTsClient(credentials, fints_name, account_config, holdings_config)
balance_accounts, holdings_accounts = client.detect_accounts() balance_accounts, holdings_accounts = client.detect_accounts()
accounts: list[SensorEntity] = [] accounts: list[SensorEntity] = []
@ -115,21 +117,27 @@ class FinTsClient:
Use this class as Context Manager to get the FinTS3Client object. Use this class as Context Manager to get the FinTS3Client object.
""" """
def __init__(self, credentials: BankCredentials, name: str) -> None: def __init__(
self,
credentials: BankCredentials,
name: str,
account_config: dict,
holdings_config: dict,
) -> None:
"""Initialize a FinTsClient.""" """Initialize a FinTsClient."""
self._credentials = credentials self._credentials = credentials
self._account_information: dict[str, dict] = {}
self._account_information_fetched = False
self.name = name self.name = name
self.account_config = account_config
self.holdings_config = holdings_config
@property @cached_property
def client(self): def client(self) -> FinTS3PinTanClient:
"""Get the client object. """Get the FinTS client object.
As the fints library is stateless, there is not benefit in caching The FinTS library persists the current dialog with the bank
the client objects. If that ever changes, consider caching the client and stores bank capabilities. So caching the client is beneficial.
object and also think about potential concurrency problems.
Note: As of version 2, the fints library is not stateless anymore.
This should be considered when reworking this integration.
""" """
return FinTS3PinTanClient( return FinTS3PinTanClient(
@ -139,26 +147,77 @@ class FinTsClient:
self._credentials.url, self._credentials.url,
) )
def detect_accounts(self): def get_account_information(self, iban: str) -> dict | None:
"""Identify the accounts of the bank.""" """Get a dictionary of account IBANs as key and account information as value."""
bank = self.client if not self._account_information_fetched:
accounts = bank.get_sepa_accounts() self._account_information = {
account_types = { account["iban"]: account
x["iban"]: x["type"] for account in self.client.get_information()["accounts"]
for x in bank.get_information()["accounts"] }
if x["iban"] is not None self._account_information_fetched = True
}
return self._account_information.get(iban, None)
def is_balance_account(self, account: SEPAAccount) -> bool:
"""Determine if the given account is of type balance account."""
if not account.iban:
return False
account_information = self.get_account_information(account.iban)
if not account_information:
return False
if not account_information["type"]:
# bank does not support account types, use value from config
if (
account_information["iban"] in self.account_config
or account_information["account_number"] in self.account_config
):
return True
elif 1 <= account_information["type"] <= 9:
return True
return False
def is_holdings_account(self, account: SEPAAccount) -> bool:
"""Determine if the given account of type holdings account."""
if not account.iban:
return False
account_information = self.get_account_information(account.iban)
if not account_information:
return False
if not account_information["type"]:
# bank does not support account types, use value from config
if (
account_information["iban"] in self.holdings_config
or account_information["account_number"] in self.holdings_config
):
return True
elif 30 <= account_information["type"] <= 39:
return True
return False
def detect_accounts(self) -> tuple[list, list]:
"""Identify the accounts of the bank."""
balance_accounts = [] balance_accounts = []
holdings_accounts = [] holdings_accounts = []
for account in accounts:
account_type = account_types[account.iban] for account in self.client.get_sepa_accounts():
if 1 <= account_type <= 9: # 1-9 is balance account
if self.is_balance_account(account):
balance_accounts.append(account) balance_accounts.append(account)
elif 30 <= account_type <= 39: # 30-39 is holdings account
elif self.is_holdings_account(account):
holdings_accounts.append(account) holdings_accounts.append(account)
else:
_LOGGER.warning("Could not determine type of account %s", account.iban)
return balance_accounts, holdings_accounts return balance_accounts, holdings_accounts