]> git.basschouten.com Git - openhab-addons.git/commitdiff
[hccrubbishcollection] HCC Rubbish Collection Binding - Initial Contribution (#9975)
authorStewart Cossey <killerofshadows@gmail.com>
Fri, 30 Apr 2021 14:23:07 +0000 (02:23 +1200)
committerGitHub <noreply@github.com>
Fri, 30 Apr 2021 14:23:07 +0000 (16:23 +0200)
Signed-off-by: Stewart Cossey <stewart.cossey@gmail.com>
15 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.hccrubbishcollection/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/README.md [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/feature/feature.xml [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/API.java [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionEventConfiguration.java [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/java/org/openhab/binding/hccrubbishcollection/internal/HCCRubbishCollectionHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/binding/binding.xml [new file with mode: 0644]
bundles/org.openhab.binding.hccrubbishcollection/src/main/resources/OH-INF/thing/thing-types.xml [new file with mode: 0644]
bundles/pom.xml

index d7365952a479500e75be9db5741a325818a4a922..9f5136c9d11c7b4322901908f845a4cfc4fee22c 100644 (file)
 /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
index a26ae4965a1977038e2fcdf634fed5e642658f62..c6d486fce12e548b539ff979f07d8b3668cf7468 100644 (file)
       <artifactId>org.openhab.binding.haywardomnilogic</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.binding.hccrubbishcollection</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.hdanywhere</artifactId>
diff --git a/bundles/org.openhab.binding.hccrubbishcollection/NOTICE b/bundles/org.openhab.binding.hccrubbishcollection/NOTICE
new file mode 100644 (file)
index 0000000..38d625e
--- /dev/null
@@ -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 (file)
index 0000000..8a6b4c2
--- /dev/null
@@ -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 (file)
index 0000000..8414b72
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.openhab.addons.bundles</groupId>
+    <artifactId>org.openhab.addons.reactor.bundles</artifactId>
+    <version>3.1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.openhab.binding.hccrubbishcollection</artifactId>
+
+  <name>openHAB Add-ons :: Bundles :: HCC Rubbish Collection Binding</name>
+
+</project>
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 (file)
index 0000000..b21a42a
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.hccrubbishcollection-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
+       <repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
+
+       <feature name="openhab-binding-hccrubbishcollection" description="HCC Rubbish Collection Binding" version="${project.version}">
+               <feature>openhab-runtime-base</feature>
+               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.hccrubbishcollection/${project.version}</bundle>
+       </feature>
+</features>
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 (file)
index 0000000..c7ca5d8
--- /dev/null
@@ -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 (file)
index 0000000..aac108c
--- /dev/null
@@ -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 (file)
index 0000000..75af8d0
--- /dev/null
@@ -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 (file)
index 0000000..34e0c3b
--- /dev/null
@@ -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 (file)
index 0000000..3a640d4
--- /dev/null
@@ -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 (file)
index 0000000..af35883
--- /dev/null
@@ -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<ThingTypeUID> 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 (file)
index 0000000..c10961e
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<binding:binding id="hccrubbishcollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
+
+       <name>HCC Rubbish Collection Binding</name>
+       <description>Get the rubbish collection dates for Hamilton City Council (New Zealand).</description>
+
+</binding:binding>
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 (file)
index 0000000..fed9d34
--- /dev/null
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="hccrubbishcollection"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <thing-type id="collection">
+               <label>HCC NZ Rubbish Collection</label>
+               <description>Rubbish collection days for Hamilton City Council (NZ).</description>
+
+               <channels>
+                       <channel id="day" typeId="day"/>
+                       <channel id="recycling" typeId="bin">
+                               <label>Recycling Bin Collection Date</label>
+                               <description>The next collection date of the recycling (yellow bin and green bin).</description>
+                       </channel>
+                       <channel id="general" typeId="bin">
+                               <label>General Bin Collection Date</label>
+                               <description>The next collection date of the general rubbish (red bin).</description>
+                       </channel>
+                       <channel id="collection-event" typeId="collection-event"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="address" type="text" required="true">
+                               <label>Address</label>
+                               <description>The street address to get rubbish collection dates for.</description>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+
+       <channel-type id="day">
+               <item-type>Number</item-type>
+               <label>Collection Day</label>
+               <description>The rubbish collection Day of the Week</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="1">Monday</option>
+                               <option value="2">Tuesday</option>
+                               <option value="3">Wednesday</option>
+                               <option value="4">Thursday</option>
+                               <option value="5">Friday</option>
+                               <option value="6">Saturday</option>
+                               <option value="7">Sunday</option>
+                       </options>
+               </state>
+       </channel-type>
+       <channel-type id="bin">
+               <item-type>DateTime</item-type>
+               <label>Collection Date</label>
+               <state pattern="%1$tY-%1$tm-%1$td" readOnly="true"></state>
+       </channel-type>
+       <channel-type id="collection-event">
+               <kind>trigger</kind>
+               <label>Collection Event</label>
+               <description>Event for the day when collection occurs.</description>
+               <event>
+                       <options>
+                               <option value="RECYCLING">RECYCLING</option>
+                               <option value="GENERAL">GENERAL</option>
+                       </options>
+               </event>
+               <config-description>
+                       <parameter name="offset" type="integer" min="-2880" max="2880" unit="min">
+                               <label>Offset</label>
+                               <description>Moves the event forward or backward (in minutes).</description>
+                               <default>0</default>
+                       </parameter>
+               </config-description>
+       </channel-type>
+</thing:thing-descriptions>
index 4320e099313e00e74e043bb5849b30f63322315f..091bbaf5facbddb9dcb9c1cd197aa2514e750221 100644 (file)
     <module>org.openhab.binding.haassohnpelletstove</module>
     <module>org.openhab.binding.harmonyhub</module>
     <module>org.openhab.binding.haywardomnilogic</module>
+    <module>org.openhab.binding.hccrubbishcollection</module>
     <module>org.openhab.binding.hdanywhere</module>
     <module>org.openhab.binding.hdpowerview</module>
     <module>org.openhab.binding.helios</module>