diff --git a/CODEOWNERS b/CODEOWNERS
index d7365952a47..9f5136c9d11 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -101,6 +101,7 @@
/bundles/org.openhab.binding.haassohnpelletstove/ @chingon007
/bundles/org.openhab.binding.harmonyhub/ @digitaldan
/bundles/org.openhab.binding.haywardomnilogic/ @matchews
+/bundles/org.openhab.binding.hccrubbishcollection/ @cossey
/bundles/org.openhab.binding.hdanywhere/ @kgoderis
/bundles/org.openhab.binding.hdpowerview/ @beowulfe
/bundles/org.openhab.binding.helios/ @kgoderis
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index a26ae4965a1..c6d486fce12 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -491,6 +491,11 @@
org.openhab.binding.haywardomnilogic
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.hccrubbishcollection
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.hdanywhere
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/NOTICE b/bundles/org.openhab.binding.hccrubbishcollection/NOTICE
new file mode 100644
index 00000000000..38d625e3492
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/README.md b/bundles/org.openhab.binding.hccrubbishcollection/README.md
new file mode 100644
index 00000000000..8a6b4c232f1
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/README.md
@@ -0,0 +1,46 @@
+# HCC Rubbish Collection Binding
+
+A Hamilton City Council (NZ) _"Fight the Landfill"_ binding.
+This binding will keep track of your rubbish collection days and uses the [Fight the Landfill](https://www.fightthelandfill.co.nz/) website API to fetch the upcoming collection dates.
+
+## Supported Things
+
+A single supported thing called `collection`.
+
+## Thing Configuration
+
+The thing supports one setting labelled `address` which is your street number and name as it appears on Google.
+*For Example:
+1 Victoria Street*
+
+> Note: The above address example is not valid as it is a business address.
+
+*__If the address is not valid or rubbish collection service does not apply (for example, a business address) then a `CONFIGURATION_ERROR` will occur.__*
+
+## Channels
+
+| channel | type | description |
+| ---------------- | ------ | -------------------------------------------------------------------- |
+| day | Number | The upcoming rubbish collection day of the week (1=Monday, 7=Sunday) |
+| general | Date | The next general household (red bin) collection day |
+| recycling | Date | The next recycling (yellow bin, glass bin) colleciton day |
+| collection-event | Event | Event trigger on the day of the rubbish |
+
+### Collection Event
+
+The collection event `collection-event` triggers on the day of rubbish collection.
+
+#### Events
+
+| event | description |
+| --------- | ------------------------------- |
+| GENERAL | General household rubbish event |
+| RECYCLING | Recycling rubbish event |
+
+#### Configuration
+
+You can set an `offset` in minutes.
+This can then trigger the collection event before or after the normal time of 12:00am on the day of the collection.
+
+*For Example:
+If you want the event to trigger at 7pm the day before, to remind you to take out the bins, then set the `offset` to `-300` (5 hours x 60 minutes).*
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/pom.xml b/bundles/org.openhab.binding.hccrubbishcollection/pom.xml
new file mode 100644
index 00000000000..8414b72aa77
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 3.1.0-SNAPSHOT
+
+
+ org.openhab.binding.hccrubbishcollection
+
+ openHAB Add-ons :: Bundles :: HCC Rubbish Collection Binding
+
+
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/feature/feature.xml b/bundles/org.openhab.binding.hccrubbishcollection/src/main/feature/feature.xml
new file mode 100644
index 00000000000..b21a42aac3b
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.hccrubbishcollection/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/API.java b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/API.java
new file mode 100644
index 00000000000..c7ca5d86ad2
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/API.java
@@ -0,0 +1,200 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hccrubbishcollection.internal;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * The {@link API} contains all code relating to accessing the online rubbish collection API.
+ *
+ * @author Stewart Cossey - Initial contribution
+ */
+@NonNullByDefault
+public class API {
+ private static final int REQUEST_TIMEOUT = 10;
+ private static final String REQUEST_URL = "https://hccfightthelandfill.azure-api.net/get_Collection_Dates?address_string=";
+ private static final int HTTP_OK = 200;
+
+ private final Logger logger = LoggerFactory.getLogger(API.class);
+
+ private final HttpClient httpClient;
+ private final String address;
+
+ private String errorDetailMessage = "";
+ private ThingStatusDetail errorDetail = ThingStatusDetail.NONE;
+
+ private @Nullable Integer collectionWeek = null;
+ private @Nullable Integer day = null;
+ private @Nullable ZonedDateTime recycling = null;
+ private @Nullable ZonedDateTime general = null;
+
+ /**
+ * Create a new API class.
+ *
+ * @param httpClient The common http client provided from openHAB.
+ * @param address The address of the premises.
+ */
+ public API(HttpClient httpClient, String address) {
+ this.httpClient = httpClient;
+ this.address = address;
+ }
+
+ /**
+ * Connects to the web service and gets the data.
+ *
+ * @return boolean Success.
+ */
+ public boolean update() {
+ try {
+ final String url = REQUEST_URL + URLEncoder.encode(address, StandardCharsets.UTF_8.toString());
+
+ logger.debug("Fetching data from URL {} (address hidden)", REQUEST_URL);
+
+ ContentResponse response = httpClient.newRequest(url).timeout(REQUEST_TIMEOUT, TimeUnit.SECONDS).send();
+
+ if (response.getStatus() == HTTP_OK) {
+ String content = response.getContentAsString();
+ // Return response is encapsulated in square brackets, remove to create valid json.
+ String cleanedContent = content.trim().substring(1, content.length() - 1);
+ logger.trace("Got cleaned content: {}", cleanedContent);
+
+ JsonObject jsonResponse = JsonParser.parseString(cleanedContent).getAsJsonObject();
+
+ JsonElement dayElement = jsonResponse.get("CollectionDay");
+ JsonElement collectionWeekElement = jsonResponse.get("CollectionWeek");
+ JsonElement generalElement = jsonResponse.get("RedBin");
+ JsonElement recyclingElement = jsonResponse.get("YellowBin");
+
+ // The elements are missing if the address is invalid or council does not service (due to address being
+ // a business)
+ if (generalElement == null || recyclingElement == null) {
+ logger.debug("RedBin or YellowBin object is missing. Invalid premises or address");
+
+ errorDetail = ThingStatusDetail.CONFIGURATION_ERROR;
+ errorDetailMessage = "Invalid address";
+ return false;
+ }
+
+ // Get API dates as LocalDateTime objects.
+ LocalDateTime localGeneralDate = LocalDateTime.parse(generalElement.getAsString());
+ LocalDateTime localRecyclingDate = LocalDateTime.parse(recyclingElement.getAsString());
+
+ ZoneId zone = ZonedDateTime.now().getZone(); // Gets the local time zone.
+
+ // Convert LocalDateTime objects to be compatible with openHAB
+ ZonedDateTime zonedGeneralDate = ZonedDateTime.of(localGeneralDate, zone);
+ ZonedDateTime zonedRecyclingDate = ZonedDateTime.of(localRecyclingDate, zone);
+
+ errorDetail = ThingStatusDetail.NONE; // Sets to no error since we have successfully parsed response.
+
+ // Set the local properties with values from API.
+ recycling = zonedRecyclingDate;
+ general = zonedGeneralDate;
+
+ day = dayElement.getAsInt();
+ collectionWeek = collectionWeekElement.getAsInt();
+
+ return true;
+ } else {
+ logger.error("Data fetch failed, got HTTP Code {}", response.getStatus());
+ errorDetail = ThingStatusDetail.COMMUNICATION_ERROR;
+ errorDetailMessage = "HTTP Code " + response.getStatus();
+ return false;
+ }
+ } catch (UnsupportedEncodingException ue) {
+ errorDetail = ThingStatusDetail.COMMUNICATION_ERROR;
+ errorDetailMessage = "Encoding not supported!";
+ return false;
+ } catch (TimeoutException to) {
+ errorDetail = ThingStatusDetail.COMMUNICATION_ERROR;
+ errorDetailMessage = "Response Timeout (will try again soon)";
+ return false;
+ } catch (InterruptedException | ExecutionException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the last request status.
+ *
+ * @return ThingStatusDetail The openHAB error type.
+ */
+ public ThingStatusDetail getErrorDetail() {
+ return errorDetail;
+ }
+
+ /**
+ * Gets the error, if occurred.
+ *
+ * @return String The error message.
+ */
+ public String getErrorDetailMessage() {
+ return errorDetailMessage;
+ }
+
+ /**
+ * The collection week.
+ *
+ * @return Integer The week number.
+ */
+ public @Nullable Integer getCollectionWeek() {
+ return collectionWeek;
+ }
+
+ /**
+ * Gets the collection day of week.
+ *
+ * @return Integer The day of the week. 1 = Monday.
+ */
+ public @Nullable Integer getDay() {
+ return day;
+ }
+
+ /**
+ * The upcoming recycling collection date.
+ *
+ * @return ZonedDateTime
+ */
+ public @Nullable ZonedDateTime getRecyclingDate() {
+ return recycling;
+ }
+
+ /**
+ * The upcoming general rubbish collection date.
+ *
+ * @return ZonedDateTime
+ */
+ public @Nullable ZonedDateTime getGeneralDate() {
+ return general;
+ }
+}
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionBindingConstants.java b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionBindingConstants.java
new file mode 100644
index 00000000000..aac108ce87e
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionBindingConstants.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hccrubbishcollection.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link HCCRubbishCollectionBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Stewart Cossey - Initial contribution
+ */
+@NonNullByDefault
+public class HCCRubbishCollectionBindingConstants {
+
+ private static final String BINDING_ID = "hccrubbishcollection";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID THING_TYPE_COLLECTION = new ThingTypeUID(BINDING_ID, "collection");
+
+ // List of all Channel ids
+ public static final String CHANNEL_DAY = "day";
+ public static final String CHANNEL_BIN_GENERAL = "general";
+ public static final String CHANNEL_BIN_RECYCLING = "recycling";
+
+ public static final String TRIGGER_COLLECTION = "collection-event";
+ public static final String EVENT_RECYCLING = "RECYCLING";
+ public static final String EVENT_GENERAL = "GENERAL";
+}
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionConfiguration.java b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionConfiguration.java
new file mode 100644
index 00000000000..75af8d0db8f
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionConfiguration.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hccrubbishcollection.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HCCRubbishCollectionConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Stewart Cossey - Initial contribution
+ */
+@NonNullByDefault
+public class HCCRubbishCollectionConfiguration {
+ public String address = "";
+}
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionEventConfiguration.java b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionEventConfiguration.java
new file mode 100644
index 00000000000..34e0c3bfe67
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionEventConfiguration.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hccrubbishcollection.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HCCRubbishCollectionEventConfiguration} class defines configuration for the collection event channel.
+ *
+ * @author Stewart Cossey - Initial contribution
+ */
+@NonNullByDefault
+public class HCCRubbishCollectionEventConfiguration {
+ public int offset;
+}
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandler.java b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandler.java
new file mode 100644
index 00000000000..3a640d46be6
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandler.java
@@ -0,0 +1,282 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hccrubbishcollection.internal;
+
+import static org.openhab.binding.hccrubbishcollection.internal.HCCRubbishCollectionBindingConstants.*;
+
+import java.time.ZonedDateTime;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link HCCRubbishCollectionHandler} is responsible for handling commands,
+ * updating the channels and polling the API.
+ *
+ * @author Stewart Cossey - Initial contribution
+ */
+@NonNullByDefault
+public class HCCRubbishCollectionHandler extends BaseThingHandler {
+ private static final int DELAY_NETWORKERROR = 3; // On network error tries again in 3 minutes.
+ private static final int DELAY_UPDATE = 480; // Polls API every 8 hours.
+
+ private final Logger logger = LoggerFactory.getLogger(HCCRubbishCollectionHandler.class);
+
+ private final HttpClient httpClient;
+ private @Nullable API api;
+
+ private @Nullable ScheduledFuture> refreshScheduler; // The API refresh scheduler
+ private @Nullable ScheduledFuture> collectionScheduler; // The Collection event trigger scheduler
+
+ /** Object disposing flag */
+ private boolean isDisposing = false;
+
+ /**
+ * Create Handler.
+ *
+ * @param thing The thing type passed from the Handler Factory.
+ * @param httpClient The common http client provided from openHAB.
+ */
+ public HCCRubbishCollectionHandler(Thing thing, HttpClient httpClient) {
+ super(thing);
+
+ this.httpClient = httpClient;
+ }
+
+ /**
+ * Handles a command coming from openHAB.
+ * Only RefreshType is supported as all channels are read only.
+ *
+ * @param channelUID The channel UID.
+ * @param command The command.
+ */
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (command instanceof RefreshType) {
+ updateNow();
+ }
+ }
+
+ /**
+ * Refreshes the data immediately.
+ */
+ private void updateNow() {
+ if (isDisposing) {
+ return;
+ }
+
+ logger.debug("Updating data immediately");
+ stopUpdate(false);
+ startUpdate(0);
+ }
+
+ @Override
+ public void initialize() {
+ final HCCRubbishCollectionConfiguration config = getConfigAs(HCCRubbishCollectionConfiguration.class);
+
+ updateStatus(ThingStatus.UNKNOWN);
+
+ api = new API(httpClient, config.address);
+ startUpdate(0);
+ }
+
+ /**
+ * Gets the Rubbish collection data from the {@link API} and updates the
+ * channels with the data.
+ */
+ private void updateData() {
+ logger.debug("Fetching new data");
+ final API localApi = api;
+ if (localApi != null) {
+ if (isDisposing) {
+ return;
+ }
+ if (localApi.update()) {
+ if (isDisposing) {
+ return;
+ }
+ updateStatus(ThingStatus.ONLINE); // Updates Thing to online since API update was successful.
+
+ Integer localDay = localApi.getDay();
+ if (localDay != null) {
+ updateState(CHANNEL_DAY, new DecimalType(localDay));
+ }
+
+ ZonedDateTime localGeneralDate = localApi.getGeneralDate();
+ if (localGeneralDate != null) {
+ updateState(CHANNEL_BIN_GENERAL, new DateTimeType(localGeneralDate));
+ }
+
+ ZonedDateTime localRecyclingDate = localApi.getRecyclingDate();
+ if (localRecyclingDate != null) {
+ updateState(CHANNEL_BIN_RECYCLING, new DateTimeType(localRecyclingDate));
+ }
+
+ if (localGeneralDate != null && localRecyclingDate != null) {
+ setupCollectionEvent(localGeneralDate, localRecyclingDate);
+ } else {
+ logger.debug("Cannot setup Collection Event, one or both collection dates are null.");
+ }
+ } else {
+ if (localApi.getErrorDetail() != ThingStatusDetail.COMMUNICATION_ERROR) {
+ updateStatus(ThingStatus.OFFLINE, localApi.getErrorDetail(), localApi.getErrorDetailMessage());
+ stopUpdate(false);
+ } else {
+ stopUpdate(true);
+ }
+ }
+ } else {
+ logger.error("API object is null, cannot update");
+ }
+ }
+
+ /**
+ * Calculates some values for the Collection Event before setting up the
+ * Collection trigger {@link #scheduleCollectionEvent}.
+ *
+ * @param generalDate The General Rubbish Collection Date and Time.
+ * @param recyclingDate The Recycling Collection Date and Time.
+ */
+ private void setupCollectionEvent(ZonedDateTime generalDate, ZonedDateTime recyclingDate) {
+ logger.trace("Setup Collection Trigger");
+
+ String event;
+ ZonedDateTime dateTime;
+ if (generalDate.compareTo(recyclingDate) < 0) {
+ logger.trace("Using General Date {} for Event", generalDate);
+ dateTime = generalDate;
+ event = EVENT_GENERAL;
+ } else {
+ logger.trace("Using Recycling Date {} for Event", recyclingDate);
+ dateTime = recyclingDate;
+ event = EVENT_RECYCLING;
+ }
+
+ logger.trace("Loading channel config");
+ Channel collectionTriggerChannel = getThing().getChannel(TRIGGER_COLLECTION);
+ HCCRubbishCollectionEventConfiguration collectionEventConfig = (collectionTriggerChannel == null) ? null
+ : collectionTriggerChannel.getConfiguration().as(HCCRubbishCollectionEventConfiguration.class);
+
+ long offset = 0;
+ if (collectionEventConfig != null) {
+ offset = (long) collectionEventConfig.offset;
+ } else {
+ logger.debug("Could not get event config, default offset of {} set", offset);
+ }
+
+ ZonedDateTime offsettedDateTime = dateTime.plusMinutes(offset);
+ logger.trace("Event offset by {} minutes, new datetime {}", offset, offsettedDateTime);
+ scheduleCollectionEvent(offsettedDateTime, event);
+ }
+
+ /**
+ * Sets up the collection event trigger.
+ *
+ * @param dateTime The Date and time to trigger the Collection Event.
+ * @param event The name of the Event to be triggered.
+ */
+ private void scheduleCollectionEvent(ZonedDateTime dateTime, String event) {
+ stopScheduleCollectionEvent(); // Stop the currently scheduled event
+
+ if (isDisposing) {
+ return;
+ }
+
+ logger.trace("Setup Collection Trigger Scheduler");
+
+ logger.trace("Local Time {}", ZonedDateTime.now());
+ long delay = dateTime.toEpochSecond() - ZonedDateTime.now().toEpochSecond();
+
+ logger.debug("Start collection scheduler, delay {} seconds ({} minutes)", delay, delay / 60);
+ if (delay > 0) {
+ collectionScheduler = scheduler.schedule(() -> {
+ if (isDisposing) {
+ return;
+ }
+ triggerChannel(TRIGGER_COLLECTION, event);
+ }, delay, TimeUnit.SECONDS);
+ } else {
+ logger.debug("Collection trigger delay already in past, ignoring");
+ }
+ }
+
+ /**
+ * Starts the data update scheduler.
+ *
+ * @param delay The start delay in minutes. 0 executes an immediate update.
+ */
+ private void startUpdate(int delay) {
+ if (isDisposing) {
+ return;
+ }
+ logger.debug("Start refresh scheduler, delay {}", delay);
+
+ refreshScheduler = scheduler.scheduleWithFixedDelay(this::updateData, delay, DELAY_UPDATE, TimeUnit.MINUTES);
+ }
+
+ /**
+ * Stops the scheduler for the collection event trigger.
+ */
+ private void stopScheduleCollectionEvent() {
+ ScheduledFuture> localCollectionScheduler = collectionScheduler;
+ logger.debug("Stopping Collection Trigger Scheduler");
+ if (localCollectionScheduler != null) {
+ localCollectionScheduler.cancel(true);
+ collectionScheduler = null;
+ }
+ }
+
+ /**
+ * Stop the data update scheduler (stops updating data). If stopping due to a
+ * network error, then resets the update scheduler {@link #startUpdate(int)}
+ * with an initial delay to wait a short period then try again.
+ *
+ * @param networkError Set to true if a network error. False if terminating.
+ */
+ private void stopUpdate(boolean networkError) {
+ final ScheduledFuture> localRefreshScheduler = refreshScheduler;
+ logger.debug("Stopping updater scheduler, networkError = {}", networkError);
+ if (localRefreshScheduler != null) {
+ localRefreshScheduler.cancel(true);
+ refreshScheduler = null;
+ }
+ if (networkError) {
+ logger.debug("Waiting {} minutes to try again", DELAY_NETWORKERROR);
+ startUpdate(DELAY_NETWORKERROR);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ isDisposing = true; // Set true to exit any running functions
+ stopUpdate(false);
+ stopScheduleCollectionEvent();
+
+ super.dispose();
+ }
+}
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandlerFactory.java b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandlerFactory.java
new file mode 100644
index 00000000000..af35883c03a
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandlerFactory.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.hccrubbishcollection.internal;
+
+import static org.openhab.binding.hccrubbishcollection.internal.HCCRubbishCollectionBindingConstants.*;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link HCCRubbishCollectionHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Stewart Cossey - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.hccrubbishcollection", service = ThingHandlerFactory.class)
+public class HCCRubbishCollectionHandlerFactory extends BaseThingHandlerFactory {
+
+ private final HttpClient httpClient;
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_COLLECTION);
+
+ @Activate
+ public HCCRubbishCollectionHandlerFactory(final @Reference HttpClientFactory httpClientFactory) {
+ this.httpClient = httpClientFactory.getCommonHttpClient();
+ }
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+ if (THING_TYPE_COLLECTION.equals(thingTypeUID)) {
+ return new HCCRubbishCollectionHandler(thing, httpClient);
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644
index 00000000000..c10961e0f11
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/binding/binding.xml
@@ -0,0 +1,9 @@
+
+
+
+ HCC Rubbish Collection Binding
+ Get the rubbish collection dates for Hamilton City Council (New Zealand).
+
+
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 00000000000..fed9d344e42
--- /dev/null
+++ b/bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ Rubbish collection days for Hamilton City Council (NZ).
+
+
+
+
+
+ The next collection date of the recycling (yellow bin and green bin).
+
+
+
+ The next collection date of the general rubbish (red bin).
+
+
+
+
+
+
+
+ The street address to get rubbish collection dates for.
+
+
+
+
+
+
+ Number
+
+ The rubbish collection Day of the Week
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DateTime
+
+
+
+
+ trigger
+
+ Event for the day when collection occurs.
+
+
+
+
+
+
+
+
+
+ Moves the event forward or backward (in minutes).
+ 0
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 4320e099313..091bbaf5fac 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -133,6 +133,7 @@
org.openhab.binding.haassohnpelletstove
org.openhab.binding.harmonyhub
org.openhab.binding.haywardomnilogic
+ org.openhab.binding.hccrubbishcollection
org.openhab.binding.hdanywhere
org.openhab.binding.hdpowerview
org.openhab.binding.helios