Adding renoweb_dk (#1795)

* Added renoweb.dk

* README.md updated

* info.md updated

* requested modifications

* requested modifications

* reformatting

---------

Co-authored-by: Rune <{ID}+{username}@users.noreply.github.com>
Co-authored-by: 5ila5 <5ila5@users.noreply.github.com>
This commit is contained in:
r-poulsen
2024-02-17 22:32:55 +01:00
committed by GitHub
parent 5a521c39b5
commit e89b1251bc
4 changed files with 212 additions and 1 deletions

View File

@@ -498,6 +498,7 @@ Waste collection schedules in the following formats and countries are supported.
<summary>Denmark</summary>
- [Renosyd](/doc/source/renosyd_dk.md) / renosyd.dk
- [RenoWeb](/doc/source/renoweb_dk.md) / renoweb.dk
</details>
<details>

View File

@@ -0,0 +1,134 @@
"""Support for Renoweb waste collection schedule."""
import json
import logging
import re
from datetime import datetime
from typing import List
import requests
from waste_collection_schedule import Collection # type: ignore[attr-defined]
TITLE = "RenoWeb"
DESCRIPTION = "RenoWeb collections"
URL = "https://renoweb.dk"
API_URL = "https://{municipality}.renoweb.dk/Legacy/JService.asmx/{{endpoint}}"
TEST_CASES = {
"test_01": {
"municipality": "frederiksberg",
"address": "Roskildevej 40",
},
"test_02": {
"municipality": "htk",
"address_id": 45149,
},
"test_03": {
"municipality": "rudersdal",
"address": "Stationsvej 38",
},
}
_LOGGER = logging.getLogger("waste_collection_schedule.renoweb_dk")
class Source:
"""Source class for RenoWeb."""
_api_url: str
__address_id: int
def __init__(
self,
municipality: str,
address: str | None = None,
address_id: int | None = None,
):
_LOGGER.debug(
"Source.__init__(); municipality=%s, address_id=%s, address=%s",
municipality,
address_id,
address,
)
self._api_url = API_URL.format(municipality=municipality.lower())
if address_id:
self.__address_id = address_id
elif address:
self._address = address
else:
raise ValueError("Either address or address_id must be provided")
self._session = requests.Session()
self._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",
}
def _get_address_id(self) -> None:
"""Get the address id."""
response = self._session.post(
url=self._api_url.format(endpoint="Adresse_SearchByString"),
json={"searchterm": f"{self._address},", "addresswithmateriel": 3},
)
response.raise_for_status()
_LOGGER.debug(
"Address '%s'; id %s",
json.loads(response.json()["d"])["list"][0]["label"],
json.loads(response.json()["d"])["list"][0]["value"],
)
self.__address_id = json.loads(response.json()["d"])["list"][0]["value"]
@property
def _address_id(self) -> int:
"""Return the address id."""
if not hasattr(self, "__address_id"):
self._get_address_id()
return self.__address_id
def fetch(self) -> List[Collection]:
"""Fetch data from RenoWeb."""
_LOGGER.debug("Source.fetch()")
entries: list[Collection] = []
response = self._session.post(
url=self._api_url.format(endpoint="GetAffaldsplanMateriel_mitAffald"),
json={"adrid": self._address_id, "common": False},
)
response.raise_for_status()
# For some reason the response is a JSON structure inside a JSON string
for entry in json.loads(response.json()["d"])["list"]:
if not entry["afhentningsbestillingmateriel"] and re.search(
r"dag den \d{2}-\d{2}-\d{4}", entry["toemningsdato"]
):
response = self._session.post(
url=self._api_url.format(endpoint="GetCalender_mitAffald"),
json={"materialid": entry["id"]},
)
response.raise_for_status()
entry["name"] = " - ".join(
[entry["ordningnavn"], entry["materielnavn"]]
)
for date in [
datetime.strptime(date_string.split()[-1], "%d-%m-%Y").date()
for date_string in json.loads(response.json()["d"])["list"]
]:
entries.append(Collection(date=date, t=entry["name"]))
return entries

76
doc/source/renoweb_dk.md Normal file
View File

@@ -0,0 +1,76 @@
# Renoweb
Support for schedules provided by Sweco's [RenoWeb](https://renoweb.dk/), serving many Danish municipalities.
## Configuration via configuration.yaml
```yaml
waste_collection_schedule:
sources:
- name: renoweb_dk
args:
municipality: MUNICIPALITY
address: ADDRESS
address_id: ADDRESS_ID
```
### Configuration Variables
**municipality**
_(String) (required)_
The name of the municipality as it appears in the URL. E.g. https://htk.renoweb.dk/Legacy/selvbetjening/mit_affald.aspx where "htk" is for Høje-Taastrup municipality.
**address**
_(String) (required)_
The address to look up. It should be exactly as it is on the website until the comma between the street address and the postal code.
**address_id**
_(Int) (optional)_
Use address_id if the address lookup fails.
## Example
```yaml
waste_collection_schedule:
sources:
- name: renoweb_dk
args:
municipality: frederiksberg
address: "Roskildevej 40"
customize:
- type: "Haveaffald - Haveaffald henteordning (1 stk.)"
alias: "Haveaffald"
```
## How to find the address_id
Go to the RenoWeb site for your municipality, e.g. https://htk.renoweb.dk/Legacy/selvbetjening/mit_affald.aspx if you are lucky enough to live in Høje-Taastrup.
Open the developer console in your browser.
Enter your address and select it in the dropdown menu. (Note that the Ejd.nr. in the dropdown is _not_ the ID we are looking for).
In the Network tab in the browser console, find the latest URL ending in **Adresse_SearchByString** and look in under Response, where you should see a chunk of JSON-ish data. The ID you need to use for adress_id is in the "value" field.
```json
d: '{"list":[{"value":"45149","label":"Rådhusstræde 1, 2630 Taastrup (Ejd.nr. 186783)"}],"status":{"id":0,"status":"Ok","msg":""}}'
```
### Customizing Waste Types
Customizing waste types is a feature of the Waste Collection Schedule component and is very useful here, since the waste types in RenoWeb are often long and not very consistent.
```yaml
waste_collection_schedule:
sources:
- name: renoweb_dk
args:
municipality: frederiksberg
address: "Roskildevej 40"
customize:
- type: "Haveaffald - Haveaffald henteordning (1 stk.)"
alias: "Haveaffald"
```

File diff suppressed because one or more lines are too long