Scripting local Library Website
My local library sends me an email when a book is due three days in advance. But I want this information in Homeassistant, of course without manually inserting the date there.
By using playwright I login to their website and get the first entry of the books I borrowed. The secrets (username and password) are stored in a json file and loaded at the beginning. When successfully retrieved the next return date, an upload function is called with the date. The code for this function is at the end of this post.
Code to get the next return date for my local library:
import asyncio import datetime import json from playwright.async_api import async_playwright async def get_return_dates(): async with async_playwright() as p: # load username / password from a json file # format: {"username": "0123456", "password": "your_password"} secrets = json.load(open(".secrets.json")) browser = await p.chromium.launch() page = await browser.new_page() await page.goto("https://stadtbibliothek-stuttgart.de") # go to login mask await page.get_by_role("link", name="Mein Konto").click() # fill login form await page.locator("#IDENT_1").fill(secrets.get("username")) await page.locator("#LPASSW_1").fill(secrets.get("password")) # click on Anmelden button await page.get_by_role("button", name="Anmelden").click() # goto list of rentals await page.get_by_role("link", name="Ausleihen").click() # there is only one table; this could be more granular - currently this is good enough lines = await page.query_selector_all("tr") for item in lines: columns = await item.query_selector_all("td") # filter empty columns if len(columns): # second column is the date dt = datetime.datetime.strptime( (await columns[1].inner_html()).strip(), "%d.%m.%Y" ) date = str(dt.date()) # print result print("next_return_date:", date)) # upload to homeassistant upload(date) # first line with a date is all we need break else: # nothing found; untested - because I have no empty list here atm print("no return date found.") await browser.close() asyncio.run(get_return_dates())
Add this trigger to your Homeassistant configuration.yaml
:
trigger: - trigger: - platform: webhook webhook_id: !secret stadtbib allowed_methods: - POST unique_id: "stadtbib" sensor: - name: "next return date" state: "{{ trigger.json.stadtbib_next_return }}" device_class: date unique_id: "stadtbib_next_return"
And add a (unique) secret to use in the upload function.
Function references above upload the date to Homeassistant. The code needs httpx (or requests by replacing every httpx with requests).
def upload(date: datetime.date): import httpx r = httpx.post( f"http://IP_OF_HOMEASSISTANT:8123/api/webhook/your_unique_secret", json={"stadtbib_next_return": date}, ) assert r.status_code == 200
Calling this once a day is enough. The next return date could only change when I return the book with the earliest return date.
Of course, this pattern can be used for any other website to get the desired information into Homeassistant. Some time ago I scraped the water levels of rivers in Baden Württemberg with playwright (code). I didn't use the data over years so I stopped the Github Action that downloaded the data. But scraping the river near your home could be an interesting datapoint to upload to Homeassistant.