def list_accounts(): """ List all shared accounts (id, name, balance) """ # Ensure the whole account is included print("\nLoading Bunq API environment...") env = ApiEnvironmentType.PRODUCTION # Authenticate session & load context print("Authenticating and loading context...") api_context = ApiContext(ApiEnvironmentType.PRODUCTION, API_KEY, socket.gethostname()) api_context.ensure_session_active() BunqContext.load_api_context(api_context) # Get user info print("Loading user info...\n") user = endpoint.User.get().value.get_referenced_object() # Fetch account detials accounts = endpoint.MonetaryAccountJoint.list().value for account in accounts: print( f"[{account.id_}] {account.description} (\N{euro sign}{account.balance.value})" )
def _reload_bunq_ctx(cls, conf_path=None): if conf_path is None: conf_path = cls.config.get('bunq', 'conf_path', fallback='') if not conf_path: return try: api_ctx = ApiContext.restore(conf_path) # print('Bunq config loaded from', conf_path, file=sys.stderr) except FileNotFoundError: basepath = os.getcwd() path = os.path.join(basepath, conf_path) try: api_ctx = ApiContext.restore(path) # print('Bunq config loaded from', path, file=sys.stderr) except FileNotFoundError: raise service.exceptions.NotFoundException( f'Bunq config not found at {path}' ) try: cls.bunq_ctx.api_context().close_session() except (BunqException, TypeError): pass try: cls.bunq_ctx.load_api_context(api_ctx) except AttributeError: raise service.exceptions.ConfigException('Bunq config invalid')
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Bunq Bank sensor platform.""" from bunq.sdk.context import ( ApiContext, ApiEnvironmentType, BunqContext) from bunq.sdk.exception import BunqException, ApiException from bunq.sdk.model.generated import endpoint from bunq.sdk.json import converter # set environment type api_environment = ApiEnvironmentType.SANDBOX \ if config.get(CONF_SANDBOX) else ApiEnvironmentType.PRODUCTION accs = [] try: # create the api context variable bunq_context = ApiContext( api_environment, config.get(CONF_API_KEY), 'Home Assistant' ) # ensure the key is active, or activate bunq_context.ensure_session_active() # load user context from api context (not IO) # checks if the user has active accounts # raises BunqException otherwise BunqContext.load_api_context(bunq_context) # call the account list endpoint accounts = converter.serialize( endpoint.MonetaryAccount.list().value) # create and add the devices to the list accs = [ BunqAccountSensor( acc['MonetaryAccountBank']['description'], acc['MonetaryAccountBank']['id'], acc['MonetaryAccountBank']['currency'], float(acc['MonetaryAccountBank']['balance']['value'])) for acc in accounts ] async_add_entities(accs) # create the refresh object data = BunqData(hass, bunq_context, accs) # schedule the first update await data.schedule_update(INTERVAL_TO_NEXT_REFRESH) except ApiException as err: # if there is something wrong with the user setup # such as a incorrect key or invalid IP address # log the error and raise HA error # nothing to setup further until the key is changed _LOGGER.error(err) except BunqException as err: # if the Bunq sdk errors out there is # such as API rate limit throtteling # log the error and raise PlatformNotReady to retry _LOGGER.error(err) raise PlatformNotReady
def main(): parser = argparse.ArgumentParser() parser.add_argument(OPTION_API_KEY) all_option = parser.parse_args() if all_option.api_key is None: raise BunqException(ERROR_OPTION_MISSING_API_KEY) api_context = ApiContext(ApiEnvironmentType.PRODUCTION, all_option.api_key, socket.gethostname()) api_context.save(DEFAULT_BUNQ_CONFIGURATION_FILE_NAME_PRODUCTION)
def fetch_transactions(days): # Ensure the whole account is included print("\nLoading Bunq API environment...") env = ApiEnvironmentType.PRODUCTION # Authenticate session & load context print("Authenticating and loading context...") api_context = ApiContext(ApiEnvironmentType.PRODUCTION, API_KEY, socket.gethostname()) api_context.ensure_session_active() BunqContext.load_api_context(api_context) # Get user info print("Loading user info...") user = endpoint.User.get().value.get_referenced_object() # Fetch account detials account = endpoint.MonetaryAccountJoint.get(ACCOUNT_ID).value description = account.description balance = account.balance.value print( f"\nLoaded account '{description}' with current balance of \N{euro sign}{balance}" ) start_date = datetime.now() - timedelta(days) transactions = [] for transaction in iterate_transactions(ACCOUNT_ID): transaction_dict = { "timestamp": transaction.created, "amount": transaction.amount.value, "description": transaction.description.replace("\r", "").replace("\n", " "), "counterparty": transaction.counterparty_alias.label_monetary_account.display_name, } # Still in range if dateparse.parse(transaction.created) >= start_date: transactions.append(transaction_dict) else: break print(transactions)
def test_auto_bunq_context_update(self): """ Tests the auto update of BunqContext. """ api_context: ApiContext = BunqContext.api_context() api_context_json: object = json.loads(api_context.to_json()) api_context_json[self.__FIELD_SESSION_CONTEXT][ self.__FIELD_EXPIRE_TIME] = self.__TIME_STAMP_IN_PAST expired_api_context = ApiContext.from_json(json.dumps(api_context_json)) self.assertNotEqual(api_context.session_context.expiry_time, expired_api_context.session_context.expiry_time) self.assertEqual(BunqContext.api_context().session_context.expiry_time, api_context.session_context.expiry_time) BunqContext.update_api_context(expired_api_context) BunqContext.user_context().refresh_user_context() self.assertNotEqual( BunqContext.api_context().session_context.expiry_time, api_context.session_context.expiry_time ) self.assertFalse(BunqContext.api_context().ensure_session_active())
def setup_context(self): if Path(self.determine_bunq_conf_filename()).is_file(): pass else: sandbox_user = self.generate_new_sandbox_user() ApiContext( ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname() ).save(self.determine_bunq_conf_filename()) self._api_context = ApiContext.restore(self.determine_bunq_conf_filename()) self._api_context.ensure_session_active() self._api_context.save(self.determine_bunq_conf_filename()) BunqContext.load_api_context(self._api_context) self._user_context = BunqContext.user_context()
def setup_context(self): if isfile(self.determine_bunq_conf_filename()): pass # Config is already present elif self.env == ApiEnvironmentType.SANDBOX: sandbox_user = self.generate_new_sandbox_user() ApiContext(ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname()).save( self.determine_bunq_conf_filename()) else: raise BunqException(self._ERROR_COULD_NOT_DETIRMINE_CONF) api_context = ApiContext.restore(self.determine_bunq_conf_filename()) api_context.ensure_session_active() api_context.save(self.determine_bunq_conf_filename()) BunqContext.load_api_context(api_context)
def automatic_sandbox_install(): """ :rtype: ApiContext """ sandbox_user = __generate_new_sandbox_user() return ApiContext(ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname())
def _setup_context(self): if not self._user_is_registered(): self._register_user() api_context = ApiContext.restore( Config.get_option('API_CONTEXT_FILE_PATH')) api_context.ensure_session_active() api_context.save(Config.get_option('API_CONTEXT_FILE_PATH')) BunqContext.load_api_context(api_context)
def setup_context(self, reset_config_if_needed=True): if isfile(self.determine_bunq_conf_filename()): pass # Config is already present elif self.env == ApiEnvironmentType.SANDBOX: sandbox_user = self.generate_new_sandbox_user() ApiContext(ApiEnvironmentType.SANDBOX, sandbox_user.api_key, socket.gethostname()).save( self.determine_bunq_conf_filename()) else: raise BunqException(self._ERROR_COULD_NOT_DETERMINE_CONF) try: api_context = ApiContext.restore( self.determine_bunq_conf_filename()) api_context.ensure_session_active() api_context.save(self.determine_bunq_conf_filename()) BunqContext.load_api_context(api_context) except ForbiddenException as forbidden_exception: if reset_config_if_needed: self.__handle_forbidden_exception(forbidden_exception) else: raise forbidden_exception
def test_api_context_restore(self): """ Saves an ApiContext to a temporary file, restores an ApiContext from that file, and compares whether the api_keys, tokens, and environment types are equal in the ApiContext and the restored ApiContext. Removes the temporary file before assertion. """ self._API_CONTEXT.save(self._TMP_FILE_PATH_FULL) api_context_restored = ApiContext.restore(self._TMP_FILE_PATH_FULL) os.remove(self._TMP_FILE_PATH_FULL) self.assertEqual(api_context_restored, self._API_CONTEXT)
def main(): parser = argparse.ArgumentParser() parser.add_argument(OPTION_API_KEY) all_option = parser.parse_args() if all_option.api_key is None: raise BunqException(ERROR_OPTION_MISSING_API_KEY) api_context = ApiContext(ApiEnvironmentType.PRODUCTION, all_option.api_key, '*') BunqContext.load_api_context(api_context) end = 50 for i in range(0, end): endpoint.Payment.create(amount=Amount('0.01', 'EUR'), counterparty_alias=Pointer('IBAN', '', ''), description=str(round(i / end * 100, 2)) + " Prozent von deinem Geld", monetary_account_id=) time.sleep(0.33333)
def _get_api_context_from_aws(self) -> ApiContext: json_string = self.parameter_manager.get("/bunq/api_context") return ApiContext.from_json(json_string)
def _create_api_context(self) -> ApiContext: api_key = self.parameter_manager.get('/bunq/api_key') api_context = ApiContext(ApiEnvironmentType.PRODUCTION, api_key, 'runs-to-gadgetfund') self._update_remote_api_context(api_context) return api_context
def setup_context(self, api_key): api_context = ApiContext(ApiEnvironmentType.SANDBOX, api_key, socket.gethostname()) api_context.ensure_session_active() BunqContext.load_api_context(api_context)
from bunq.sdk.context import ApiContext, BunqContext with open('bunq.conf', 'r') as content_file: content = content_file.read() api = ApiContext.from_json(content) BunqContext.load_api_context(api) print(BunqContext.user_context().primary_monetary_account.balance.value)
from bunq.sdk.client import Pagination from bunq.sdk.context import ApiContext from bunq.sdk.context import ApiEnvironmentType from bunq.sdk.context import BunqContext from bunq.sdk.model.generated import endpoint from json import dumps if "SECRET" not in environ: print("SECRET not specified. Exiting.") exit(0) app = Flask(__name__) CONTEXT_FILE = "bunq-production.conf" api_context = ApiContext.restore(CONTEXT_FILE) api_context.ensure_session_active() api_context.save(CONTEXT_FILE) BunqContext.load_api_context(api_context) def get_all_monetary_account_active(): pagination = Pagination() pagination.count = 25 all_monetary_account_bank = endpoint.MonetaryAccountBank.list(pagination.url_params_count_only).value all_monetary_account_bank_active = [] for monetary_account_bank in all_monetary_account_bank: if monetary_account_bank.status == "ACTIVE": all_monetary_account_bank_active.append(monetary_account_bank)
def all_transactions(dt=None): # This should be enough to ensure the whole account is included. if dt == None: dt = epoch env = ApiEnvironmentType.PRODUCTION if not isfile('bunq-production.conf'): raise Exception("No config file found, run start.py first.") # Reload the API context api_context = ApiContext.restore('bunq-production.conf') api_context.ensure_session_active() api_context.save('bunq-production.conf') BunqContext.load_api_context(api_context) # User info user = endpoint.User.get().value.get_referenced_object() # To get a list we want a pagination object. # When making a pagination object yourself you normally only set the 'count' # Then you get the url params from it using 'url_params_count_only' pagination = Pagination() pagination.count = 100 accounts = [] all_monetary_account_bank = endpoint.MonetaryAccountBank.list( pagination.url_params_count_only).value for monetary_account_bank in all_monetary_account_bank: if monetary_account_bank.status == "ACTIVE": accounts.append(monetary_account_bank) all_monetary_account_savings = endpoint.MonetaryAccountSavings.list( pagination.url_params_count_only).value for monetary_account_savings in all_monetary_account_savings: if monetary_account_savings.status == "ACTIVE": accounts.append(monetary_account_savings) # Reload where we where last time. try: with open("seen.pickle", "rb") as fp: seen = pickle.load(fp) except Exception: seen = set() # We will keep a list of transactions that are already processed in this set. # The transactions will contain: # - A set of the two possible roundings of the datestamp # - The ammount of money in absolute value # - The description # - A set containing the two accounts involved # The goal here is that this representation is the same for two accounts when shifting money arround. for a in accounts: aid = a.id_ # keep track of where we are print(a.description) for p in iter_payments(aid): # python can handle the dates we get back date = dateparse.parse(p.created) #round to the second to get a (sort of) unique, but not to precise timestamp since_epoch = int(unix_time(date)) row = [ p.created, p.amount.value, p.description.replace("\r", "").replace("\n", " "), p.alias.label_monetary_account.iban, p.counterparty_alias.label_monetary_account.iban ] # frozenset can be used to hash a set, so the order does not matter. summary = ( unique_float( since_epoch), #take both so there is norounding problem abs(float(p.amount.value)), p.description, frozenset([ p.alias.label_monetary_account.iban, p.counterparty_alias.label_monetary_account.iban ])) # Still in range if date >= dt: if summary in seen: continue else: seen.add(summary) yield (row) else: break with open("seen.pickle", "wb") as fp: pickle.dump(seen, fp)
from bunq.sdk.context import ApiContext, BunqContext from bunq.sdk.context import ApiEnvironmentType import socket apiContext = ApiContext( ApiEnvironmentType.SANDBOX, "sandbox_ae9afa8798dc521804bd1d8900167457243b9fd323e6799a9d42d75d", socket.gethostname()) apiContext.save("bunq.conf") BunqContext.load_api_context(apiContext)
from os.path import isfile import argparse from bunq.sdk.context import ApiContext from bunq.sdk.context import ApiEnvironmentType parser = argparse.ArgumentParser("Generate a Bunq API context.") parser.add_argument("api_key", metavar="KEY", help="An API key as acquired from the Bunq app.") parser.add_argument("device_name", metavar="NAME", help="A device name.") parser.add_argument("output_filename", metavar="FILE", help="A destination for the context.") args = parser.parse_args() ApiContext(ApiEnvironmentType.PRODUCTION, args.api_key, args.device_name).save(args.output_filename) api_context = ApiContext.restore(args.output_filename) api_context.ensure_session_active() api_context.save()