Exemple #1
0
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})"
        )
Exemple #2
0
    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')
Exemple #3
0
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)
Exemple #5
0
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)
Exemple #6
0
    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()
Exemple #8
0
    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)
Exemple #9
0
def automatic_sandbox_install():
    """
    :rtype: ApiContext
    """

    sandbox_user = __generate_new_sandbox_user()

    return ApiContext(ApiEnvironmentType.SANDBOX, sandbox_user.api_key,
                      socket.gethostname())
Exemple #10
0
    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)
Exemple #11
0
    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
Exemple #12
0
    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)
Exemple #13
0
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)
Exemple #17
0
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)
Exemple #18
0
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)
Exemple #19
0
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)
Exemple #20
0
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)
Exemple #21
0
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()