This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## What it provides
|
## What it provides
|
||||||
|
|
||||||
- One AUD balance sensor for each account selected during setup, including Savers and 2Up (joint) accounts.
|
- One $ balance sensor for each account selected during setup, including Savers and 2Up (joint) accounts.
|
||||||
- Stable entity identity based on Up's account ID, so changing an account name does not create a new entity.
|
- Stable entity identity based on Up's account ID, so changing an account name does not create a new entity.
|
||||||
- The `up_banking_transaction` Home Assistant event for new activity. Its compact data includes `transaction_id`, `event_type` (`created`, `settled`, or `deleted`), `status` (`held`, `settled`, or `deleted`), `account_id`, `amount`, `currency`, `description`, and `created_at`.
|
- The `up_banking_transaction` Home Assistant event for new activity. Its compact data includes `transaction_id`, `event_type` (`created`, `settled`, or `deleted`), `status` (`held`, `settled`, or `deleted`), `account_id`, `amount`, `currency`, `description`, and `created_at`.
|
||||||
- A five-minute coordinated poll. It reads changed activity since the last successful poll, so a Home Assistant restart or brief outage catches up. It retains at most 500 small de-duplication records in HA storage and does **not** put transaction history on entities or into Recorder attributes.
|
- A five-minute coordinated poll. It reads changed activity since the last successful poll, so a Home Assistant restart or brief outage catches up. It retains at most 500 small de-duplication records in HA storage and does **not** put transaction history on entities or into Recorder attributes.
|
||||||
@@ -36,7 +36,7 @@ automation:
|
|||||||
data:
|
data:
|
||||||
message: >-
|
message: >-
|
||||||
{{ trigger.event.data.description }}:
|
{{ trigger.event.data.description }}:
|
||||||
{{ trigger.event.data.amount }} {{ trigger.event.data.currency }}
|
{{ trigger.event.data.currency }}{{ trigger.event.data.amount }}
|
||||||
```
|
```
|
||||||
|
|
||||||
Amounts use Up's signed decimal representation: an outgoing purchase is normally negative.
|
Amounts use Up's signed decimal representation: an outgoing purchase is normally negative.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class UpApi:
|
|||||||
if response.status >= 400:
|
if response.status >= 400:
|
||||||
raise UpApiError(f"Up API request failed ({response.status})")
|
raise UpApiError(f"Up API request failed ({response.status})")
|
||||||
return (await response.json())["data"]
|
return (await response.json())["data"]
|
||||||
except aiohttp.ClientError as err:
|
except (aiohttp.ClientError, TimeoutError) as err:
|
||||||
raise UpApiError("Unable to reach the Up API") from err
|
raise UpApiError("Unable to reach the Up API") from err
|
||||||
|
|
||||||
async def _list(
|
async def _list(
|
||||||
@@ -70,5 +70,5 @@ class UpApi:
|
|||||||
if response.status >= 400:
|
if response.status >= 400:
|
||||||
raise UpApiError(f"Up API request failed ({response.status})")
|
raise UpApiError(f"Up API request failed ({response.status})")
|
||||||
return await response.json()
|
return await response.json()
|
||||||
except aiohttp.ClientError as err:
|
except (aiohttp.ClientError, TimeoutError) as err:
|
||||||
raise UpApiError("Unable to reach the Up API") from err
|
raise UpApiError("Unable to reach the Up API") from err
|
||||||
|
|||||||
@@ -59,7 +59,10 @@ class UpCoordinator(DataUpdateCoordinator[dict[str, dict[str, Any]]]):
|
|||||||
for tx_id, saved in list(self._seen.items()):
|
for tx_id, saved in list(self._seen.items()):
|
||||||
if saved["status"] != "HELD":
|
if saved["status"] != "HELD":
|
||||||
continue
|
continue
|
||||||
current = await self.api.transaction(tx_id)
|
try:
|
||||||
|
current = await self.api.transaction(tx_id)
|
||||||
|
except UpApiError as err:
|
||||||
|
raise UpdateFailed(str(err)) from err
|
||||||
if current is None:
|
if current is None:
|
||||||
# A held item that no longer exists was deleted by Up.
|
# A held item that no longer exists was deleted by Up.
|
||||||
payload = dict(saved["event"])
|
payload = dict(saved["event"])
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def transaction_event(transaction: dict[str, Any], event_type: str) -> dict[str,
|
|||||||
"status": attrs["status"].lower(),
|
"status": attrs["status"].lower(),
|
||||||
"account_id": account.get("id"),
|
"account_id": account.get("id"),
|
||||||
"amount": money["value"],
|
"amount": money["value"],
|
||||||
"currency": money["currencyCode"],
|
"currency": "$",
|
||||||
"description": attrs.get("description", ""),
|
"description": attrs.get("description", ""),
|
||||||
"created_at": attrs.get("createdAt"),
|
"created_at": attrs.get("createdAt"),
|
||||||
"raw_text": attrs.get("rawText"),
|
"raw_text": attrs.get("rawText"),
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,15 @@
|
|||||||
"""Tests for event payloads: no Home Assistant runtime is required."""
|
"""Tests for event payloads: no Home Assistant runtime is required."""
|
||||||
from custom_components.up_banking.models import account_label, transaction_event
|
from importlib.util import module_from_spec, spec_from_file_location
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
_MODELS_PATH = Path(__file__).parents[1] / "custom_components" / "up_banking" / "models.py"
|
||||||
|
_spec = spec_from_file_location("up_banking_models", _MODELS_PATH)
|
||||||
|
assert _spec and _spec.loader
|
||||||
|
_models = module_from_spec(_spec)
|
||||||
|
_spec.loader.exec_module(_models)
|
||||||
|
account_label = _models.account_label
|
||||||
|
transaction_event = _models.transaction_event
|
||||||
|
|
||||||
|
|
||||||
def test_account_label_includes_saver_and_ownership():
|
def test_account_label_includes_saver_and_ownership():
|
||||||
@@ -13,4 +23,4 @@ def test_transaction_event_is_small_and_automation_ready():
|
|||||||
"attributes": {"status": "HELD", "amount": {"value": "-12.34", "currencyCode": "AUD"}, "description": "Coffee", "createdAt": "2026-01-01T00:00:00Z", "rawText": "Cafe"},
|
"attributes": {"status": "HELD", "amount": {"value": "-12.34", "currencyCode": "AUD"}, "description": "Coffee", "createdAt": "2026-01-01T00:00:00Z", "rawText": "Cafe"},
|
||||||
"relationships": {"account": {"data": {"id": "account-1"}}},
|
"relationships": {"account": {"data": {"id": "account-1"}}},
|
||||||
}
|
}
|
||||||
assert transaction_event(transaction, "created") == {"transaction_id": "tx-1", "event_type": "created", "status": "held", "account_id": "account-1", "amount": "-12.34", "currency": "AUD", "description": "Coffee", "created_at": "2026-01-01T00:00:00Z", "raw_text": "Cafe"}
|
assert transaction_event(transaction, "created") == {"transaction_id": "tx-1", "event_type": "created", "status": "held", "account_id": "account-1", "amount": "-12.34", "currency": "$", "description": "Coffee", "created_at": "2026-01-01T00:00:00Z", "raw_text": "Cafe"}
|
||||||
|
|||||||
Reference in New Issue
Block a user