diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 291968a5..61287e85 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,33 +1,38 @@ // See https://aka.ms/vscode-remote/devcontainer.json for format details. { "name": "Waste Collection Schedule development", - "image": "ghcr.io/ludeeus/devcontainer/integration:stable", - "context": "..", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "appPort": [ "9123:8123" ], "containerEnv": { "TZ": "Europe/Berlin" // Set your local timezone here }, - "postCreateCommand": "container install", - "extensions": [ - "ms-python.python", - "github.vscode-pull-request-github", - "ryanluker.vscode-coverage-gutters", - "ms-python.vscode-pylance" - ], - "settings": { - "files.eol": "\n", - "editor.tabSize": 4, - "terminal.integrated.shell.linux": "/bin/bash", - "python.defaultInterpreterPath": "/usr/bin/python3", - "python.analysis.autoSearchPaths": false, - "python.linting.pylintEnabled": true, - "python.linting.enabled": true, - "python.formatting.provider": "black", - "editor.formatOnPaste": false, - "editor.formatOnSave": true, - "editor.formatOnType": true, - "files.trimTrailingWhitespace": true + "features": { + "ghcr.io/devcontainers/features/python:1": {} + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "github.vscode-pull-request-github", + "ryanluker.vscode-coverage-gutters", + "ms-python.vscode-pylance" + ], + "settings": { + "files.eol": "\n", + "editor.tabSize": 4, + "terminal.integrated.shell.linux": "/bin/bash", + "python.defaultInterpreterPath": "/usr/bin/python3", + "python.analysis.autoSearchPaths": false, + "python.linting.pylintEnabled": true, + "python.linting.enabled": true, + "python.formatting.provider": "black", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true + } + } } } \ No newline at end of file diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/renosyd_dk.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/renosyd_dk.py index 09bd7f36..9981ca59 100644 --- a/custom_components/waste_collection_schedule/waste_collection_schedule/source/renosyd_dk.py +++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/renosyd_dk.py @@ -1,134 +1,59 @@ -import requests, re, time, datetime -from bs4 import BeautifulSoup, NavigableString -from dateutil import parser -from waste_collection_schedule import Collection # type: ignore[attr-defined] +from datetime import datetime from typing import List +import requests +from waste_collection_schedule import Collection # type: ignore[attr-defined] + TITLE = "Renosyd" DESCRIPTION = "Renosyd collections for Skanderborg and Odder kommunes" URL = "https://renosyd.dk" TEST_CASES = { "TestCase1": { - "kommune": "skanderborg", - "husnummer": 123000, + "house_number": "013000", }, "TestCase2": { - "kommune": "skanderborg", - "husnummer": 186305, + "house_number": "012000", }, "TestCase3": { - "kommune": "odder", - "husnummer": 89042, + "house_number": 11000, }, } ICON_MAP = { "RESTAFFALD": "mdi:trash-can", - "PAPIR/PAP": "mdi:note-multiple", + "PAPIR": "mdi:newspaper", + "PAP": "mdi:archive", "EMBALLAGE": "mdi:recycle", - "STORSKRALD": "mdi:dump-truck", "HAVEAFFALD": "mdi:leaf", # Uncertain about this name, can't find an example + "GLAS": "mdi:bottle-wine", + "METAL": "mdi:wrench", + "HÅRD PLAST": "mdi:bottle-soda-classic", } -DANISH_MONTHS = [ - None, - "jan", - "feb", - "mar", - "apr", - "maj", - "jun", - "jul", - "aug", - "sep", - "okt", - "nov", - "dec", -] - class Source: - def __init__(self, kommune: str, husnummer: int): - self._kommune = kommune - self._husnummer = husnummer - self._api_url = ( - "https://" - + self._kommune.lower() - + ".netdialog.renosyd.dk/citizen/default.aspx" - ) + def __init__(self, house_number: str | int): + house_number = str(house_number).zfill(6) + self._api_url = f"https://skoda-selvbetjeningsapi.renosyd.dk/api/v1/toemmekalender?nummer={house_number}" def fetch(self) -> List[Collection]: - session = requests.Session() - - session.headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0", - "Accept-Encoding": "gzip, deflate", - "Accept": "*/*", - "Connection": "keep-alive", - } - - address_select = session.get( - self._api_url, - cookies={"StoredAddress": str(self._husnummer)}, - ) - address_select.raise_for_status() - - address_select_soup = BeautifulSoup(address_select.text, "html.parser") - data = { - i["name"]: i.get("value", "") - for i in address_select_soup.select("input[name]") - } - - binfo = session.post(self._api_url, data=data) - binfo.raise_for_status() - - binfo_soup = BeautifulSoup(binfo.text, "html.parser") - - calendar = binfo_soup.find_all(attrs={"class": "tableContainersAtProperty"}) - - months = [] - this_year = time.localtime().tm_year - - for month in calendar[1].find_all("th"): - value = month.contents[0].strip() - if value == "Beholder": - continue - - months.append(datetime.date(this_year, DANISH_MONTHS.index(value), 1)) - - if value == "dec": - this_year += 1 + response = requests.get(self._api_url) + response.raise_for_status() + data = response.json() entries = [] - rows = calendar[1].find_all("tr") - - for row in rows[1:]: - elements = row.find_all("td") - - result = re.search( - r"^(\d{1,2}\s?x\s?)([A-Za-z\/]*)(\s*\d{1,4}\s?L)?$", - elements[0].contents[0].strip(), - ) - if result is None: - continue - - container_type = result.groups()[1] - - for idx, element in enumerate(elements[1:]): - for subelement in element.contents: - if not isinstance(subelement, NavigableString): - continue - - if subelement.strip() == "": - continue - + for item in data: + for toemning in item.get("planlagtetømninger", []): + date = datetime.strptime(toemning["dato"], "%Y-%m-%dT%H:%M:%SZ").date() + for fraktion in toemning["fraktioner"]: entries.append( Collection( - date=months[idx] - + datetime.timedelta(days=int(subelement.strip()) - 1), - t=container_type, - icon=ICON_MAP.get(container_type.upper()), + date=date, + t=fraktion, + icon=ICON_MAP.get( + fraktion.upper(), "mdi:trash-can-outline" + ), ) ) return entries diff --git a/doc/source/renosyd_dk.md b/doc/source/renosyd_dk.md index beaace5d..df3adf7d 100644 --- a/doc/source/renosyd_dk.md +++ b/doc/source/renosyd_dk.md @@ -9,19 +9,15 @@ waste_collection_schedule: sources: - name: renosyd_dk args: - kommune: "odder" OR "skanderborg" - husnummer: See description - + house_number: See description + ``` ### Configuration Variables -**kommune** +**house_number** *(String) (required)* -**husnummer** -*(Int) (required)* - ## Example ```yaml @@ -29,16 +25,20 @@ waste_collection_schedule: sources: - name: renosyd_dk args: - kommune: skanderborg - husnummer: 123000 - + house_number: "023000" + ``` ## How to get the house number / husnummer -Go to the page for either [Odder](https://odder.netdialog.renosyd.dk/citizen/) or [Skanderborg](https://skanderborg.netdialog.renosyd.dk/citizen/). Select your street and then house/apartment number. Select "husk min adresse", and then select "Næste...". +Go to the [Mit renosyd](https://mit.renosyd.dk/toemmekalender) page, enter your address and click "Gem". -Now the house number is saved as a cookie. Open developer tools (right-click-> inspect in Firefox/Chrome), and look at the cookies (in Storage in Firefox, Application->Storage->Cookies in Chrome). There should be a single cookie storing a "StoredAddress" value - the house id number - and a session id which you can ignore. +Your house number is saved in Local Storage. To get this, you can: +1) Open Developer Console + - Chrome / Microsoft Edge: `CTRL + SHIFT + J` or `Cmd + Option + J` + - Firefox: `CTRL + SHIFT + K` or `Cmd + Option + K` + - Safari: `CMD + OPTION + C` +2) Paste the following and press `enter`, which will output the number you need: `JSON.parse(localStorage.getItem('bookmarkedCollectionSites'))[0].standpladsNummer` ### Filtering Example @@ -49,10 +49,9 @@ waste_collection_schedule: sources: - name: renosyd_dk args: - kommune: skanderborg - husnummer: 123001 + house_number: 123001 customize: - type: Storskrald show: false - + ```