Update 9-1-2022: added javascript version.
To retrieve the garbage pickup dates, the following rule can be used. This rule updates a number of items:
Group gAfvalData "Afval ophaaldata" <calendar> (gTuin)
DateTime AfvalData_gft "Groente, Fruit en Tuin [%1$ta %1$td/%1$tm]" <garbage_gft> (gAfvalData)
DateTime AfvalData_papier "Papier [%1$ta %1$td/%1$tm]" <garbage_papier> (gAfvalData)
DateTime AfvalData_restafval "Restafval [%1$ta %1$td/%1$tm]" <garbage_restafval> (gAfvalData)
DateTime AfvalData_pmd "Plastic [%1$ta %1$td/%1$tm]" <garbage_pmd> (gAfvalData)
DateTime AfvalData_kca "Chemisch [%1$ta %1$td/%1$tm]" <garbage_kca> (gAfvalData)
DateTime AfvalData_volgendeDatum "Volgende afvalophaaldag [%1$ta %1$td/%1$tm]" <clock> (gAfvalData)
String AfvalData_volgendeType "Volgende afvalophaalsoort" (gAfvalData)
To use the rule you have to install the javascript scripting add-in. Furthermore, you have to edit the CONFIG object according to your personal situation. When you want to use mijnafvalwijzer.nl
, you have to provide an API key. I cannot provide that, so you have to use Google to find it. I’m planning to adapt the script to make a scraper version for mijnafvalwijzer.nl
when I find more time. Then the API key will no longer be needed.
/**
*
* Dit script bevat een JSRule die afval ophaaldata kan lezen van diverse afvalkalenders. De afvalkalenders worden
* twee maal per dag uitgelazen.
*
* Creëer de volgende Items om met de afvalkalender te kunnen werken:
*
* Group gAfvalData "Afval ophaaldata" <calendar> (gTuin)
* DateTime AfvalData_gft "Groente, Fruit en Tuin [%1$ta %1$td/%1$tm]" <garbage_gft> (gAfvalData)
* DateTime AfvalData_papier "Papier [%1$ta %1$td/%1$tm]" <garbage_papier> (gAfvalData)
* DateTime AfvalData_restafval "Restafval [%1$ta %1$td/%1$tm]" <garbage_restafval> (gAfvalData)
* DateTime AfvalData_pmd "Plastic [%1$ta %1$td/%1$tm]" <garbage_pmd> (gAfvalData)
* DateTime AfvalData_kca "Chemisch [%1$ta %1$td/%1$tm]" <garbage_kca> (gAfvalData)
* DateTime AfvalData_volgendeDatum "Volgende afvalophaaldag [%1$ta %1$td/%1$tm]" <clock> (gAfvalData)
* String AfvalData_volgendeType "Volgende afvalophaalsoort" (gAfvalData)
*
* En pas daarna de onderstaande configuratie aan.
* Voor MijnAfvalwijzer zijn de volgende gegevens nodig:
* postcode, huisnummer, toevoeging en apikey
* Voor Afvalkalender zijn de volgende gegevens nodig:
* postcode, huisnummer, afvalKalenderUri
*
* Kies voor de Afvalkalender eventueel een afvalkalender uit de onderstaande lijst
*
* Cyclus NV: https://afvalkalender.cyclusnv.nl
* HVC: https://apps.hvcgroep.nl
* Dar: https://afvalkalender.dar.nl
* Afvalvrij / Circulus-Berkel: https://afvalkalender.circulus-berkel.nl
* Meerlanden: https://afvalkalender.meerlanden.nl
* Cure: https://afvalkalender.cure-afvalbeheer.nl
* Avalex: https://www.avalex.nl
* RMN: https://inzamelschema.rmn.nl
* Venray: https://afvalkalender.venray.nl
* Den Haag: https://huisvuilkalender.denhaag.nl
* Berkelland: https://afvalkalender.gemeenteberkelland.nl
* Alphen aan den Rijn: https://afvalkalender.alphenaandenrijn.nl
* Waalre: https://afvalkalender.waalre.nl
* ZRD: https://afvalkalender.zrd.nl
* Spaarnelanden: https://afvalwijzer.spaarnelanden.nl
* Montfoort: https://afvalkalender.montfoort.nl
* GAD: https://inzamelkalender.gad.nl
* Cranendonck: https://afvalkalender.cranendonck.nl
*
* Deze lijst is overgenomen uit: https://www.gadget-freakz.com/domoticz-dzvents-getgarbagedates-script/
*
*/
const logger = log('afvalkalender');
// const MijnAfvalWijzerConfig = {
// huisnummer: "41",
// toevoeging: '',
// postcode: "3825AL",
// apikey: "<voer-hier-de-apikey-in>"
// };
const AfvalkalenderConfig = {
huisnummer: "17",
postcode: "5581BG",
afvalkalenderUri: 'https://afvalkalender.waalre.nl'
};
//const CONFIG = MijnAfvalWijzerConfig;
const CONFIG = AfvalkalenderConfig;
//CONFIG.itemPrefix = "MijnEigenAfvalItem";
rules.JSRule({
name: "Afval ophaaldata updaten",
description: "Haal gegevens op uit de afvalwijzer en/of afvalkalender over ophalen van afval",
triggers: [
triggers.GenericCronTrigger('0 30 7 * * ?'),
triggers.GenericCronTrigger('0 30 19 * * ?')
],
execute: data => {
const { postcode, huisnummer, toevoeging } = CONFIG;
itemPrefix = CONFIG.itemPrefix ?? "AfvalData";
logger.debug(`Afval ophaaldata ophalen voor ${postcode}-${huisnummer}${toevoeging}`);
if ('afvalkalenderUri' in CONFIG && CONFIG.afvalkalenderUri)
pickupdates = getPickupdatesAfvalkalender(postcode, huisnummer, CONFIG.afvalkalenderUri);
else
pickupdates = getPickupdatesMijnafvalwijzer(postcode, huisnummer, toevoeging, CONFIG.apikey);
if (!pickupdates || pickupdates.length == 0) {
logger.warn(`Kon ophaaldata niet downloaden voor ${postcode}-${huisnummer}${toevoeging}`);
return;
}
processPickupdates(pickupdates, 'papier', 'oud papier', `${itemPrefix}_papier`);
processPickupdates(pickupdates, 'gft', 'groente-, fruit- en tuinafval', `${itemPrefix}_gft`);
processPickupdates(pickupdates, 'restafval', 'restafval', `${itemPrefix}_restafval`);
processPickupdates(pickupdates, 'pmd', 'plastic- en metaalafval en drinkkartons', `${itemPrefix}_pmd`);
processPickupdates(pickupdates, 'kca', 'klein chemisch afval', `${itemPrefix}_kca`);
setNextDateAndType(pickupdates, `${itemPrefix}_volgendeDatum`, `${itemPrefix}_volgendeType`);
}
});
class pickupDate {
constructor(date, type) {
this.date = date;
this.type = type;
}
}
function processPickupdates(dates, afvalType, afvalTypeDescription, item, now = undefined) {
if (!pickupdates || pickupdates.length == 0)
return;
const afvalTypeDates = dates.filter(d => d.type == afvalType).map(d => d.date);
if (afvalTypeDates.length == 0)
return;
if (now == undefined)
now = time.ZonedDateTime.now();
logger.debug(`Ophaaldata verwerken voor ${afvalType}: ${afvalTypeDescription} en Item ${item} on ${now}`);
if (items.getItem(item, true) == null) {
logger.warn(`Kan ophaaldata niet verwerken want Item ${item} bestaat niet`);
return;
}
const today = now.toLocalDate();
const tomorrow = today.plusDays(1);
let pickupDate = afvalTypeDates.find(d => !d.isBefore(today));
if (pickupDate == undefined)
pickupDate = afvalTypeDates.at(-1);
items.getItem(item).postUpdate(pickupDate.format(time.DateTimeFormatter.ISO_LOCAL_DATE));
const currentTime = now.toLocalTime();
if (today.isEqual(pickupDate) && currentTime.hour() <= 12) {
message = `Vandaag wordt het ${afvalTypeDescription} opgehaald.`;
logger.info(message);
actions.NotificationAction.sendBroadcastNotification(message);
}
else if (tomorrow.isEqual(pickupDate) && currentTime.hour() > 12) {
message = `Morgen wordt het ${afvalTypeDescription} opgehaald.`;
logger.info(message);
actions.NotificationAction.sendBroadcastNotification(message);
}
}
function setNextDateAndType(dates, nextDateItem, nextTypeItem, now=undefined)
{
if (items.getItem(nextDateItem, true) == null) {
logger.warn(`Kan volgende ophaaldatum niet verwerken want Item ${nextDateItem} bestaat niet`);
return;
}
if (items.getItem(nextTypeItem, true) == null) {
logger.warn(`Kan volgende ophaaldatum niet verwerken want Item ${nextTypeItem} bestaat niet`);
return;
}
now = now ?? time.ZonedDateTime.now();
const today = now.toLocalDate();
const sortedDates = dates.sort((d1, d2) => d1.date.compareTo(d2.date));
let nextDate = sortedDates.find(d => !d.date.isBefore(today));
if (nextDate == undefined)
nextDate = sortedDates.at(-1);
items.getItem(nextDateItem).postUpdate(nextDate.date.format(time.DateTimeFormatter.ISO_LOCAL_DATE));
items.getItem(nextTypeItem).postUpdate(nextDate.type);
}
function getJSONResponse(uri)
{
const result = actions.HTTP.sendHttpGetRequest(uri);
if (!result)
{
logger.warn(`Geen respons gekregen van ${uri}`);
return undefined;
}
try {
return JSON.parse(result);
}
catch (error)
{
logger.warn(`Kon respons van ${uri} niet naar JSON omzetten: ${error}`);
return undefined;
}
}
/**
* Gets pickup dates from different garbage collectors from mijnafvalwijzer.
*/
function getPickupdatesMijnafvalwijzer(postcode, huisnummer, toevoeging, apikey) {
const dateFormatter = time.DateTimeFormatter.ofPattern('yyyy-MM-dd');
const today = time.ZonedDateTime.now().format(dateFormatter);
const uri = `https://api.mijnafvalwijzer.nl/webservices/appsinput/?method=postcodecheck&street=&postcode=${postcode}&huisnummer=${huisnummer}&toevoeging=${toevoeging}&apikey=${apikey}&app_name=afvalwijzer&platform=phone&mobiletype=android&afvaldata=${today}&version=58&langs=nl`;
logger.debug(`Afvalwijzer URI: ${uri}`);
try {
const response = getJSONResponse(uri);
const ophaaldagen = response.ophaaldagen;
if (ophaaldagen.response !== 'OK')
{
logger.warn(`Ongeldige respons bij het downloaden van ophaaldata van ${uri}: ${pickupdates.error}`);
return undefined;
}
pickupdates = [];
return ophaaldagen.data.map(d => new pickupDate(time.LocalDate.parse(d.date, dateFormatter), d.type));
}
catch (error)
{
logger.warn(`Kon ophaaldata niet destileren uit respons van ${uri}: ${error}`);
return undefined;
}
}
/**
* Gets pickup dates from different garbage collectors from afvalkalender
*/
function getPickupdatesAfvalkalender(postcode, huisnummer, afvalkalenderUri) {
const uriAddress=`${afvalkalenderUri}/rest/adressen/${postcode}-${huisnummer}`;
const responseAddress = getJSONResponse(uriAddress);
if (!responseAddress) {
logger.warn(`Kon adresgegevens niet downloaden van ${uriAddress}`);
return undefined;
}
const bagId = responseAddress[0].bagId;
const uriPickupdates=`${afvalkalenderUri}/rest/adressen/${bagId}/afvalstromen`;
const responsePickupdates = getJSONResponse(uriPickupdates);
if (!responsePickupdates)
{
logger.warn(`Kon ophaaldata niet downloaden van ${uriPickupdates}`);
return undefined;
}
pickupdates = [];
return responsePickupdates
.filter(d => d.icon && d.ophaaldatum)
.map(d => new pickupDate(time.LocalDate.parse(d.ophaaldatum,time.DateTimeFormatter.ofPattern('yyyy-MM-dd')), d.icon === "rest" ? "restafval" : d.icon));
}
The Python/Jython version of this Rule is also available. For this you have to install the Jython addon and the Jython helper libraries. I don’t use Jython anymore, so this version will not receive updates.
'''
This script contains a rule that consults afvalkalenders
for garbage pickup times twice a day and update Items
with the dates. The script can create the Items needed, if they do not exist.
Please configure this script by filling in your address data in afvalkalender_configuration below.
Please select the base URI for your garbage collector provider from the list below
MijnAfvalWijzer: https://json.mijnafvalwijzer.nl
Cyclus NV: https://afvalkalender.cyclusnv.nl
HVC: https://apps.hvcgroep.nl
Dar: https://afvalkalender.dar.nl
Afvalvrij / Circulus-Berkel: https://afvalkalender.circulus-berkel.nl
Meerlanden: https://afvalkalender.meerlanden.nl
Cure: https://afvalkalender.cure-afvalbeheer.nl
Avalex: https://www.avalex.nl
RMN: https://inzamelschema.rmn.nl
Venray: https://afvalkalender.venray.nl
Den Haag: https://huisvuilkalender.denhaag.nl
Berkelland: https://afvalkalender.gemeenteberkelland.nl
Alphen aan den Rijn: https://afvalkalender.alphenaandenrijn.nl
Waalre: https://afvalkalender.waalre.nl
ZRD: https://afvalkalender.zrd.nl
Spaarnelanden: https://afvalwijzer.spaarnelanden.nl
Montfoort: https://afvalkalender.montfoort.nl
GAD: https://inzamelkalender.gad.nl
Cranendonck: https://afvalkalender.cranendonck.nl
List taken from: https://www.gadget-freakz.com/domoticz-dzvents-getgarbagedates-script/
'''
'''
This script contains a rule that consults afvalkalenders
for garbage pickup times twice a day and update Items
with the dates. The script can create the Items needed, if they do not exist.
Please configure this script by filling in your address data in afvalkalender_configuration below.
Please select the base URI for your garbage collector provider from the list below
MijnAfvalWijzer: https://json.mijnafvalwijzer.nl
Cyclus NV: https://afvalkalender.cyclusnv.nl
HVC: https://apps.hvcgroep.nl
Dar: https://afvalkalender.dar.nl
Afvalvrij / Circulus-Berkel: https://afvalkalender.circulus-berkel.nl
Meerlanden: https://afvalkalender.meerlanden.nl
Cure: https://afvalkalender.cure-afvalbeheer.nl
Avalex: https://www.avalex.nl
RMN: https://inzamelschema.rmn.nl
Venray: https://afvalkalender.venray.nl
Den Haag: https://huisvuilkalender.denhaag.nl
Berkelland: https://afvalkalender.gemeenteberkelland.nl
Alphen aan den Rijn: https://afvalkalender.alphenaandenrijn.nl
Waalre: https://afvalkalender.waalre.nl
ZRD: https://afvalkalender.zrd.nl
Spaarnelanden: https://afvalwijzer.spaarnelanden.nl
Montfoort: https://afvalkalender.montfoort.nl
GAD: https://inzamelkalender.gad.nl
Cranendonck: https://afvalkalender.cranendonck.nl
List taken from: https://www.gadget-freakz.com/domoticz-dzvents-getgarbagedates-script/
'''
import json
from datetime import date, datetime, timedelta
from core.rules import rule
from core.triggers import when
from core.items import add_item
from core.actions import HTTP
from core.utils import postUpdate as post_update
from personal.etxean_utils import push_notification
afvalkalender_configuration = {
# 'afvalkalender_url': 'https://json.mijnafvalwijzer.nl,
'huisnummer': "5",
'toevoeging': '',
'postcode': "1234AB",
'create_missing_items': False,
}
MIJNAFVALWIJZER_APIKEY = "<specify-key-here>"
@rule("TrashPickupdates", description="Haal de gegevens over ophalen van afval op uit de afvalwijzer", tags=["afvalkalender"])
@when("Time cron 0 30 7 * * ?")
@when("Time cron 0 30 19 * * ?")
def send_trash_pickup_notifications(event):
# Get data from mijnafvalwijzer
postcode = afvalkalender_configuration['postcode']
huisnummer = afvalkalender_configuration['huisnummer']
toevoeging = afvalkalender_configuration['toevoeging']
send_trash_pickup_notifications.log.debug('Getting pickup dates for {}-{}{}'.format(postcode, huisnummer, toevoeging))
if 'afvalkalender_uri' in afvalkalender_configuration and afvalkalender_configuration['afvalkalender_uri'] and afvalkalender_configuration['afvalkalender_uri'] != 'https://json.mijnafvalwijzer.nl':
pickupdates = get_pickupdates_afvalkalender(postcode, huisnummer, afvalkalender_configuration['afvalkalender_uri'])
else:
pickupdates = get_pickupdates_mijnafvalwijzer(postcode, huisnummer, toevoeging)
process_pickupdates(pickupdates, 'papier', 'oud papier', 'AfvalData_papier')
process_pickupdates(pickupdates, 'gft', 'groente-, fruit- en tuinafval', 'AfvalData_gft')
process_pickupdates(pickupdates, 'restafval', 'restafval', 'AfvalData_restafval')
process_pickupdates(pickupdates, 'pmd', 'plastic- en metaalafval en drinkkartons', 'AfvalData_pmd')
process_pickupdates(pickupdates, 'kca', 'klein chemisch afval', 'AfvalData_kca')
the_log = send_trash_pickup_notifications.log
def process_pickupdates(dates, type, description, item, now=None):
log = the_log
if not dates or len(dates) == 0:
return
pickup_dates = [x['date'] for x in dates if x['type'] == type]
if len(pickup_dates) == 0:
return
if now is None:
now = datetime.now()
log.debug('Processing pickup dates for {}: {} and item {} on {}'.format(type, description, item, now))
if ir.getItems(item) == []:
if afvalkalender_configuration['create_missing_items']:
add_item(item, item_type="DateTime", label="{} [%%1$td-%1$tm]".format(description), category="Calendar", groups=[], tags=[])
else:
return
today = str(now.date())
tomorrow = str(now.date() + timedelta(days=1))
next_date = next((d for d in pickup_dates if d >= today), pickup_dates[-1])
next_date_list = next_date.split("-")
pickup_date = DateTimeType().zonedDateTime.withYear(int(next_date_list[0])).withMonth(int(next_date_list[1])).withDayOfMonth(int(next_date_list[2])).withHour(0).withMinute(0).withSecond(0).withNano(0)
post_update(item, str(pickup_date))
current_time = now.time()
if today == next_date and current_time.hour <= 12:
message = "Vandaag wordt het {} opgehaald.".format(description)
log.info(message)
push_notification(message)
if tomorrow == next_date and current_time.hour > 12:
message = "Morgen wordt het {} opgehaald.".format(description)
log.info(message)
push_notification(message)
def get_json_response(uri):
log = the_log
the_page = HTTP.sendHttpGetRequest(uri)
if not the_page:
log.warn("Could not get response from {}".format(uri))
return None
try:
return json.loads(the_page.decode('utf-8'))
except Exception as ex:
log.warn('Could not interpret json response from {}: {}'.format(uri, ex))
return None
def get_pickupdates_mijnafvalwijzer(postcode, huisnummer, toevoeging=''):
'''Gets pickup dates from different garbage collectors from mijnafvalwijzer.'''
log = the_log
today = date.today().strftime("%d-%m-%Y")
uri = 'https://api.mijnafvalwijzer.nl/webservices/appsinput/?method=postcodecheck&street=&postcode={0}&huisnummer={1}&toevoeging={2}&apikey={3}&app_name=afvalwijzer&platform=phone&mobiletype=android&afvaldata={4}&version=58&langs=nl'.format(postcode, huisnummer, toevoeging, MIJNAFVALWIJZER_APIKEY,today)
log.info('Afvalkalender uri: {}'.format(uri))
pickupdates = {}
try:
response = get_json_response(uri)
pickupdates = response['ophaaldagen']
if pickupdates[u'response'] != u'OK':
log.warn('Invalid response while getting garbage pickup dates from {}: {}'.format(uri, pickupdates['error']))
return None
return pickupdates['data']
except Exception as ex:
log.warn('Could not interpret garbage pickup dates from {}: {}'.format(uri, ex))
return None
def get_pickupdates_afvalkalender(postcode, huisnummer, afvalkalender_uri):
'''Gets pickup dates from different garbage collectors.'''
log = the_log
uri_address='{0}/rest/adressen/{1}-{2}'.format(afvalkalender_uri, postcode, huisnummer)
response_address = get_json_response(uri_address)
if not response_address:
log.warn('Could not get garbage pickup information from {}'.format(uri_address))
return None
uri_pickupdates='{0}/rest/adressen/{1}/afvalstromen'.format(afvalkalender_uri, response_address[0]['bagId'])
response_pickupdates = get_json_response(uri_pickupdates)
if not response_pickupdates:
log.warn('Could not get garbage pickup dates from {}'.format(uri_pickupdates))
return None
pickupdates = []
for pickupdate in response_pickupdates:
newdate = {}
if pickupdate['icon'] and pickupdate['ophaaldatum']:
newdate['type'] = pickupdate['icon']
newdate['date'] = pickupdate['ophaaldatum']
if newdate['type'] == 'rest':
newdate['type'] = 'restafval'
pickupdates.append(newdate)
return pickupdates