diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/service/AbfallIO.py b/custom_components/waste_collection_schedule/waste_collection_schedule/service/AbfallIO.py index 19e152408..73e3535e9 100644 --- a/custom_components/waste_collection_schedule/waste_collection_schedule/service/AbfallIO.py +++ b/custom_components/waste_collection_schedule/waste_collection_schedule/service/AbfallIO.py @@ -42,11 +42,6 @@ "url": "https://www.geb-goettingen.de/", "service_id": "7dd0d724cbbd008f597d18fcb1f474cb", }, - { - "title": "Landkreis Heilbronn", - "url": "https://www.landkreis-heilbronn.de/", - "service_id": "1a1e7b200165683738adddc4bd0199a2", - }, { "title": "Abfallwirtschaft Landkreis Kitzingen", "url": "https://www.abfallwelt.de/", diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/service/AbfallIOGraphQL.py b/custom_components/waste_collection_schedule/waste_collection_schedule/service/AbfallIOGraphQL.py new file mode 100644 index 000000000..4f81e69d1 --- /dev/null +++ b/custom_components/waste_collection_schedule/waste_collection_schedule/service/AbfallIOGraphQL.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + + +SERVICE_MAP = [ + { + "title": "Landkreis Heilbronn", + "url": "https://www.landkreis-heilbronn.de/", + "service_id": "1a1e7b200165683738adddc4bd0199a2", + }, +] diff --git a/custom_components/waste_collection_schedule/waste_collection_schedule/source/abfall_io_graphql.py b/custom_components/waste_collection_schedule/waste_collection_schedule/source/abfall_io_graphql.py new file mode 100644 index 000000000..203f2eefc --- /dev/null +++ b/custom_components/waste_collection_schedule/waste_collection_schedule/source/abfall_io_graphql.py @@ -0,0 +1,142 @@ +import datetime +import logging +import re +from html.parser import HTMLParser + +import requests +from waste_collection_schedule import Collection # type: ignore[attr-defined] +from waste_collection_schedule.service.AbfallIOGraphQL import SERVICE_MAP +from waste_collection_schedule.service.ICS import ICS + +TITLE = "Abfall.IO GraphQL" +DESCRIPTION = ( + "Source for AbfallPlus.de waste collection. Service is hosted on abfall.io." +) +URL = "https://www.abfallplus.de" +COUNTRY = "de" + + +def EXTRA_INFO(): + return [ + { + "title": s["title"], + "url": s["url"], + "default_params": {"key": s["service_id"]}, + } + for s in SERVICE_MAP + ] + + +TEST_CASES = { + "Neckarsulm": { + "key": "18adb00cb5135f6aa16b5fdea6dae2c63a507ae0f836540e", + "idHouseNumber": 304, + # "wasteTypes": ["28", "19", "31", "654"] + }, + "Weinsberg": { + "key": "18adb00cb5135f6aa16b5fdea6dae2c63a507ae0f836540e", + "idHouseNumber": 353, + # "wasteTypes": ["28", "19", "31", "654"] + }, +} +_LOGGER = logging.getLogger(__name__) + +MODUS_KEY = "d6c5855a62cf32a4dadbc2831f0f295f" +HEADERS = {"user-agent": "Mozilla/5.0 (xxxx Windows NT 10.0; Win64; x64)"} + + +# Parser for HTML input (hidden) text +class HiddenInputParser(HTMLParser): + def __init__(self): + super().__init__() + self._args = {} + + @property + def args(self): + return self._args + + def handle_starttag(self, tag, attrs): + if tag == "input": + d = dict(attrs) + if d["type"] == "hidden": + self._args[d["name"]] = d["value"] + + +class Source: + def __init__( + self, + key, + idHouseNumber, + wasteTypes=[], + ): + self._key = key + self._idHouseNumber = idHouseNumber + self._wasteTypes = wasteTypes # list of integers + self._ics = ICS() + + def fetch(self): + # get token + params = {"key": self._key, "modus": MODUS_KEY, "waction": "init"} + + now = datetime.datetime.now() + date2 = now + datetime.timedelta(days=365) + args["timeperiod"] = f"{now.strftime('%Y%m%d')}-{date2.strftime('%Y%m%d')}" + + body = { + "query": "\nquery Query($idHouseNumber: ID!, $wasteTypes: [ID], $dateMin: Date, $dateMax: Date, $showInactive: Boolean) {\nappointments(idHouseNumber: $idHouseNumber, wasteTypes: $wasteTypes, dateMin: $dateMin, dateMax: $dateMax, showInactive: $showInactive) {\nid\ndate\ntime\nlocation\nnote\nwasteType {\nid\nname\ncolor\ninternals {\npdfLegend\niconLow\n}\n}\n}\n}\n", + "variables": { + "idHouseNumber": self._idHouseNumber, + "wasteTypes": self._wasteTypes, + "dateMin": now.strftime("%Y-%m-%d"), + "dateMax": date2.strftime("%Y-%m-%d"), + } + } + + r = requests.post("https://widgets.abfall.io/graphql", + params=params, headers=HEADERS, data=body) + + # add all hidden input fields to form data + # There is one hidden field which acts as a token: + # It consists of a UUID key and a UUID value. + p = HiddenInputParser() + p.feed(r.text) + args = p.args + + args["idHouseNumber"] = self._idHouseNumber + + for i in range(len(self._wasteTypes)): + args[f"f_id_abfalltyp_{i}"] = self._wasteTypes[i] + + args["wasteTypes_index_max"] = len(self._wasteTypes) + args["wasteTypes"] = ",".join( + map(lambda x: str(x), self._wasteTypes)) + + params = {"key": self._key, "modus": MODUS_KEY, + "waction": "export_ics"} + + # get csv file + r = requests.post( + "https://api.abfall.io", params=params, data=args, headers=HEADERS + ) + + # parse ics file + r.encoding = "utf-8" # requests doesn't guess the encoding correctly + ics_file = r.text + + # Remove all lines starting with " + # waste type. The warning could be removed by adding the extra config + # option "f_abfallarten" with the following values [27, 28, 17, 67] + html_warnings = re.findall(r"\