Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for kcm (klikogroep / klikocontainermanager) #361

Merged
merged 9 commits into from
Dec 28, 2024
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ _Component to integrate with the following providers/communities. Be aware that
| rad (ximmio) |
| westland (ximmio) |
| woerden (ximmio) |
| oudeijsselstreek (klikogroep, needs user / pass) |

This custom component dynamically creates sensor.afvalwijzer\_\* items. For me personally the items created are gft, restafval, papier, pmd and kerstbomen. Look in the states overview in the developer tools in Home Assistant what the sensor names for your region are and modify where necessary.

Expand Down Expand Up @@ -198,6 +199,8 @@ Here's an example of my own Home Asisstant config: https://github.com/xirixiz/ho
postal_code: 1234AB # (required, default = '')
street_number: 5 # (required, default = '')
suffix: '' # (optional, default = '')
username: '' # (optional, default = '')
password: '' # (optional, default = '')
exclude_pickup_today: true # (optional, default = true) to take or not to take Today into account in the next pickup.
date_isoformat: false # (optional, default = false) show the date in full isoformat if desired. Example: "2024-01-14T08:40:33.993521"
default_label: geen # (optional, default = geen) label if no date found
Expand Down
92 changes: 92 additions & 0 deletions custom_components/afvalwijzer/collector/klikogroep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from ..const.const import _LOGGER, SENSOR_COLLECTORS_KLIKOGROEP
from ..common.main_functions import _waste_type_rename

import requests
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


def get_waste_data_raw(provider, username, password):
url = SENSOR_COLLECTORS_KLIKOGROEP[provider]['url']
app = SENSOR_COLLECTORS_KLIKOGROEP[provider]['app']

headers = {
'Content-Type': 'application/json',
'Referer': url,
}

##########################################################################
# First request: login and get token
##########################################################################
data = {
"cardNumber": username,
"password": password,
"clientName": provider,
"app": app,
}

try:
raw_response = requests.post(url="{}/loginWithPassword".format(url), timeout=60, headers=headers, json=data)
raw_response.raise_for_status()
except requests.exceptions.RequestException as err:
raise ValueError(err) from err

try:
response = raw_response.json()
except ValueError as err:
raise ValueError(f"Invalid and/or no data received from {url}/loginWithPassword") from err

if 'success' not in response or not response['success']:
_LOGGER.error('Login failed. Check card number (username) and / or password!')
return

token = response["token"]

##########################################################################
# Second request: get the dates
##########################################################################
data = {
"token": token,
"clientName": provider,
"app": app,
}

try:
raw_response = requests.post(url="{}/getMyWasteCalendar".format(url), timeout=60, headers=headers, json=data)
raw_response.raise_for_status()
except requests.exceptions.RequestException as err:
raise ValueError(err) from err

try:
response = raw_response.json()
except ValueError as err:
raise ValueError(f"Invalid and/or no data received from {url}/getMyWasteCalendar") from err

waste_data_raw = []
waste_type_mapping = {}
for waste_type in response['fractions']:
waste_type_mapping[waste_type['id']] = _waste_type_rename(waste_type['name'].lower())

for pickup_date in response["dates"]:
num_pickup = len(response["dates"][pickup_date][0])
for idx in range(0, num_pickup):
pick_up = response["dates"][pickup_date][0][idx]
if pick_up != 0:
waste_data_raw.append({
"type": waste_type_mapping[pick_up],
"date": pickup_date,
})

##########################################################################
# Third request: invalidate token / close session
##########################################################################
data = {
"token": token,
"clientName": provider,
"app": app,
}

response = requests.post(url="{}/logout".format(url), timeout=60, headers=headers, json=data).json()
# We really don't care about this result, honestly.

return waste_data_raw
13 changes: 12 additions & 1 deletion custom_components/afvalwijzer/collector/main_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
SENSOR_COLLECTORS_CIRCULUS,
SENSOR_COLLECTORS_DEAFVALAPP,
SENSOR_COLLECTORS_ICALENDAR,
SENSOR_COLLECTORS_KLIKOGROEP,
SENSOR_COLLECTORS_OPZET,
SENSOR_COLLECTORS_RD4,
SENSOR_COLLECTORS_ROVA,
SENSOR_COLLECTORS_XIMMIO_IDS
)

try:
from . import afvalalert, burgerportaal, circulus, deafvalapp, icalendar, mijnafvalwijzer, opzet, rd4, rova, rwm, ximmio
from . import afvalalert, burgerportaal, circulus, deafvalapp, icalendar, klikogroep, mijnafvalwijzer, opzet, rd4, rova, rwm, ximmio
except ImportError as err:
_LOGGER.error(f"Import error {err.args}")

Expand All @@ -26,6 +27,8 @@ def __init__(
postal_code,
street_number,
suffix,
username,
password,
exclude_pickup_today,
date_isoformat,
exclude_list,
Expand All @@ -36,6 +39,8 @@ def __init__(
self.postal_code = str(postal_code).strip().upper()
self.street_number = str(street_number).strip()
self.suffix = str(suffix).strip().lower()
self.username = str(username).strip().lower()
self.password = str(password)

# Handle boolean and string parameters correctly
self.exclude_pickup_today = str(exclude_pickup_today).lower() if isinstance(
Expand Down Expand Up @@ -89,6 +94,12 @@ def __init__(
self.street_number,
self.suffix,
)
elif provider in SENSOR_COLLECTORS_KLIKOGROEP.keys():
waste_data_raw = klikogroep.get_waste_data_raw(
self.provider,
self.username,
self.password,
)
elif provider in SENSOR_COLLECTORS_OPZET.keys():
waste_data_raw = opzet.get_waste_data_raw(
self.provider,
Expand Down
3 changes: 2 additions & 1 deletion custom_components/afvalwijzer/common/main_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def _waste_type_rename(item_name):
"gft & etensresten": "gft",
"glass": "glas",
"gft afval": "gft",
"gft+e": "gft",
"green": "gft",
"groene container": "gft",
"groente": "gft",
Expand All @@ -26,6 +27,7 @@ def _waste_type_rename(item_name):
"packages": "pmd",
"pap": "papier",
"paper": "papier",
"pbd": "pmd",
"pdb": "pmd",
"papier en karton": "papier",
"papierinzameling": "papier",
Expand All @@ -35,7 +37,6 @@ def _waste_type_rename(item_name):
"plastic, blik & drinkpakken": "pmd",
"plastic, blik & drinkpakken arnhem": "pmd",
"plastic, blik & drinkpakken overbetuwe": "pmd",
"pmd": "pmd",
"pmdrest": "pmd-restafval",
"pmd-zak": "pmd",
"pruning_waste": "snoeiafval",
Expand Down
4 changes: 4 additions & 0 deletions custom_components/afvalwijzer/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
CONF_POSTAL_CODE,
CONF_STREET_NUMBER,
CONF_SUFFIX,
CONF_USERNAME,
CONF_PASSWORD,
CONF_EXCLUDE_PICKUP_TODAY,
CONF_DATE_ISOFORMAT,
CONF_DEFAULT_LABEL,
Expand All @@ -20,6 +22,8 @@
vol.Required(CONF_POSTAL_CODE): cv.string,
vol.Required(CONF_STREET_NUMBER): cv.string,
vol.Optional(CONF_SUFFIX, default=""): cv.string,
vol.Optional(CONF_USERNAME, default=""): cv.string,
vol.Optional(CONF_PASSWORD, default=""): cv.string,
vol.Optional(CONF_EXCLUDE_PICKUP_TODAY, default=True): cv.boolean,
vol.Optional(CONF_DATE_ISOFORMAT, default=False): cv.boolean,
vol.Optional(CONF_DEFAULT_LABEL, default="geen"): cv.string,
Expand Down
9 changes: 9 additions & 0 deletions custom_components/afvalwijzer/const/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@
"veldhoven": "https://www.veldhoven.nl/afvalkalender/{5}/{1}-{2}.ics",
}

SENSOR_COLLECTORS_KLIKOGROEP = {
"oudeijsselstreek": {
"url": "https://cp-oudeijsselstreek.klikocontainermanager.com/MyKliko",
"app": "cp-oudeijsselstreek.kcm.com"
}
}

SENSOR_COLLECTORS_AFVALWIJZER = [
"mijnafvalwijzer",
"afvalstoffendienstkalender",
Expand Down Expand Up @@ -128,6 +135,8 @@
CONF_POSTAL_CODE = "postal_code"
CONF_STREET_NUMBER = "street_number"
CONF_SUFFIX = "suffix"
CONF_USERNAME = "username"
CONF_PASSWORD = "password"
CONF_DATE_FORMAT = "date_format"
CONF_EXCLUDE_PICKUP_TODAY = "exclude_pickup_today"
CONF_DEFAULT_LABEL = "default_label"
Expand Down
10 changes: 10 additions & 0 deletions custom_components/afvalwijzer/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
CONF_POSTAL_CODE,
CONF_STREET_NUMBER,
CONF_SUFFIX,
CONF_USERNAME,
CONF_PASSWORD,
SCAN_INTERVAL,
)
from .sensor_custom import CustomSensor
Expand All @@ -33,6 +35,8 @@
vol.Required(CONF_POSTAL_CODE): cv.string,
vol.Required(CONF_STREET_NUMBER): cv.string,
vol.Optional(CONF_SUFFIX, default=""): cv.string,
vol.Optional(CONF_USERNAME, default=""): cv.string,
vol.Optional(CONF_PASSWORD, default=""): cv.string,
vol.Optional(CONF_EXCLUDE_PICKUP_TODAY, default=True): cv.boolean,
vol.Optional(CONF_DATE_ISOFORMAT, default=False): cv.boolean,
vol.Optional(CONF_EXCLUDE_LIST, default=""): cv.string,
Expand Down Expand Up @@ -63,6 +67,8 @@ async def _setup_sensors(hass, config, async_add_entities):
postal_code = config.get(CONF_POSTAL_CODE)
street_number = config.get(CONF_STREET_NUMBER)
suffix = config.get(CONF_SUFFIX, "")
username = config.get(CONF_USERNAME, "")
password = config.get(CONF_PASSWORD, "")
exclude_pickup_today = config.get(CONF_EXCLUDE_PICKUP_TODAY, True)
date_isoformat = config.get(CONF_DATE_ISOFORMAT, False)
exclude_list = config.get(CONF_EXCLUDE_LIST, "")
Expand Down Expand Up @@ -125,6 +131,8 @@ def update(self):
postal_code = self.config.get(CONF_POSTAL_CODE)
street_number = self.config.get(CONF_STREET_NUMBER)
suffix = self.config.get(CONF_SUFFIX)
username = self.config.get(CONF_USERNAME)
password = self.config.get(CONF_PASSWORD)
exclude_pickup_today = self.config.get(CONF_EXCLUDE_PICKUP_TODAY)
date_isoformat = self.config.get(CONF_DATE_ISOFORMAT)
default_label = self.config.get(CONF_DEFAULT_LABEL)
Expand All @@ -136,6 +144,8 @@ def update(self):
postal_code,
street_number,
suffix,
username,
password,
exclude_pickup_today,
date_isoformat,
exclude_list,
Expand Down
4 changes: 3 additions & 1 deletion custom_components/afvalwijzer/sensor_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
CONF_POSTAL_CODE,
CONF_STREET_NUMBER,
CONF_SUFFIX,
CONF_USERNAME,
CONF_PASSWORD,
CONF_DATE_ISOFORMAT,
SENSOR_ICON,
SENSOR_PREFIX,
Expand Down Expand Up @@ -48,7 +50,7 @@ def __init__(self, hass, waste_type, fetch_data, config):
self._state = self._default_label
self._icon = SENSOR_ICON
self._unique_id = hashlib.sha1(
f"{waste_type}{config.get(CONF_ID)}{config.get(CONF_POSTAL_CODE)}{config.get(CONF_STREET_NUMBER)}{config.get(CONF_SUFFIX, '')}".encode(
f"{waste_type}{config.get(CONF_ID)}{config.get(CONF_POSTAL_CODE)}{config.get(CONF_STREET_NUMBER)}{config.get(CONF_SUFFIX, '')}{config.get(CONF_USERNAME, '')}{config.get(CONF_PASSWORD, '')}".encode(
"utf-8"
)
).hexdigest()
Expand Down
2 changes: 2 additions & 0 deletions custom_components/afvalwijzer/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"postal_code": "Postal code (e.g., 1234AB)",
"street_number": "Street number",
"suffix": "Address suffix",
"username": "Provider username",
"password": "Provider password",
"exclude_pickup_today": "Exclude today's pickup",
"date_isoformat": "Use ISO date format",
"default_label": "Default label when no data is available",
Expand Down
2 changes: 2 additions & 0 deletions custom_components/afvalwijzer/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"postal_code": "Postcode (bijv. 1234AB)",
"street_number": "Huisnummer",
"suffix": "Huisnummer toevoeging",
"username": "Provider gebruikersnaam",
"password": "Provider wachtwoord",
"exclude_pickup_today": "Sluit ophalen van vandaag uit",
"date_isoformat": "Gebruik ISO-datumformaat",
"default_label": "Standaard label bij geen datum bekend",
Expand Down
Loading