mirror of
https://github.com/sascha-hemi/hacs_waste_collection_schedule.git
synced 2026-03-21 00:04:11 +01:00
Add new source AWM München (#1768)
* Add new source AWM München * fstring + typehint + comment fix --------- Co-authored-by: 5ila5 <5ila5@users.noreply.github.com>
This commit is contained in:
@@ -563,7 +563,6 @@ Waste collection schedules in the following formats and countries are supported.
|
||||
- [Abfallwirtschaftsbetrieb Landkreis Aurich](/doc/source/c_trace_de.md) / mkw-grossefehn.de
|
||||
- [Abfallwirtschaftsbetrieb Landkreis Karlsruhe](/doc/ics/awb_landkreis_karlsruhe_de.md) / awb-landkreis-karlsruhe.de
|
||||
- [Abfallwirtschaftsbetrieb LK Mainz-Bingen](/doc/source/awb_mainz_bingen_de.md) / awb-mainz-bingen.de
|
||||
- [Abfallwirtschaftsbetrieb München](/doc/ics/awm_muenchen_de.md) / awm-muenchen.de
|
||||
- [Abfallwirtschaftsbetriebe Münster](/doc/source/muellmax_de.md) / stadt-muenster.de
|
||||
- [Abfallwirtschaftsgesellschaft Landkreis Schaumburg](/doc/ics/aws_shg_de.md) / aws-shg.de
|
||||
- [Abfallwirtschaftsverband Kreis Groß-Gerau](/doc/source/c_trace_de.md) / awv-gg.de
|
||||
@@ -602,6 +601,7 @@ Waste collection schedules in the following formats and countries are supported.
|
||||
- [AWISTA Düsseldorf](/doc/source/muellmax_de.md) / awista.de
|
||||
- [Awista Starnberg](/doc/ics/awista_starnberg_de.md) / awista-starnberg.de
|
||||
- [AWL Neuss](/doc/source/awlneuss_de.md) / buergerportal.awl-neuss.de
|
||||
- [AWM München](/doc/source/awm_muenchen_de.md) / awm-muenchen.de
|
||||
- [Bad Arolsen (MyMuell App)](/doc/source/jumomind_de.md) / mymuell.de
|
||||
- [Bad Homburg vdH](/doc/source/jumomind_de.md) / bad-homburg.de
|
||||
- [Bad Kissingen](/doc/source/app_abfallplus_de.md) / Abfall+ App: abfallappbk
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import urllib.parse
|
||||
from html.parser import HTMLParser
|
||||
from typing import Tuple
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from waste_collection_schedule import Collection # type: ignore[attr-defined]
|
||||
from waste_collection_schedule.service.ICS import ICS
|
||||
|
||||
TITLE = "AWM München"
|
||||
DESCRIPTION = "Source for AWM München."
|
||||
URL = "https://www.awm-muenchen.de"
|
||||
TEST_CASES = {
|
||||
"Waltenbergerstr. 1": {
|
||||
"street": "Waltenbergerstr.",
|
||||
"house_number": "1",
|
||||
},
|
||||
"Geretsrieder Str. 10a": {
|
||||
"street": "Geretsrieder Str.",
|
||||
"house_number": "10a",
|
||||
},
|
||||
"Neureutherstr. 8": {
|
||||
"street": "Neureutherstr.",
|
||||
"house_number": "8",
|
||||
"r_collect_cycle": "1/2;G",
|
||||
},
|
||||
}
|
||||
|
||||
ICON_MAP = {
|
||||
"Restmülltonne": "mdi:delete",
|
||||
"Biotonne": "mdi:leaf",
|
||||
"Papiertonne": "mdi:newspaper",
|
||||
"Wertstofftonne": "mdi:recycle",
|
||||
}
|
||||
|
||||
BASE_URL = "https://www.awm-muenchen.de"
|
||||
|
||||
|
||||
# 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 str(d["type"]).lower() == "hidden":
|
||||
self._args[d["name"]] = d["value"] if "value" in d else ""
|
||||
|
||||
|
||||
class Source:
|
||||
def __init__(
|
||||
self,
|
||||
street: str,
|
||||
house_number: str,
|
||||
r_collect_cycle="",
|
||||
b_collect_cycle="",
|
||||
p_collect_cycle="",
|
||||
):
|
||||
self._street = street
|
||||
self._hnr = house_number
|
||||
self._ics = ICS()
|
||||
self._r_collect_cycle = r_collect_cycle
|
||||
self._b_collect_cycle = b_collect_cycle
|
||||
self._p_collect_cycle = p_collect_cycle
|
||||
|
||||
def fetch(self):
|
||||
s = requests.session()
|
||||
|
||||
# special request header is required, server backend checks for Origin
|
||||
headers = {
|
||||
"Origin": "https://www.awm-muenchen.de",
|
||||
}
|
||||
s.headers.update(headers)
|
||||
|
||||
# request default page
|
||||
r = s.get(f"{BASE_URL}/entsorgen/abfuhrkalender")
|
||||
r.raise_for_status()
|
||||
r.encoding = "utf-8"
|
||||
|
||||
step1_action_url, args = self._get_html_form_infos(r.text, "abfuhrkalender")
|
||||
|
||||
# add the address information
|
||||
args["tx_awmabfuhrkalender_abfuhrkalender[strasse]"] = self._street
|
||||
args["tx_awmabfuhrkalender_abfuhrkalender[hausnummer]"] = self._hnr
|
||||
args["tx_awmabfuhrkalender_abfuhrkalender[section]"] = "address"
|
||||
args["tx_awmabfuhrkalender_abfuhrkalender[submitAbfuhrkalender]"] = "true"
|
||||
|
||||
# ready for step 1 - we post the address
|
||||
r = s.post(
|
||||
step1_action_url,
|
||||
data=args,
|
||||
)
|
||||
r.raise_for_status()
|
||||
|
||||
# result is the result page or the collection cycle select page
|
||||
ics_action_url = ""
|
||||
page_soup = BeautifulSoup(r.text, "html.parser")
|
||||
if download_link := page_soup.find("a", {"class": "downloadics"}):
|
||||
ics_action_url = download_link.get("href")
|
||||
else:
|
||||
action_url, args = self._get_html_form_infos(r.text, "abfuhrkalender")
|
||||
|
||||
error_message = ""
|
||||
|
||||
for key in ("B", "P", "R"):
|
||||
if (
|
||||
f"tx_awmabfuhrkalender_abfuhrkalender[leerungszyklus][{key}]"
|
||||
not in args
|
||||
):
|
||||
if self.__getattribute__(f"_{key.lower()}_collect_cycle") == "":
|
||||
cycle_options = {}
|
||||
cycle_options = page_soup.find(
|
||||
"form", id="abfuhrkalender"
|
||||
).find_all("option")
|
||||
|
||||
error_message += f"Optional parameter {key.lower()}_collect_cycle required. Possible values: "
|
||||
for option in cycle_options:
|
||||
error_message += f"{option.get('value')} ({option.text}) "
|
||||
else:
|
||||
args[
|
||||
f"tx_awmabfuhrkalender_abfuhrkalender[leerungszyklus][{key}]"
|
||||
] = self.__getattribute__(f"_{key.lower()}_collect_cycle")
|
||||
|
||||
if error_message:
|
||||
raise ValueError(error_message)
|
||||
|
||||
r = s.post(
|
||||
action_url,
|
||||
data=args,
|
||||
)
|
||||
r.raise_for_status()
|
||||
|
||||
page_soup = BeautifulSoup(r.text, "html.parser")
|
||||
if download_link := page_soup.find("a", {"class": "downloadics"}):
|
||||
ics_action_url = download_link.get("href")
|
||||
else:
|
||||
raise ValueError("Unknown error getting ics link with cycle options.")
|
||||
|
||||
# Download the ics.file
|
||||
r = s.get(f"{URL}{urllib.parse.unquote(ics_action_url)}")
|
||||
r.raise_for_status()
|
||||
|
||||
dates = self._ics.convert(r.text)
|
||||
|
||||
entries = []
|
||||
for d in dates:
|
||||
bin_type = d[1].split(",")[0].strip()
|
||||
|
||||
entries.append(Collection(d[0], bin_type, ICON_MAP.get(bin_type)))
|
||||
|
||||
return entries
|
||||
|
||||
def _get_html_form_infos(self, html: str, form_name: str) -> Tuple[str, dict]:
|
||||
"""Return a tuple with form action url and hidden form fields."""
|
||||
# collect the url where we post to
|
||||
page_soup = BeautifulSoup(html, "html.parser")
|
||||
form_soup = page_soup.find("form", id=form_name)
|
||||
action_url = f"{URL}{urllib.parse.unquote(form_soup.get('action'))}"
|
||||
|
||||
# collect the hidden input fields
|
||||
parser = HiddenInputParser()
|
||||
parser.feed(page_soup.find("form", id=form_name).decode_contents())
|
||||
|
||||
return action_url, parser.args
|
||||
@@ -1,24 +0,0 @@
|
||||
# Abfallwirtschaftsbetrieb München
|
||||
|
||||
Abfallwirtschaftsbetrieb München is supported by the generic [ICS](/doc/source/ics.md) source. For all available configuration options, please refer to the source description.
|
||||
|
||||
|
||||
## How to get the configuration arguments
|
||||
|
||||
- Goto <https://www.awm-muenchen.de/entsorgen/abfuhrkalender> and select your location.
|
||||
- Right-click on `Download ICS-Datei 20xx für Ihren Kalender` link and copy link address.
|
||||
- Replace the `url` in the example configuration with this link.
|
||||
- Replace the year in the url with `{%Y}`.
|
||||
|
||||
## Examples
|
||||
|
||||
### Adolaweg 1
|
||||
|
||||
```yaml
|
||||
waste_collection_schedule:
|
||||
sources:
|
||||
- name: ics
|
||||
args:
|
||||
regex: (.*), .*
|
||||
url: https://www.awm-muenchen.de/entsorgen/abfuhrkalender?tx_awmabfuhrkalender_abfuhrkalender%5Bhausnummer%5D=1&tx_awmabfuhrkalender_abfuhrkalender%5Bleerungszyklus%5D%5BB%5D=1%2F2%3BG&tx_awmabfuhrkalender_abfuhrkalender%5Bleerungszyklus%5D%5BP%5D=1%2F2%3BU&tx_awmabfuhrkalender_abfuhrkalender%5Bleerungszyklus%5D%5BR%5D=001%3BG&tx_awmabfuhrkalender_abfuhrkalender%5Bsection%5D=ics&tx_awmabfuhrkalender_abfuhrkalender%5Bsinglestandplatz%5D=false&tx_awmabfuhrkalender_abfuhrkalender%5Bstandplatzwahl%5D=true&tx_awmabfuhrkalender_abfuhrkalender%5Bstellplatz%5D%5Bbio%5D=70082516&tx_awmabfuhrkalender_abfuhrkalender%5Bstellplatz%5D%5Bpapier%5D=70082516&tx_awmabfuhrkalender_abfuhrkalender%5Bstellplatz%5D%5Brestmuell%5D=70082516&tx_awmabfuhrkalender_abfuhrkalender%5Bstrasse%5D=Adaloweg&tx_awmabfuhrkalender_abfuhrkalender%5Byear%5D={%Y}&cHash=e346bec0e7fdb173ae2d0e8650ecd980
|
||||
```
|
||||
@@ -1,11 +0,0 @@
|
||||
title: Abfallwirtschaftsbetrieb München
|
||||
url: https://www.awm-muenchen.de
|
||||
howto: |
|
||||
- Goto <https://www.awm-muenchen.de/entsorgen/abfuhrkalender> and select your location.
|
||||
- Right-click on `Download ICS-Datei 20xx für Ihren Kalender` link and copy link address.
|
||||
- Replace the `url` in the example configuration with this link.
|
||||
- Replace the year in the url with `{%Y}`.
|
||||
test_cases:
|
||||
Adolaweg 1:
|
||||
url: "https://www.awm-muenchen.de/entsorgen/abfuhrkalender?tx_awmabfuhrkalender_abfuhrkalender%5Bhausnummer%5D=1&tx_awmabfuhrkalender_abfuhrkalender%5Bleerungszyklus%5D%5BB%5D=1%2F2%3BG&tx_awmabfuhrkalender_abfuhrkalender%5Bleerungszyklus%5D%5BP%5D=1%2F2%3BU&tx_awmabfuhrkalender_abfuhrkalender%5Bleerungszyklus%5D%5BR%5D=001%3BG&tx_awmabfuhrkalender_abfuhrkalender%5Bsection%5D=ics&tx_awmabfuhrkalender_abfuhrkalender%5Bsinglestandplatz%5D=false&tx_awmabfuhrkalender_abfuhrkalender%5Bstandplatzwahl%5D=true&tx_awmabfuhrkalender_abfuhrkalender%5Bstellplatz%5D%5Bbio%5D=70082516&tx_awmabfuhrkalender_abfuhrkalender%5Bstellplatz%5D%5Bpapier%5D=70082516&tx_awmabfuhrkalender_abfuhrkalender%5Bstellplatz%5D%5Brestmuell%5D=70082516&tx_awmabfuhrkalender_abfuhrkalender%5Bstrasse%5D=Adaloweg&tx_awmabfuhrkalender_abfuhrkalender%5Byear%5D={%Y}&cHash=e346bec0e7fdb173ae2d0e8650ecd980"
|
||||
regex: "(.*), .*"
|
||||
85
doc/source/awm_muenchen_de.md
Normal file
85
doc/source/awm_muenchen_de.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Abfallwirtschaftsbetrieb München
|
||||
|
||||
Support for schedules provided by [Abfallwirtschaftsbetrieb München](https://www.awm-muenchen.de/), Germany.
|
||||
|
||||
|
||||
## Configuration via configuration.yaml
|
||||
|
||||
```yaml
|
||||
waste_collection_schedule:
|
||||
sources:
|
||||
- name: awm_muenchen_de
|
||||
args:
|
||||
street: STREET
|
||||
house_number: HNR
|
||||
b_collect_cycle: COLLECTION CYCLE ID
|
||||
p_collect_cycle: COLLECTION CYCLE ID
|
||||
r_collect_cycle: COLLECTION CYCLE ID
|
||||
```
|
||||
|
||||
### Configuration Variables
|
||||
|
||||
**street**
|
||||
*(string) (required)*
|
||||
|
||||
**house_number**
|
||||
*(string) (required)*
|
||||
|
||||
**b_collect_cycle**
|
||||
*(string) (optional) (default: "")*
|
||||
|
||||
**p_collect_cycle**
|
||||
*(string) (optional) (default: "")*
|
||||
|
||||
**r_collect_cycle**
|
||||
*(string) (optional) (default: "")*
|
||||
|
||||
## Example
|
||||
|
||||
```yaml
|
||||
waste_collection_schedule:
|
||||
sources:
|
||||
- name: awm_muenchen_de
|
||||
args:
|
||||
street: "Waltenbergerstr."
|
||||
house_number: "1"
|
||||
- name: awm_muenchen_de
|
||||
args:
|
||||
street: "Neureutherstr."
|
||||
house_number: "8"
|
||||
r_collect_cycle: "1/2;G"
|
||||
```
|
||||
|
||||
Some addresses have different bin collection cycles (ex: weekly, bi-weekly). For these addresses the optional parameters are required.
|
||||
|
||||
## How to get the optional configuration arguments
|
||||
|
||||
- Setup the component without the optional parameter and restart Home Assistant
|
||||
- Check the Home Assistant log for entries from this component.
|
||||
- The available options are listed in the error message.
|
||||
- Adjust the configuration and restart Home Assistant.
|
||||
|
||||
## Examples
|
||||
|
||||
### Waltenbergerstr. 1
|
||||
|
||||
```yaml
|
||||
waste_collection_schedule:
|
||||
sources:
|
||||
- name: awm_muenchen_de
|
||||
args:
|
||||
street: "Waltenbergerstr."
|
||||
house_number: "1"
|
||||
```
|
||||
|
||||
### Neureutherstr. 8 with an collection cycle option
|
||||
|
||||
```yaml
|
||||
waste_collection_schedule:
|
||||
sources:
|
||||
- name: awm_muenchen_de
|
||||
args:
|
||||
street: "Neureutherstr."
|
||||
house_number: "8"
|
||||
r_collect_cycle: "1/2;G"
|
||||
```
|
||||
@@ -159,7 +159,6 @@ This source has been successfully tested with the following service providers:
|
||||
- [Abfallwirtschaft Sonneberg](/doc/ics/abfallwirtschaft_sonneberg_de.md) / abfallwirtschaft-sonneberg.de
|
||||
- [Abfallwirtschaftsbetrieb Ilm-Kreis](/doc/ics/ilm_kreis_de.md) / ilm-kreis.de
|
||||
- [Abfallwirtschaftsbetrieb Landkreis Karlsruhe](/doc/ics/awb_landkreis_karlsruhe_de.md) / awb-landkreis-karlsruhe.de
|
||||
- [Abfallwirtschaftsbetrieb München](/doc/ics/awm_muenchen_de.md) / awm-muenchen.de
|
||||
- [Abfallwirtschaftsgesellschaft Landkreis Schaumburg](/doc/ics/aws_shg_de.md) / aws-shg.de
|
||||
- [ALBA Braunschweig](/doc/ics/alba_bs_de.md) / alba-bs.de
|
||||
- [Altmarkkreis Salzwedel](/doc/ics/abfall_app_net.md) / altmarkkreis-salzwedel.de
|
||||
|
||||
Reference in New Issue
Block a user