From 1838e35b78c5f302eb08655704f36d4379a1f892 Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Mon, 21 Jul 2025 21:15:28 +0000 Subject: [PATCH 1/5] allow to choose a list by UUID --- .env.example | 3 ++- README.md | 34 ++++++++++++++++++++++++++++++++-- fetch_lists.py | 36 ++++++++++++++++++++++++++++++++++++ main.py | 12 +++++++++++- 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 fetch_lists.py diff --git a/.env.example b/.env.example index 042e84c..2a49010 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ EMAIL=email PASSWORD=password -WEBHOOK_URL=trmnl webhook url \ No newline at end of file +#LIST_UUID=optional list uuid +WEBHOOK_URL=trmnl webhook url diff --git a/README.md b/README.md index 60e3d06..5ee6447 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,8 @@ You can either run the backend service through docker or run the script directly #### Docker -Pull the docker image and pass the email, password and webhook url as env variables. +Pull the docker image and pass the email, password and webhook url as env variables.
+You can also provide an optional list UUID (see 3. below). ```bash docker pull ghcr.io/yshrdbrn/trmnl-bring-plugin:main @@ -47,15 +48,44 @@ docker run -e 'EMAIL'='' -e 'PASSWORD'='' -e 'WEBHOOK_URL'='' + -e 'LIST_UUID'='' # optional 'ghcr.io/yshrdbrn/trmnl-bring-plugin:main' ``` #### Run the script directly -Clone the repo and rename `.env.example` to `.env` and put in your email, password and webhook url there. Then run the script: +Clone the repo and rename `.env.example` to `.env` and put in your email, password, webhook url, and optionally list UUID there. Then run the script: ```bash python3 -m venv venv pip install -r requirements.txt python ./main.py ``` + +### 3. Optional: Determine your list UUID and provide it to the plugin + +You can manage several shopping lists on Bring with the same account.
+If you want to choose a specific list, you need to determine its UUID. Otherwise the plugin just uses the first list it encounters (which might not be the first list you see in the app or web page). + +```bash +docker run + -e 'EMAIL'='' + -e 'PASSWORD'='' + 'ghcr.io/yshrdbrn/trmnl-bring-plugin:main' + python -u ./fetch_lists.py +``` + +Or run it without docker (EMAIL and PASSWORD must be set in .env and dependencies installed as in 2. above): + +```bash +python ./fetch_lists.py +``` + +You should get a list of lists like this: + +| **Name** | **UUID** | +| --------- | ------------------------------- | +| Groceries | 12345678-90ab-cdef-0123456789ab | +| Stuff | abcdef01-2345-6789-0123456789ab | + +Copy the UUID of the list you would like to display, and set it as `LIST_UUID` either via `-e` in docker or in the `.env` file. Restart your plugin service to apply the change. diff --git a/fetch_lists.py b/fetch_lists.py new file mode 100644 index 0000000..5545218 --- /dev/null +++ b/fetch_lists.py @@ -0,0 +1,36 @@ +import asyncio +import os +import aiohttp +from bring_api import Bring +from dotenv import load_dotenv + +load_dotenv() + +class Color: + BOLD = '\033[1m' + END = '\033[0m' + +async def main(): + email = os.getenv("EMAIL") + password = os.getenv("PASSWORD") + + async with aiohttp.ClientSession() as session: + bring = Bring(session, email, password) + await bring.login() + + lists = (await bring.load_lists()).lists + + if len(lists) <= 0: + print("No lists found on account") + return + + name_col_width = max(len("Name"), max(len(l.name) for l in lists)) + 2 # padding + uuid_col_width = max(len("UUID"), max(len(l.listUuid) for l in lists)) + + print(f"{Color.BOLD}{'Name'.ljust(name_col_width)} {'UUID'.ljust(uuid_col_width)}{Color.END}") + for l in lists: + print(f"{l.name.ljust(name_col_width)} {l.listUuid.ljust(uuid_col_width)}") + +if __name__ == "__main__": + asyncio.run(main()) + diff --git a/main.py b/main.py index 0c2b677..6bdaee6 100644 --- a/main.py +++ b/main.py @@ -47,6 +47,7 @@ class BringPlugin: def __init__(self): self.email = os.getenv("EMAIL") self.password = os.getenv("PASSWORD") + self.list_uuid = os.getenv("LIST_UUID") self.webhook_url = os.getenv("WEBHOOK_URL") self.bring = None self.existing_list = None @@ -95,7 +96,16 @@ class BringPlugin: if self.existing_list: new_list = copy.deepcopy(self.existing_list) else: - bring_api_list = (await self.bring.load_lists()).lists[0] + all_api_lists = (await self.bring.load_lists()).lists + if self.list_uuid is None: + if len(all_api_lists) > 1: + print(f"LIST_UUID not specified, will use first list.") + bring_api_list = all_api_lists[0] + else: + bring_api_list = next((l for l in all_api_lists if l.listUuid == self.list_uuid), None) + if bring_api_list is None: + print(f"No list with UUID \"{self.list_uuid}\" found, will use first list instead.") + bring_api_list = all_api_lists[0] new_list = BringList(name=bring_api_list.name, uuid=bring_api_list.listUuid) await self.grab_items(new_list) From a31e490dcb695feb6f2d005e6dfa13037321e804 Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Mon, 21 Jul 2025 21:15:57 +0000 Subject: [PATCH 2/5] add a super simple compose.yml for convenience --- compose.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 compose.yml diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..c1452fb --- /dev/null +++ b/compose.yml @@ -0,0 +1,5 @@ +services: + server: + build: . + env_file: .env + From de8af89d718eab3c95b1b2ab1323cbe11e042be3 Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Tue, 22 Jul 2025 22:18:05 +0000 Subject: [PATCH 3/5] add item specification (details), if available --- main.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 6bdaee6..2260b6e 100644 --- a/main.py +++ b/main.py @@ -55,10 +55,16 @@ class BringPlugin: async def grab_items(self, bring_list): """Grabs the items of the list using the list's uuid""" item_objs = (await self.bring.get_list(bring_list.uuid)).items.purchase - bring_list.items = [item.itemId for item in item_objs] + bring_list.items = [self.transform_item(item) for item in item_objs] print(f"Successfully fetched items at {datetime.datetime.now().isoformat()}") print(f"Items = {bring_list.items}") + def transform_item(self, api_item): + if api_item.specification: + return f"{api_item.itemId} ({api_item.specification})" + else: + return api_item.itemId + async def send_list_to_trmnl(self, session, bring_list): """Sends the list to TRMNL if it has changed""" if self.existing_list == bring_list: From b57546140b9ab80afdf9282f9e71318d1781c897 Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Sat, 3 Jan 2026 21:59:09 +0000 Subject: [PATCH 4/5] remove unused compose file --- compose.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 compose.yml diff --git a/compose.yml b/compose.yml deleted file mode 100644 index c1452fb..0000000 --- a/compose.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - server: - build: . - env_file: .env - From f4159bc30d5cdab449093d8a449359845d1c7777 Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Sat, 3 Jan 2026 21:59:20 +0000 Subject: [PATCH 5/5] debug log --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 2260b6e..d897a03 100644 --- a/main.py +++ b/main.py @@ -51,6 +51,7 @@ class BringPlugin: self.webhook_url = os.getenv("WEBHOOK_URL") self.bring = None self.existing_list = None + print(f"email: {self.email}") async def grab_items(self, bring_list): """Grabs the items of the list using the list's uuid"""