From 11e625fb521262fd80b10c29b1faac8c5f0ef5c5 Mon Sep 17 00:00:00 2001 From: 5ila5 <5ila5@users.noreply.github.com> Date: Thu, 26 Dec 2024 19:18:09 +0100 Subject: [PATCH] add avfallsor.no, Kristiansand --- README.md | 1 + .../waste_collection_schedule/sources.json | 6 + .../translations/de.json | 20 +++ .../translations/en.json | 20 +++ .../translations/it.json | 20 +++ .../source/avfallsor_no.py | 135 ++++++++++++++++++ doc/source/avfallsor_no.md | 33 +++++ info.md | 2 +- 8 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 custom_components/waste_collection_schedule/waste_collection_schedule/source/avfallsor_no.py create mode 100644 doc/source/avfallsor_no.md diff --git a/README.md b/README.md index 34f1f7dcc..3dadaf062 100644 --- a/README.md +++ b/README.md @@ -1845,6 +1845,7 @@ If your service provider is not listed, feel free to open a [source request issu
Norway +- [Avfall Sør, Kristiansand](/doc/source/avfallsor_no.md) / avfallsor.no - [BIR (Bergensområdets Interkommunale Renovasjonsselskap)](/doc/source/bir_no.md) / bir.no - [Fosen Renovasjon](/doc/source/fosenrenovasjon_no.md) / fosenrenovasjon.no - [IRiS](/doc/source/iris_salten_no.md) / iris-salten.no diff --git a/custom_components/waste_collection_schedule/sources.json b/custom_components/waste_collection_schedule/sources.json index 2cee78bb5..a4f246b83 100644 --- a/custom_components/waste_collection_schedule/sources.json +++ b/custom_components/waste_collection_schedule/sources.json @@ -11841,6 +11841,12 @@ } ], "Norway": [ + { + "title": "Avfall S\u00f8r, Kristiansand", + "module": "avfallsor_no", + "default_params": {}, + "id": "avfallsor_no" + }, { "title": "BIR (Bergensomr\u00e5dets Interkommunale Renovasjonsselskap)", "module": "bir_no", diff --git a/custom_components/waste_collection_schedule/translations/de.json b/custom_components/waste_collection_schedule/translations/de.json index 8ba740458..f87e975fd 100644 --- a/custom_components/waste_collection_schedule/translations/de.json +++ b/custom_components/waste_collection_schedule/translations/de.json @@ -10331,6 +10331,26 @@ }, "data_description": {} }, + "args_avfallsor_no": { + "title": "Quelle konfigurieren", + "description": "Konfiguriere deinen Service Provider. Mehr details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/avfallsor_no.md", + "data": { + "calendar_title": "Kalender Titel", + "address": "Addresse" + }, + "data_description": { + "calendar_title": "Ein lesbarerer oder benutzerfreundlicherer Name für den Müllkalender. Wenn nichts angegeben wird, wird der Name der Quelle verwendet." + } + }, + "reconfigure_avfallsor_no": { + "title": "Quelle Neu Konfigurieren", + "description": "Konfiguriere deinen Service Provider. Mehr details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/avfallsor_no.md", + "data": { + "calendar_title": "Kalender Titel", + "address": "Addresse" + }, + "data_description": {} + }, "args_bir_no": { "title": "Quelle konfigurieren", "description": "Konfiguriere deinen Service Provider. Mehr details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/bir_no.md", diff --git a/custom_components/waste_collection_schedule/translations/en.json b/custom_components/waste_collection_schedule/translations/en.json index 998cfaccb..42f4a3d27 100644 --- a/custom_components/waste_collection_schedule/translations/en.json +++ b/custom_components/waste_collection_schedule/translations/en.json @@ -10343,6 +10343,26 @@ }, "data_description": {} }, + "args_avfallsor_no": { + "title": "Configure Source", + "description": "Configure your service provider. More details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/avfallsor_no.md.", + "data": { + "calendar_title": "Calendar Title", + "address": "Address" + }, + "data_description": { + "calendar_title": "A more readable, or user-friendly, name for the waste calendar. If nothing is provided, the name returned by the source will be used." + } + }, + "reconfigure_avfallsor_no": { + "title": "Reconfigure Source", + "description": "Configure your service provider. More details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/avfallsor_no.md.", + "data": { + "calendar_title": "Calendar Title", + "address": "Address" + }, + "data_description": {} + }, "args_bir_no": { "title": "Configure Source", "description": "Configure your service provider. More details: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/bir_no.md.", diff --git a/custom_components/waste_collection_schedule/translations/it.json b/custom_components/waste_collection_schedule/translations/it.json index 2eabb21ea..507cd181f 100644 --- a/custom_components/waste_collection_schedule/translations/it.json +++ b/custom_components/waste_collection_schedule/translations/it.json @@ -10313,6 +10313,26 @@ }, "data_description": {} }, + "args_avfallsor_no": { + "title": "Configurazione Sorgente", + "description": "Compila i campi per ottenere le informazioni sul tuo servizio di raccolta. Maggiori informazioni: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/avfallsor_no.md.", + "data": { + "calendar_title": "Nome Calendario", + "address": "Address" + }, + "data_description": { + "calendar_title": "Puoi cambiare il nome del calendario della raccolta dei rifiuti, altrimenti di default verra' utilizzato il nome del tuo fornitore di servizi." + } + }, + "reconfigure_avfallsor_no": { + "title": "Riconfigurazione Sorgente", + "description": "Compila i campi per ottenere le informazioni sul tuo servizio di raccolta. Per maggiori informazioni: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/avfallsor_no.md.", + "data": { + "calendar_title": "Nome Calendario", + "address": "Address" + }, + "data_description": {} + }, "args_bir_no": { "title": "Configurazione Sorgente", "description": "Compila i campi per ottenere le informazioni sul tuo servizio di raccolta. Maggiori informazioni: https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/bir_no.md.", diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/avfallsor_no.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/avfallsor_no.py new file mode 100644 index 000000000..5221f9e16 --- /dev/null +++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/avfallsor_no.py @@ -0,0 +1,135 @@ +import datetime +import logging + +import requests +from bs4 import BeautifulSoup, Tag +from waste_collection_schedule import Collection # type: ignore[attr-defined] +from waste_collection_schedule.exceptions import SourceArgumentNotFoundWithSuggestions + +TITLE = "Avfall Sør, Kristiansand" +DESCRIPTION = "Source for Avfall Sør, Kristiansand." +URL = "https://avfallsor.no/" +TEST_CASES = {"Auglandslia 1, Kristiansand": {"address": "Auglandslia 1, Kristiansand"}} +_LOGGER = logging.getLogger(__name__) + +ICON_MAP = { + "Restavfall": "mdi:trash-can", + "Bioavfall": "mdi:leaf", + "Papp og papir": "mdi:package-variant", + "Plastemballasje": "mdi:recycle", + "Glass- og metallemballasje": "mdi:bottle-soda", +} + + +API_URL = "https://avfallsor.no/wp-json/addresses/v1/address" + + +NO_WEEKDAYS = ["mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag", "søndag"] +NO_MONTHS = [ + "januar", + "februar", + "mars", + "april", + "mai", + "juni", + "juli", + "august", + "september", + "oktober", + "november", + "desember", +] + + +def parse_date(date_str: str) -> datetime.date: + date_str = date_str.lower() + for weekday in NO_WEEKDAYS: + date_str = date_str.replace(weekday, "") + + month_number = None + for i, month in enumerate(NO_MONTHS): + if month in date_str: + month_number = i + 1 + date_str = date_str.replace(month, "") + break + if month_number is None: + raise ValueError("Could not find month in date string %s" % date_str) + + date_str = date_str.replace(".", "").strip() + try: + day = int(date_str) + except ValueError: + raise ValueError("Day is not an integer: %s" % date_str) + + date_ = datetime.date(datetime.datetime.now().year, month_number, day) + if datetime.date.today() > date_: + return date_.replace(year=datetime.datetime.now().year + 1) + + return date_ + + +class Source: + def __init__(self, address: str): + self._address: str = address + + def fetch(self) -> list[Collection]: + args = {"address": self._address.split(",")[0].strip()} + + r = requests.get(API_URL, params=args) + r.raise_for_status() + matches = r.json() + href: str | None = None + + for match in matches: + if ( + match["label"] + .lower() + .replace(" ", "") + .replace(",", "") + .replace(".", "") + .casefold() + == self._address.lower() + .replace(" ", "") + .replace(",", "") + .replace(".", "") + .casefold() + ): + href = match["href"] + break + + if not href: + raise SourceArgumentNotFoundWithSuggestions( + "address", + self._address, + [match["label"] for match in matches], + ) + r = requests.get(href) + r.raise_for_status() + + soup = BeautifulSoup(r.text, "html.parser") + pickup_div = ( + soup.select_one("div.pickup-days-large") + or soup.select_one("div.pickup-days-small") + or soup.select_one("div.pickup-days") + ) + if not pickup_div: + raise ValueError("Could not find pickup days") + + entries = [] + for h3 in soup.select("h3"): + date_ = parse_date(h3.text) + div = h3.find_next_sibling("div") + + if not div or not isinstance(div, Tag): + _LOGGER.warning("Could not find div for %s", h3.text) + continue + + for text_div in div.select("div.info-boxes-box-info"): + for span in text_div.select("span"): + span.decompose() + bin_type = text_div.text.strip() + + icon = ICON_MAP.get(bin_type) # Collection icon + entries.append(Collection(date=date_, t=bin_type, icon=icon)) + + return entries diff --git a/doc/source/avfallsor_no.md b/doc/source/avfallsor_no.md new file mode 100644 index 000000000..373ce16e3 --- /dev/null +++ b/doc/source/avfallsor_no.md @@ -0,0 +1,33 @@ +# Avfall Sør, Kristiansand + +Support for schedules provided by [Avfall Sør, Kristiansand](https://avfallsor.no/), serving Kristiansand, Norway. + +## Configuration via configuration.yaml + +```yaml +waste_collection_schedule: + sources: + - name: avfallsor_no + args: + address: ADDRESS +``` + +### Configuration Variables + +**address** +*(String) (required)* + +## Example + +```yaml +waste_collection_schedule: + sources: + - name: avfallsor_no + args: + address: Auglandslia 1, Kristiansand + +``` + +## How to get the source argument + +Visit [https://avfallsor.no/henting-av-avfall/finn-hentedag/](https://avfallsor.no/henting-av-avfall/finn-hentedag/) and write the address exactly like suggested by the input field. diff --git a/info.md b/info.md index 2e7e3ff4b..7e1fa88bb 100644 --- a/info.md +++ b/info.md @@ -32,7 +32,7 @@ Waste collection schedules from service provider web sites are updated daily, de | Malta | Malta | | Netherlands | 's-Hertogenbosch, ACV Group, Afval Wijzer, Afvalstoffendienst.nl, Alpen an den Rijn, Altena, Area Afval, Avalex, Avri, Bar Afvalbeheer, Bernheze, Circulus, Cyclus NV, Dar, Den Haag, Edam-Volendam, GAD, Gemeente Almere, Gemeente Berkelland, Gemeente Cranendonck, Gemeente Hellendoorn, Gemeente Lingewaard, Gemeente Meppel, Gemeente Middelburg + Vlissingen, Gemeente Peel en Maas, Gemeente Schouwen-Duiveland, Gemeente Sudwest-Fryslan, Gemeente Venray, Gemeente Voorschoten, Gemeente Waalre, Gemeente Westland, Goes, Heusden, HVC Groep, Meerlanden, Mijn Blink, Oisterwijk, PreZero, Purmerend, RAD BV, Rd4, Reinis, Spaarnelanden, Twente Milieu, Vught, Waardlanden, Ximmio, ZRD, Ôffalkalinder van Noardeast-Fryslân & Dantumadiel | | New Zealand | Auckland Council, Christchurch City Council, Dunedin District Council, Gore, Invercargill & Southland, Hamilton City Council, Horowhenua District Council, Hutt City Council, Napier City Council, Porirua City, Rotorua Lakes Council, Tauranga City Council, Waipa District Council, Wellington City Council | -| Norway | BIR (Bergensområdets Interkommunale Renovasjonsselskap), Fosen Renovasjon, IRiS, Min Renovasjon, Movar IKS, Oslo Kommune, ReMidt Orkland muni, Sandnes Kommune, Stavanger Kommune, Trondheim | +| Norway | Avfall Sør, Kristiansand, BIR (Bergensområdets Interkommunale Renovasjonsselskap), Fosen Renovasjon, IRiS, Min Renovasjon, Movar IKS, Oslo Kommune, ReMidt Orkland muni, Sandnes Kommune, Stavanger Kommune, Trondheim | | Poland | App Moje Odpady, Bydgoszcz Pronatura, Czerwonak, Murowana Goślina, Oborniki, Ecoharmonogram, Gmina Miękinia, Koziegłowy/Objezierze/Oborniki, MPGK Katowice, Poznań, Warsaw, Wrocław | | Slovakia | fcc Environment | | Slovenia | Moji odpadki, Ljubljana, Snaga Maribor |