From 060d7c264efdf4c08ae4eeb4c496171388dc925a Mon Sep 17 00:00:00 2001 From: mampfes Date: Sun, 19 Apr 2020 19:39:26 +0200 Subject: [PATCH] add source for stadtreinigung.hamburg --- README.md | 1 + .../package/source/stadtreinigung_hamburg.py | 56 +++++++++ .../package/wizard/stadtreinigung_hamburg.py | 112 ++++++++++++++++++ doc/source/stadtreinigung_hamburg.md | 41 +++++++ info.md | 1 + 5 files changed, 211 insertions(+) create mode 100644 custom_components/waste_collection_schedule/package/source/stadtreinigung_hamburg.py create mode 100755 custom_components/waste_collection_schedule/package/wizard/stadtreinigung_hamburg.py create mode 100644 doc/source/stadtreinigung_hamburg.md diff --git a/README.md b/README.md index 10138bec..8568d7e4 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Currently the following service providers are supported: - [AbfallPlus.de / Abfall.IO](./doc/source/abfall_io.md) - [AWBKoeln.de](./doc/source/awbkoeln_de.md) - [Jumomind.de](./doc/source/jumomind_de.md) +- [Stadtreinigung.Hamburg](./doc/source/stadtreinigung_hamburg.md) ## Configuration via configuration.yaml diff --git a/custom_components/waste_collection_schedule/package/source/stadtreinigung_hamburg.py b/custom_components/waste_collection_schedule/package/source/stadtreinigung_hamburg.py new file mode 100644 index 00000000..f5084af1 --- /dev/null +++ b/custom_components/waste_collection_schedule/package/source/stadtreinigung_hamburg.py @@ -0,0 +1,56 @@ +import re +import requests +import datetime +import icalendar +from collections import OrderedDict + +from ..helpers import CollectionAppointment + + +DESCRIPTION = "Source for Stadtreinigung.Hamburg based services." +URL = "https://www.stadtreinigung.hamburg" +TEST_CASES = OrderedDict( + [ + ( + "Hamburg", + { + "asId": 5087, + "hnId": 113084, + }, + ), + ] +) + + +class Source: + def __init__(self, asId, hnId): + self._asId = asId + self._hnId = hnId + + def fetch(self): + args = {"asId": self._asId, "hnId": self._hnId, "adresse": "MeineAdresse"} + + # get ics file + r = requests.post( + f"https://www.stadtreinigung.hamburg/privatkunden/abfuhrkalender/Abfuhrtermin.ics", + data=args, + ) + + # parse ics file + calender = icalendar.Calendar.from_ical(r.text) + + # Summary text contains a lot of blabla. This reg-ex tries to extract the waste type. + regex = re.compile("Erinnerung: Abfuhr (.*) morgen") + + entries = [] + for e in calender.walk(): + if e.name == "VEVENT": + summary = str(e.get("summary")) + match = regex.match(summary) + if match: + summary = match.group(1) + dtstart = e.get("dtstart").dt.date() + datetime.timedelta(days=1) # events are reported 1 day before + summary = summary + entries.append(CollectionAppointment(dtstart, summary)) + + return entries diff --git a/custom_components/waste_collection_schedule/package/wizard/stadtreinigung_hamburg.py b/custom_components/waste_collection_schedule/package/wizard/stadtreinigung_hamburg.py new file mode 100755 index 00000000..254dab7b --- /dev/null +++ b/custom_components/waste_collection_schedule/package/wizard/stadtreinigung_hamburg.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 + +import inquirer +import requests +import json +from html.parser import HTMLParser + + +# Parser for HTML option list +class OptionParser(HTMLParser): + def __init__(self, select_name): + super().__init__() + self._select_name = select_name + self._within_select = False + self._within_option = False + self._option_name = "" + self._option_value = "-1" + self._choices = [] + + @property + def choices(self): + return self._choices + + def handle_starttag(self, tag, attrs): + if tag == "select": + for attr in attrs: + if attr[0] == "name" and attr[1] == self._select_name: + self._within_select = True + break + elif tag == "option" and self._within_select: + self._within_option = True + for attr in attrs: + if attr[0] == "value": + self._option_value = attr[1] + + def handle_endtag(self, tag): + if tag == "select": + self._within_select = False + elif tag == "option": + if ( + self._within_select + and self._within_option + and len(self._option_name) > 0 + and self._option_value != "" + ): + self._choices.append((self._option_name, self._option_value)) + self._within_option = False + self._option_name = "" + self._option_value = "-1" + + def handle_data(self, data): + if self._within_option: + self._option_name += data + + +def main(): + # search for street + questions = [ + inquirer.Text("strasse", message="Enter search string for street name"), + # inquirer.Text("hausnummer", message="Enter search string for building number"), + ] + answers = inquirer.prompt(questions) + + args = answers + args["hausnummer"] = "" + args["bestaetigung"] = "true" + args["mode"] = "search" + + r = requests.post( + "https://www.stadtreinigung.hamburg/privatkunden/abfuhrkalender/index.html", + data=args, + ) + + # parser HTML option list + parser = OptionParser(select_name="asId") + parser.feed(r.text) + + questions = [inquirer.List("asId", choices=parser.choices, message="Select street")] + answers = inquirer.prompt(questions) + + # search for building number + args = answers.copy() + args["hausnummer"] = "" + args["bestaetigung"] = "true" + args["mode"] = "search" + + r = requests.post( + "https://www.stadtreinigung.hamburg/privatkunden/abfuhrkalender/index.html", + data=args, + ) + + # parser HTML option list + parser = OptionParser(select_name="hnId") + parser.feed(r.text) + + questions = [ + inquirer.List("hnId", choices=parser.choices, message="Select house number") + ] + answers.update(inquirer.prompt(questions)) + + print("Copy the following statements into your configuration.yaml:\n") + print("# waste_collection_schedule source configuration") + print("waste_collection_schedule:") + print(" sources:") + print(" - name: stadtreinigung_hamburg") + print(" args:") + print(f" asId: {answers['asId']}") + print(f" hnId: {answers['hnId']}") + + +if __name__ == "__main__": + main() diff --git a/doc/source/stadtreinigung_hamburg.md b/doc/source/stadtreinigung_hamburg.md new file mode 100644 index 00000000..0c11d2ab --- /dev/null +++ b/doc/source/stadtreinigung_hamburg.md @@ -0,0 +1,41 @@ +# Stadtreinigung.Hamburg + +Add support for schedules provided by `Stadtreinigung.Hamburg`. + +## Configuration via configuration.yaml + +```yaml +waste_collection_schedule: + sources: + - name: stadtreinigung_hamburg + args: + asId: ASID + hnId: HNID +``` + +### Configuration Variables + +**asId**
+*(string) (required)* + +**hnId**
+*(string) (required)* + +## Example + +```yaml +waste_collection_schedule: + sources: + - name: stadtreinigung_hamburg + args: + asId: 5087 + hnId: 113084 +``` + +## How to get the source arguments + +There is a script with an interactive command line interface which generates the required source configuration: + +[https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/custom_components/waste_collection_schedule/package/wizard/stadtreinigung_hamburg.py](https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/custom_components/waste_collection_schedule/package/wizard/stadtreinigung_hamburg.py). + +Just run this script from a shell and answer the questions. diff --git a/info.md b/info.md index e3f608af..03c9478d 100644 --- a/info.md +++ b/info.md @@ -33,3 +33,4 @@ Currently the following service providers are supported: - AbfallPlus.de / Abfall.IO - AWBKoeln.de - Jumomind.de +- Stadtreinigung.Hamburg