diff --git a/CODEOWNERS b/CODEOWNERS
index d161df2f0f618..d3054bde7c512 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -63,6 +63,7 @@
/bundles/org.openhab.binding.hdpowerview/ @beowulfe
/bundles/org.openhab.binding.helios/ @kgoderis
/bundles/org.openhab.binding.heos/ @Wire82
+/bundles/org.openhab.binding.hive/ @rbrownwsws
/bundles/org.openhab.binding.homematic/ @FStolte @gerrieg @mdicke2s
/bundles/org.openhab.binding.hpprinter/ @cossey
/bundles/org.openhab.binding.hue/ @cweitkamp
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index aa79d5c99b3a2..7e79c8416dda9 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -308,6 +308,11 @@
org.openhab.addons.bundlesorg.openhab.binding.heos${project.version}
+
+
+ org.openhab.addons.bundles
+ org.openhab.binding.hive
+ ${project.version}org.openhab.addons.bundles
diff --git a/bundles/org.openhab.binding.hive/.classpath b/bundles/org.openhab.binding.hive/.classpath
new file mode 100644
index 0000000000000..a5d95095ccaaf
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/.classpath
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.hive/.project b/bundles/org.openhab.binding.hive/.project
new file mode 100644
index 0000000000000..92b99de245ff5
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/.project
@@ -0,0 +1,23 @@
+
+
+ org.openhab.binding.hive
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/bundles/org.openhab.binding.hive/NOTICE b/bundles/org.openhab.binding.hive/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/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.hive/README.md b/bundles/org.openhab.binding.hive/README.md
new file mode 100644
index 0000000000000..c2309a289e97d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/README.md
@@ -0,0 +1,86 @@
+# Hive Binding
+
+
+
+
+
+This binding integrates the [Hive smart home system](https://www.hivehome.com) using the Hive REST API.
+
+_N.B. As this binding uses the Hive REST API it can only integrate Hive branded devices connected to a Hive Hub.
+Setups that do not include a Hive Hub or connect to a different brand of Zigbee hub/bridge will not work with this binding._
+
+| Hub Type | Image |
+|--------------|-------|
+| Hive Hub | |
+| Hive Hub 360 | |
+
+## Supported Things
+
+_Please describe the different supported things / devices within this section._
+_Which different types are supported, which models were tested etc.?_
+_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._
+
+
+
+| Thing | Image | Supported | Tested |
+|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|--------|
+| Hive Thermostat V1 | | ✓ | ✗ |
+| Hive Thermostat V2 | | ✓ | ✓ |
+| Hive Radiator Valve | | ✓ | ✓ |
+| Hive Plug | | ✗ | ✗ |
+| Hive Motion Sensor | | ✗ | ✗ |
+| Hive Window or Door Sensor | | ✗ | ✗ |
+| Hive Dimmable Light Bulb\* | | ✗ | ✗ |
+| Hive Cool to Warm White Light Bulb\* | | ✗ | ✗ |
+| Hive Colour Changing Light Bulb\* | | ✗ | ✗ |
+| Hive View | | ✗ | ✗ |
+| Hive View Outdoor | | ✗ | ✗ |
+
+\*Hive Light Bulbs come in B22, E27, E14 and GU10 variants. Only one of these variants is pictured.
+
+## Discovery
+
+_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._
+
+## Binding Configuration
+
+_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it. In this section, you should link to this file and provide some information about the options. The file could e.g. look like:_
+
+```
+# Configuration for the Philips Hue Binding
+#
+# Default secret key for the pairing of the Philips Hue Bridge.
+# It has to be between 10-40 (alphanumeric) characters
+# This may be changed by the user for security reasons.
+secret=openHABSecret
+```
+
+_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/ESH-INF/binding``` of your binding._
+
+_If your binding does not offer any generic configurations, you can remove this section completely._
+
+## Thing Configuration
+
+_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_
+
+_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._
+
+## Channels
+
+_Here you should provide information about available channel types, what their meaning is and how they can be used._
+
+_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._
+
+| channel | type | description |
+|----------|--------|------------------------------|
+| control | Switch | This is the control channel |
+
+## Full Example
+
+_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._
+
+## Any custom content here!
+
+_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_
+
+[hive-hub]: ""
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.hive/pom.xml b/bundles/org.openhab.binding.hive/pom.xml
new file mode 100644
index 0000000000000..2dc277b4c0390
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 2.5.3-SNAPSHOT
+
+
+ org.openhab.binding.hive
+
+ openHAB Add-ons :: Bundles :: Hive Binding
+
+
+ 0.8.5
+
+
+
+
+ org.assertj
+ assertj-core
+ 3.15.0
+ test
+
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+ 3.0.0-M4
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.hive/src/main/feature/feature.xml b/bundles/org.openhab.binding.hive/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..3218b5d1e7275
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/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.hive/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveAccountConfig.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveAccountConfig.java
new file mode 100644
index 0000000000000..fcb3743983eed
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveAccountConfig.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link HiveAccountConfig} class contains fields mapping thing configuration parameters.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveAccountConfig {
+ public @Nullable String username;
+ public @Nullable String password;
+ public @Nullable Integer pollingInterval;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveBindingConstants.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveBindingConstants.java
new file mode 100644
index 0000000000000..1ccc070e77c09
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveBindingConstants.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * The {@link HiveBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveBindingConstants {
+ public static final String BINDING_ID = "hive";
+
+ /* ######## Type UIDs ######## */
+ public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
+ public static final ThingTypeUID THING_TYPE_BOILER_MODULE = new ThingTypeUID(BINDING_ID, "boiler_module");
+ public static final ThingTypeUID THING_TYPE_HEATING = new ThingTypeUID(BINDING_ID, "heating");
+ public static final ThingTypeUID THING_TYPE_HOT_WATER = new ThingTypeUID(BINDING_ID, "hot_water");
+ public static final ThingTypeUID THING_TYPE_HUB = new ThingTypeUID(BINDING_ID, "hub");
+ public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat");
+ public static final ThingTypeUID THING_TYPE_TRV = new ThingTypeUID(BINDING_ID, "trv");
+ public static final ThingTypeUID THING_TYPE_TRV_GROUP = new ThingTypeUID(BINDING_ID, "trv_group");
+
+ /**
+ * The set of {@link ThingTypeUID}s supported by this binding.
+ */
+ // @formatter:off
+ public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream.of(
+ THING_TYPE_ACCOUNT,
+ THING_TYPE_BOILER_MODULE,
+ THING_TYPE_HEATING,
+ THING_TYPE_HOT_WATER,
+ THING_TYPE_HUB,
+ THING_TYPE_THERMOSTAT,
+ THING_TYPE_TRV,
+ THING_TYPE_TRV_GROUP
+ ).collect(Collectors.toSet()));
+ // @formatter:on
+
+ /**
+ * The set of {@link ThingTypeUID}s that can be discovered
+ * (everything but accounts).
+ */
+ public static final Set DISCOVERABLE_THING_TYPES_UIDS = Collections.unmodifiableSet(
+ SUPPORTED_THING_TYPES_UIDS.stream()
+ .filter(it -> it != THING_TYPE_ACCOUNT)
+ .collect(Collectors.toSet())
+ );
+
+ /* ######## Channel ids ######## */
+ public static final String CHANNEL_BATTERY_LEVEL = "battery-level";
+ public static final String CHANNEL_BATTERY_LOW = "battery-low";
+ public static final String CHANNEL_MODE_ON_OFF = "mode-on_off";
+ public static final String CHANNEL_MODE_OPERATING = "mode-operating";
+ public static final String CHANNEL_MODE_OPERATING_OVERRIDE = "mode-operating-override";
+ public static final String CHANNEL_STATE_OPERATING = "state-operating";
+ public static final String CHANNEL_TEMPERATURE_CURRENT = "temperature-current";
+ public static final String CHANNEL_TEMPERATURE_TARGET = "temperature-target";
+ public static final String CHANNEL_TEMPERATURE_TARGET_BOOST = "temperature-target-boost";
+ public static final String CHANNEL_IS_ON = "is_on";
+ public static final String CHANNEL_TRANSIENT_DURATION = "transient-duration";
+ public static final String CHANNEL_TRANSIENT_REMAINING = "transient-remaining";
+ public static final String CHANNEL_TRANSIENT_ENABLED = "transient-enabled";
+ public static final String CHANNEL_TRANSIENT_START_TIME = "transient-start_time";
+ public static final String CHANNEL_TRANSIENT_END_TIME = "transient-end_time";
+ public static final String CHANNEL_RADIO_LQI_AVERAGE = "radio-lqi-average";
+ public static final String CHANNEL_RADIO_LQI_LAST_KNOWN = "radio-lqi-last_known";
+ public static final String CHANNEL_RADIO_RSSI_AVERAGE = "radio-rssi-average";
+ public static final String CHANNEL_RADIO_RSSI_LAST_KNOWN = "radio-rssi-last_known";
+
+
+ /* ######## Config params ######## */
+ public static final String CONFIG_NODE_ID = "nodeId";
+
+ public static final Duration SETTLE_TIME = Duration.ofSeconds(5);
+
+ private HiveBindingConstants() {
+ throw new AssertionError();
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveHandlerFactory.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveHandlerFactory.java
new file mode 100644
index 0000000000000..10e0a0601abf7
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/HiveHandlerFactory.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.config.discovery.DiscoveryService;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
+import org.openhab.binding.hive.internal.handler.*;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * The {@link HiveHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.hive", service = ThingHandlerFactory.class)
+public final class HiveHandlerFactory extends BaseThingHandlerFactory {
+ private final Logger logger = LoggerFactory.getLogger(HiveHandlerFactory.class);
+
+ private final Map> discoveryServices = new HashMap<>();
+
+ public HiveHandlerFactory() {
+ super();
+ }
+
+ @Override
+ protected void activate(final ComponentContext componentContext) {
+ super.activate(componentContext);
+
+ // Help keep track of when openHAB reloads binding while debugging.
+ logger.debug("Handler has been activated");
+ }
+
+ @Override
+ protected void deactivate(final ComponentContext componentContext) {
+ super.deactivate(componentContext);
+
+ // Help keep track of when openHAB reloads binding while debugging.
+ logger.debug("Handler has been deactivated");
+ }
+
+ @Override
+ public boolean supportsThingType(final ThingTypeUID thingTypeUID) {
+ return HiveBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(final Thing thing) {
+ final ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+ if (HiveBindingConstants.THING_TYPE_ACCOUNT.equals(thingTypeUID)) {
+ // Create the handler
+ final HiveAccountHandler handler = new HiveAccountHandler((Bridge) thing);
+
+ // Register the discovery service and keep track of it so we can
+ // unregister it later.
+ final ServiceRegistration> serviceReg = bundleContext.registerService(
+ DiscoveryService.class.getName(),
+ handler.getDiscoveryService(),
+ new Hashtable<>()
+ );
+ final ThingUID uid = thing.getUID();
+ this.discoveryServices.put(uid, serviceReg);
+
+ return handler;
+ } else if (HiveBindingConstants.THING_TYPE_BOILER_MODULE.equals(thingTypeUID)) {
+ return new HiveBoilerModuleHandler(thing);
+ } else if (HiveBindingConstants.THING_TYPE_HEATING.equals(thingTypeUID)) {
+ return new HiveHeatingHandler(thing);
+ } else if (HiveBindingConstants.THING_TYPE_HOT_WATER.equals(thingTypeUID)) {
+ return new HiveHotWaterHandler(thing);
+ } else if (HiveBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) {
+ return new HiveHubHandler(thing);
+ } else if (HiveBindingConstants.THING_TYPE_THERMOSTAT.equals(thingTypeUID)) {
+ return new HiveThermostatHandler(thing);
+ } else if (HiveBindingConstants.THING_TYPE_TRV.equals(thingTypeUID)) {
+ return new HiveTrvHandler(thing);
+ } else if (HiveBindingConstants.THING_TYPE_TRV_GROUP.equals(thingTypeUID)) {
+ return new HiveTrvGroupHandler(thing);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void removeHandler(final ThingHandler thingHandler) {
+ logger.debug("Removing handler");
+ if (thingHandler instanceof HiveAccountHandler) {
+ logger.debug("Handler is bridge");
+ // Clean up associated discovery service.
+ final @Nullable ServiceRegistration> serviceReg = this.discoveryServices.remove(thingHandler.getThing().getUID());
+
+ if (serviceReg != null) {
+ logger.debug("Unregistered service");
+ serviceReg.unregister();
+ }
+ }
+
+ super.removeHandler(thingHandler);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/ActionType.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/ActionType.java
new file mode 100644
index 0000000000000..a50151f08f2aa
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/ActionType.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class ActionType extends URITypeBase {
+ public static final ActionType GENERIC = new ActionType("http://alertme.com/schema/json/configuration/configuration.device.action.generic.v1.json#");
+
+ public ActionType(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/AttributeName.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/AttributeName.java
new file mode 100644
index 0000000000000..f90f86b0f8c90
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/AttributeName.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class AttributeName extends StringTypeBase {
+ public static final AttributeName ATTRIBUTE_NAME_TARGET_HEAT_TEMPERATURE = new AttributeName("targetHeatTemperature");
+
+ public AttributeName(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/BatteryLevel.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/BatteryLevel.java
new file mode 100644
index 0000000000000..48f369f67d1e1
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/BatteryLevel.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class BatteryLevel {
+ private int value;
+
+ public BatteryLevel(int value) {
+ if (value < 0 || value > 100) {
+ throw new IllegalArgumentException("Battery level is must be between 0-100 (inclusive)");
+ }
+
+ this.value = value;
+ }
+
+ public int intValue() {
+ return value;
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BatteryLevel that = (BatteryLevel) o;
+ return value == that.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
+
+ @Override
+ public String toString() {
+ return this.value + "%";
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/DefaultHiveClient.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/DefaultHiveClient.java
new file mode 100644
index 0000000000000..d2bea68cec677
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/DefaultHiveClient.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.exception.HiveApiAuthenticationException;
+import org.openhab.binding.hive.internal.client.exception.HiveApiNotAuthorisedException;
+import org.openhab.binding.hive.internal.client.exception.HiveApiUnknownException;
+import org.openhab.binding.hive.internal.client.repository.NodeRepository;
+import org.openhab.binding.hive.internal.client.repository.SessionRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Supplier;
+
+/**
+ * The default implementation of {@link HiveClient}
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+final class DefaultHiveClient implements HiveClient {
+ private final Logger logger = LoggerFactory.getLogger(DefaultHiveClient.class);
+
+ private final SessionAuthenticationManager authenticationManager;
+
+ private final Username username;
+ private final Password password;
+
+ private final SessionRepository sessionRepository;
+ private final NodeRepository nodeRepository;
+
+ private @Nullable Session session;
+
+ /**
+ * Indicated whether the client is currently authenticated with the Hive
+ * API.
+ */
+ private boolean authenticated = false;
+
+ /**
+ *
+ *
+ * @throws HiveApiAuthenticationException
+ * If we failed to authenticate with the provided username and password.
+ *
+ * @throws HiveApiUnknownException
+ * If something unexpected happens while communicating with the Hive
+ * API.
+ */
+ public DefaultHiveClient(
+ final SessionAuthenticationManager authenticationManager,
+ final Username username,
+ final Password password,
+ final SessionRepository sessionRepository,
+ final NodeRepository nodeRepository
+ ) {
+ Objects.requireNonNull(authenticationManager);
+ Objects.requireNonNull(sessionRepository);
+ Objects.requireNonNull(nodeRepository);
+
+ this.authenticationManager = authenticationManager;
+
+ this.username = username;
+ this.password = password;
+
+ this.sessionRepository = sessionRepository;
+ this.nodeRepository = nodeRepository;
+
+ authenticate();
+ }
+
+ @Override
+ public void close() throws Exception {
+ // TODO: Clean up
+ }
+
+ /**
+ * Try to authenticate with the Hive API.
+ *
+ * @throws HiveApiAuthenticationException
+ * If we failed to authenticate with the stored username and password.
+ *
+ * @throws HiveApiUnknownException
+ * If something unexpected happens while communicating with the Hive
+ * API.
+ */
+ private void authenticate() {
+ this.authenticationManager.clearSession();
+
+ final Session session = this.sessionRepository.createSession(
+ this.username,
+ this.password
+ );
+
+ this.session = session;
+ this.authenticationManager.setSession(session);
+
+ this.authenticated = true;
+ }
+
+ private T makeAuthenticatedApiCall(final Supplier apiCall) {
+ // If we get a valid result return it.
+ // If we are not authorised check if session has expired, reauthenticate and try again.
+ // Otherwise let other exceptions bubble up.
+ boolean reauthenticated = false;
+ while (true) {
+ try {
+ return apiCall.get();
+ } catch (final HiveApiNotAuthorisedException ex) {
+ if (reauthenticated || this.sessionRepository.isValidSession(this.authenticationManager.getSession())) {
+ // We are either not authorised for this resource or
+ // something has gone wrong with the client logic.
+ // Pass on the exception.
+ throw ex;
+ } else {
+ // Session seems to no longer be valid.
+ // Reauthenticate and try again.
+ authenticate();
+ reauthenticated = true;
+ }
+ }
+ }
+ }
+
+ private void makeAuthenticatedApiCall(final Runnable apiCall) {
+ makeAuthenticatedApiCall(() -> {
+ apiCall.run();
+ return null;
+ });
+ }
+
+ @Override
+ public UserId getUserId() {
+ return this.session.getUserId();
+ }
+
+ @Override
+ public Set getAllNodes() {
+ return makeAuthenticatedApiCall(this.nodeRepository::getAllNodes);
+ }
+
+ @Override
+ public @Nullable Node getNode(final NodeId nodeId) {
+ Objects.requireNonNull(nodeId);
+
+ return makeAuthenticatedApiCall(() -> this.nodeRepository.getNode(nodeId));
+ }
+
+ @Override
+ public @Nullable Node updateNode(final Node node) {
+ Objects.requireNonNull(node);
+
+ return makeAuthenticatedApiCall(() -> this.nodeRepository.updateNode(node));
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/DefaultNode.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/DefaultNode.java
new file mode 100644
index 0000000000000..cc7a07f394fa9
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/DefaultNode.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.feature.Feature;
+
+import java.util.*;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class DefaultNode implements Node {
+ private final NodeId id;
+ private final NodeName name;
+ private final NodeType nodeType;
+ private final ProductType productType;
+ private final Protocol protocol;
+ private final NodeId parentNodeId;
+ private final Map, Feature> features;
+
+ public DefaultNode(
+ final NodeId id,
+ final NodeName name,
+ final NodeType nodeType,
+ final ProductType productType,
+ final Protocol protocol,
+ final NodeId parentNodeId,
+ final Map, Feature> features
+ ) {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(nodeType);
+ Objects.requireNonNull(productType);
+ Objects.requireNonNull(protocol);
+ Objects.requireNonNull(parentNodeId);
+ Objects.requireNonNull(features);
+
+ this.id = id;
+ this.name = name;
+ this.nodeType = nodeType;
+ this.productType = productType;
+ this.protocol = protocol;
+ this.parentNodeId = parentNodeId;
+ this.features = features;
+ }
+
+ @Override
+ public NodeId getId() {
+ return this.id;
+ }
+
+ @Override
+ public NodeName getName() {
+ return this.name;
+ }
+
+ @Override
+ public NodeType getNodeType() {
+ return this.nodeType;
+ }
+
+ @Override
+ public ProductType getProductType() {
+ return this.productType;
+ }
+
+ @Override
+ public Protocol getProtocol() {
+ return this.protocol;
+ }
+
+ @Override
+ public NodeId getParentNodeId() {
+ return this.parentNodeId;
+ }
+
+ @Override
+ public Set getFeatures() {
+ return Collections.unmodifiableSet(new HashSet<>(this.features.values()));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @Nullable T getFeature(final Class classOfFeature) {
+ // N.B. "Unchecked cast" warning suppressed because we should be
+ // ensuring that the class of the value matches the key
+ // when they are put in the map.
+ return (T) this.features.get(classOfFeature);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureAttribute.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureAttribute.java
new file mode 100644
index 0000000000000..8864acedaae44
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureAttribute.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public class FeatureAttribute {
+ private final T reportedValue;
+ private final Instant reportReceivedTime;
+ private final Instant reportChangedTime;
+
+ private final T displayValue;
+
+ FeatureAttribute(
+ final T reportedValue,
+ final Instant reportChangedTime,
+ final Instant reportReceivedTime,
+ final T displayValue
+ ) {
+ Objects.requireNonNull(reportedValue);
+ Objects.requireNonNull(reportChangedTime);
+ Objects.requireNonNull(reportReceivedTime);
+ Objects.requireNonNull(displayValue);
+
+ this.reportedValue = reportedValue;
+ this.reportChangedTime = reportChangedTime;
+ this.reportReceivedTime = reportReceivedTime;
+ this.displayValue = displayValue;
+ }
+
+ public final T getReportedValue() {
+ return this.reportedValue;
+ }
+
+ public final Instant getReportReceivedTime() {
+ return this.reportReceivedTime;
+ }
+
+ public final Instant getReportChangedTime() {
+ return this.reportChangedTime;
+ }
+
+ public final T getDisplayValue() {
+ return this.displayValue;
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureAttributeFactory.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureAttributeFactory.java
new file mode 100644
index 0000000000000..26ee6abdeee11
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureAttributeFactory.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.dto.FeatureAttributeDto;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class FeatureAttributeFactory {
+ private FeatureAttributeFactory() {
+ throw new AssertionError();
+ }
+
+ @FunctionalInterface
+ public interface Adapter {
+ T adapt(F from);
+ }
+
+ public static FeatureAttribute getReadOnlyFromDto(final FeatureAttributeDto dto) {
+ Objects.requireNonNull(dto);
+
+ return new FeatureAttribute<>(
+ dto.reportedValue,
+ dto.reportChangedTime.asInstant(),
+ dto.reportReceivedTime.asInstant(),
+ dto.displayValue
+ );
+ }
+
+ public static FeatureAttribute getReadOnlyFromDtoWithAdapter(
+ final Adapter adapter,
+ final FeatureAttributeDto dto
+ ) {
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(dto);
+
+ return new FeatureAttribute<>(
+ adapter.adapt(dto.reportedValue),
+ dto.reportChangedTime.asInstant(),
+ dto.reportReceivedTime.asInstant(),
+ adapter.adapt(dto.displayValue)
+ );
+ }
+
+ public static SettableFeatureAttribute getSettableFromDto(final FeatureAttributeDto dto) {
+ Objects.requireNonNull(dto);
+
+ return new SettableFeatureAttribute<>(
+ dto.reportedValue,
+ dto.reportChangedTime.asInstant(),
+ dto.reportReceivedTime.asInstant(),
+ dto.displayValue
+ );
+ }
+
+ public static SettableFeatureAttribute getSettableFromDtoWithAdapter(
+ final Adapter adapter,
+ final FeatureAttributeDto dto
+ ) {
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(dto);
+
+ return new SettableFeatureAttribute<>(
+ adapter.adapt(dto.reportedValue),
+ dto.reportChangedTime.asInstant(),
+ dto.reportReceivedTime.asInstant(),
+ adapter.adapt(dto.displayValue)
+ );
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureType.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureType.java
new file mode 100644
index 0000000000000..9ccd185826665
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FeatureType.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class FeatureType extends URITypeBase {
+ public static final FeatureType BATTERY_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.battery_device.v1.json#");
+ public static final FeatureType CHILD_LOCK_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.child_lock.v1.json#");
+ public static final FeatureType DEVICE_MANAGEMENT_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.device_management.v1.json#");
+ public static final FeatureType DISPLAY_ORIENTATION_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.display_orientation.v1.json#");
+ public static final FeatureType ETHERNET_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.ethernet_device.v1.json#");
+ public static final FeatureType FROST_PROTECT_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.frost_protect.v1.json#");
+ public static final FeatureType GROUP_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.group.v1.json#");
+ public static final FeatureType HEATING_TEMPERATURE_CONTROL_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.heating_temperature_control_device.v1.json#");
+ public static final FeatureType HEATING_TEMPERATURE_CONTROL_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.heating_temperature_control.v1.json#");
+ public static final FeatureType HEATING_THERMOSTAT_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.heating_thermostat.v1.json#");
+ public static final FeatureType HIVE_HUB_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.hive_hub.v1.json#");
+ public static final FeatureType LIFECYCLE_STATE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.lifecycle_state.v1.json#");
+ public static final FeatureType LINKS_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.links.v1.json#");
+ public static final FeatureType MOUNTING_MODE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.mounting_mode.v1.json#");
+ public static final FeatureType ON_OFF_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.on_off_device.v1.json#");
+ public static final FeatureType PI_HEATING_DEMAND_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.pi_heating_demand.v1.json#");
+ public static final FeatureType PHYSICAL_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.physical_device.v1.json#");
+ public static final FeatureType RADIO_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.radio_device.v1.json#");
+ public static final FeatureType STANDBY_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.standby.v1.json#");
+ public static final FeatureType TEMPERATURE_SENSOR_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.temperature_sensor.v1.json#");
+ public static final FeatureType TRANSIENT_MODE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.transient_mode.v1.json#");
+ public static final FeatureType TRV_CALIBRATION_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.trv_calibration.v1.json#");
+ public static final FeatureType TRV_ERROR_DIAGNOSTICS_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.trv_error_diagnostics.v1.json#");
+ public static final FeatureType WATER_HEATER_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.water_heater.v1.json#");
+ public static final FeatureType ZIGBEE_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.zigbee_device.v1.json#");
+ public static final FeatureType ZIGBEE_ROUTING_DEVICE_V1 = new FeatureType("http://alertme.com/schema/json/feature/node.feature.zigbee_routing_device.v1.json#");
+
+ public FeatureType(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FromStringTypeBase.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FromStringTypeBase.java
new file mode 100644
index 0000000000000..8e61eaaf8162f
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/FromStringTypeBase.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.util.Objects;
+
+/**
+ * A base class for simple value types constructed from strings.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+abstract class FromStringTypeBase {
+ private final T value;
+
+ public FromStringTypeBase(final String stringValue) {
+ Objects.requireNonNull(stringValue);
+
+ this.value = transform(stringValue);
+
+ validate(this.value);
+ }
+
+ protected abstract T transform(final String stringValue);
+
+ protected void validate(final T value) {}
+
+ @Override
+ public String toString() {
+ return value.toString();
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || this.getClass() != o.getClass()) return false;
+ final FromStringTypeBase> other = (FromStringTypeBase>) o;
+ return value.equals(other.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/GsonJsonService.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/GsonJsonService.java
new file mode 100644
index 0000000000000..e2905e06a79a3
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/GsonJsonService.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.adapter.*;
+import org.openhab.binding.hive.internal.client.dto.HiveApiInstant;
+
+import java.time.ZonedDateTime;
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+final class GsonJsonService implements JsonService {
+ private final Gson gson;
+
+ public GsonJsonService() {
+ this.gson = (new GsonBuilder())
+ .registerTypeAdapter(HiveApiInstant.class, new HiveApiInstantGsonAdapter())
+ .registerTypeAdapter(UserId.class, new UserIdGsonAdapter())
+ .registerTypeAdapter(SessionId.class, new SessionIdGsonAdapter())
+ .registerTypeAdapter(Username.class, new UsernameGsonAdapter())
+ .registerTypeAdapter(Password.class, new PasswordGsonAdapter())
+ .registerTypeAdapter(NodeId.class, new NodeIdGsonAdapter())
+ .registerTypeAdapter(NodeName.class, new NodeNameGsonAdapter())
+ .registerTypeAdapter(NodeType.class, new NodeTypeGsonAdapter())
+ .registerTypeAdapter(FeatureType.class, new FeatureTypeGsonAdapter())
+ .registerTypeAdapter(ProductType.class, new ProductTypeGsonAdapter())
+ .registerTypeAdapter(ActionType.class, new ActionTypeGsonAdapter())
+ .registerTypeAdapter(AttributeName.class, new AttributeNameGsonAdapter())
+ .registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeGsonAdapter())
+ .create();
+ }
+
+ @Override
+ public String toJson(final Object object) {
+ Objects.requireNonNull(object);
+
+ return this.gson.toJson(object);
+ }
+
+ @Override
+ public T fromJson(final String json, final Class classOfT) {
+ Objects.requireNonNull(json);
+ Objects.requireNonNull(classOfT);
+
+ return this.gson.fromJson(json, classOfT);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiConstants.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiConstants.java
new file mode 100644
index 0000000000000..489f835021885
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiConstants.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.net.URI;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveApiConstants {
+ private HiveApiConstants() {
+ throw new AssertionError();
+ }
+
+ /**
+ * The location of the real Hive API.
+ */
+ public static final URI DEFAULT_BASE_PATH = URI.create("https://api.prod.bgchprod.info/omnia/");
+
+
+ public static final URI ENDPOINT_ACCESS_TOKENS = URI.create("accessTokens");
+
+ public static final URI ENDPOINT_CALLBACK_TOKENS = URI.create("callbackTokens");
+
+ public static final URI ENDPOINT_CHANNELS = URI.create("channels");
+
+ public static final URI ENDPOINT_CONTACTS = URI.create("contacts");
+
+ public static final URI ENDPOINT_DEVICE_TOKENS = URI.create("deviceTokens");
+
+ public static final URI ENDPOINT_EVENTS = URI.create("events");
+
+ public static final URI ENDPOINT_MEDIA = URI.create("media");
+
+ public static final URI ENDPOINT_MIGRATION = URI.create("migrations");
+
+ public static final URI ENDPOINT_NODES = URI.create("nodes");
+ public static final URI ENDPOINT_NODE = URI.create("nodes/");
+
+ public static final URI ENDPOINT_PASSWORDS = URI.create("auth/passwordReset");
+
+ public static final URI ENDPOINT_RESERVATIONS = URI.create("nodes/reservation");
+
+ public static final URI ENDPOINT_RULES = URI.create("rules");
+
+ public static final URI ENDPOINT_SESSIONS = URI.create("auth/sessions");
+ public static final URI ENDPOINT_SESSION = URI.create("auth/sessions/");
+
+ public static final URI ENDPOINT_TOPOLOGY = URI.create("topology");
+
+ public static final URI ENDPOINT_USERS = URI.create("users");
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiRequest.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiRequest.java
new file mode 100644
index 0000000000000..567b46da52e76
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiRequest.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.exception.HiveClientRequestException;
+
+/**
+ * A facade for HTTP requests to the Hive API
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface HiveApiRequest {
+ HiveApiRequest accept(MediaType mediaType);
+
+ /**
+ *
+ * @throws HiveClientRequestException
+ */
+ HiveApiResponse get();
+
+
+ /**
+ *
+ * @throws HiveClientRequestException
+ */
+ HiveApiResponse post(Object requestBody);
+
+ /**
+ *
+ * @throws HiveClientRequestException
+ */
+ HiveApiResponse put(Object requestBody);
+
+
+ /**
+ *
+ * @throws HiveClientRequestException
+ */
+ HiveApiResponse delete();
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiRequestFactory.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiRequestFactory.java
new file mode 100644
index 0000000000000..58ddf6248e486
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiRequestFactory.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.net.URI;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface HiveApiRequestFactory {
+ HiveApiRequest newRequest(URI endpointPath);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiResponse.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiResponse.java
new file mode 100644
index 0000000000000..cf0aad143d1d8
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveApiResponse.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A facade for HTTP responses from the Hive API
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface HiveApiResponse {
+ int getStatusCode();
+
+ T getContent(Class contentType);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveClient.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveClient.java
new file mode 100644
index 0000000000000..6645d6e192f7d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveClient.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import java.util.Set;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface HiveClient extends AutoCloseable {
+ UserId getUserId();
+
+ Set getAllNodes();
+ @Nullable Node getNode(NodeId nodeId);
+ @Nullable Node updateNode(Node node);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveClientFactory.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveClientFactory.java
new file mode 100644
index 0000000000000..3ee24ccb097db
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/HiveClientFactory.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.openhab.binding.hive.internal.client.repository.DefaultNodeRepository;
+import org.openhab.binding.hive.internal.client.repository.DefaultSessionRepository;
+import org.openhab.binding.hive.internal.client.repository.NodeRepository;
+import org.openhab.binding.hive.internal.client.repository.SessionRepository;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveClientFactory {
+ public static HiveClient newClient(
+ final Username username,
+ final Password password
+ ) {
+ Objects.requireNonNull(username);
+ Objects.requireNonNull(password);
+
+ final HttpClient httpClient = new HttpClient(new SslContextFactory());
+ try {
+ httpClient.start();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+
+ final JettyHiveApiRequestFactory requestFactory = new JettyHiveApiRequestFactory(
+ httpClient,
+ HiveApiConstants.DEFAULT_BASE_PATH,
+ new GsonJsonService(),
+ "openHAB"
+ );
+
+ final SessionRepository sessionRepository = new DefaultSessionRepository(
+ requestFactory
+ );
+
+ final NodeRepository nodeRepository = new DefaultNodeRepository(
+ requestFactory
+ );
+
+ return new DefaultHiveClient(
+ requestFactory,
+ username,
+ password,
+ sessionRepository,
+ nodeRepository
+ );
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiRequest.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiRequest.java
new file mode 100644
index 0000000000000..452a0efcc024e
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiRequest.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpMethod;
+import org.openhab.binding.hive.internal.client.exception.HiveClientRequestException;
+
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+final class JettyHiveApiRequest implements HiveApiRequest {
+ private final JsonService jsonService;
+ private final Request request;
+
+ public JettyHiveApiRequest(
+ final JsonService jsonService,
+ final Request request
+ ) {
+ Objects.requireNonNull(jsonService);
+ Objects.requireNonNull(request);
+
+ this.jsonService = jsonService;
+ this.request = request;
+ }
+
+ private void setContent(final Object content) {
+ this.request.content(
+ new StringContentProvider(this.jsonService.toJson(content)),
+ MediaType.JSON.toString()
+ );
+ }
+
+ @Override
+ public HiveApiRequest accept(final MediaType mediaType) {
+ this.request.accept(mediaType.toString());
+
+ return this;
+ }
+
+ @Override
+ public HiveApiResponse get() {
+ this.request.method(HttpMethod.GET);
+
+ return this.sendRequest();
+ }
+
+ @Override
+ public HiveApiResponse post(final Object requestBody) {
+ Objects.requireNonNull(requestBody);
+
+ this.request.method(HttpMethod.POST);
+ this.setContent(requestBody);
+
+ return this.sendRequest();
+ }
+
+ @Override
+ public HiveApiResponse put(final Object requestBody) {
+ Objects.requireNonNull(requestBody);
+
+ this.request.method(HttpMethod.PUT);
+ this.setContent(requestBody);
+
+ return this.sendRequest();
+ }
+
+ @Override
+ public HiveApiResponse delete() {
+ this.request.method(HttpMethod.DELETE);
+
+ return this.sendRequest();
+ }
+
+ private HiveApiResponse sendRequest() {
+ try {
+ final ContentResponse response = this.request.send();
+
+ return new JettyHiveApiResponse(this.jsonService, response);
+ } catch (InterruptedException | TimeoutException | ExecutionException ex) {
+ throw new HiveClientRequestException(ex);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiRequestFactory.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiRequestFactory.java
new file mode 100644
index 0000000000000..923a3a64d707c
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiRequestFactory.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler;
+import org.eclipse.jetty.client.api.Request;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+final class JettyHiveApiRequestFactory implements HiveApiRequestFactory, SessionAuthenticationManager {
+ private static final String ACCESS_TOKEN_HEADER = "X-Omnia-Access-Token";
+ private static final String CLIENT_ID_HEADER = "X-Omnia-Client";
+
+ private final Logger logger = LoggerFactory.getLogger(JettyHiveApiRequestFactory.class);
+
+ private final HttpClient httpClient;
+ private final URI apiBasePath;
+ private final JsonService jsonService;
+ private final String clientId;
+
+ @Nullable
+ private Session session = null;
+
+ public JettyHiveApiRequestFactory(
+ final HttpClient httpClient,
+ final URI apiBasePath,
+ final JsonService jsonService,
+ final String clientId
+ ) {
+ Objects.requireNonNull(httpClient);
+ Objects.requireNonNull(apiBasePath);
+ Objects.requireNonNull(jsonService);
+ Objects.requireNonNull(clientId);
+
+ if (!apiBasePath.isAbsolute()) {
+ throw new IllegalArgumentException("API base path must be absolute");
+ }
+
+ this.httpClient = httpClient;
+ this.apiBasePath = apiBasePath;
+ this.jsonService = jsonService;
+ this.clientId = clientId;
+
+ // Remove jetty's default authentication handler as it will be confused
+ // by the Hive API's lack of "WWW-Authenticate" header and will mess
+ // up manual authentication.
+ this.httpClient.getProtocolHandlers().remove(WWWAuthenticationProtocolHandler.NAME);
+ }
+
+ @Override
+ public void clearSession() {
+ this.session = null;
+ }
+
+ @Override
+ public @Nullable Session getSession() {
+ return this.session;
+ }
+
+ @Override
+ public void setSession(final Session session) {
+ Objects.requireNonNull(session);
+
+ this.session = session;
+ }
+
+ @Override
+ public HiveApiRequest newRequest(final URI endpointPath) {
+ final URI target = this.apiBasePath.resolve(endpointPath);
+
+ final Request request = this.httpClient.newRequest(target);
+
+ // Add X-Omnia-Client header
+ request.header(CLIENT_ID_HEADER, this.clientId);
+
+ // If we have a session ID add X-Omnia-Access-Token header.
+ if (this.session != null) {
+ request.header(ACCESS_TOKEN_HEADER, this.session.getSessionId().toString());
+ }
+
+ return new JettyHiveApiRequest(
+ this.jsonService,
+ request
+ );
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiResponse.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiResponse.java
new file mode 100644
index 0000000000000..683e4ff95806b
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JettyHiveApiResponse.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.api.ContentResponse;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+final class JettyHiveApiResponse implements HiveApiResponse {
+ private final JsonService jsonService;
+ private final ContentResponse response;
+
+ public JettyHiveApiResponse(
+ final JsonService jsonService,
+ final ContentResponse response
+ ) {
+ Objects.requireNonNull(jsonService);
+ Objects.requireNonNull(response);
+
+ this.jsonService = jsonService;
+ this.response = response;
+ }
+
+ @Override
+ public int getStatusCode() {
+ return this.response.getStatus();
+ }
+
+ @Override
+ public T getContent(Class contentType) {
+ return this.jsonService.fromJson(this.response.getContentAsString(), contentType);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JsonService.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JsonService.java
new file mode 100644
index 0000000000000..272148b648963
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/JsonService.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A facade for JSON serialisation/deserialisation.
+ *
+ *
+ * Used to decouple from Gson and make testing easier.
+ * ({@code Gson} is final and so annoying to replace with a test double)
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface JsonService {
+ String toJson(Object object);
+ T fromJson(String json, Class classOfT);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/MediaType.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/MediaType.java
new file mode 100644
index 0000000000000..98058fb97f485
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/MediaType.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class MediaType extends StringTypeBase {
+ public static final MediaType JSON = new MediaType("application/json");
+ public static final MediaType API_V6_0_0_JSON = new MediaType("application/vnd.alertme.zoo-6.0.0+json");
+ public static final MediaType API_V6_1_0_JSON = new MediaType("application/vnd.alertme.zoo-6.1.0+json");
+ public static final MediaType API_V6_2_0_JSON = new MediaType("application/vnd.alertme.zoo-6.2.0+json");
+ public static final MediaType API_V6_3_0_JSON = new MediaType("application/vnd.alertme.zoo-6.3.0+json");
+ public static final MediaType API_V6_4_0_JSON = new MediaType("application/vnd.alertme.zoo-6.4.0+json");
+ public static final MediaType API_V6_5_0_JSON = new MediaType("application/vnd.alertme.zoo-6.5.0+json");
+ public static final MediaType API_V6_6_0_JSON = new MediaType("application/vnd.alertme.zoo-6.6.0+json");
+
+ public MediaType(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Node.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Node.java
new file mode 100644
index 0000000000000..25702ef7be317
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Node.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.feature.Feature;
+
+import java.util.Set;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface Node {
+ NodeId getId();
+ NodeName getName();
+ NodeType getNodeType();
+ ProductType getProductType();
+ Protocol getProtocol();
+ NodeId getParentNodeId();
+
+ Set getFeatures();
+ @Nullable T getFeature(Class classOfFeature);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeId.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeId.java
new file mode 100644
index 0000000000000..4d6cc158ce512
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeId.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class NodeId extends UuidTypeBase {
+ public NodeId(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeName.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeName.java
new file mode 100644
index 0000000000000..a3f66b223ff44
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeName.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class NodeName extends StringTypeBase {
+ public NodeName(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeType.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeType.java
new file mode 100644
index 0000000000000..553871cdf6261
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/NodeType.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class NodeType extends URITypeBase {
+ public static final NodeType HUB = new NodeType("http://alertme.com/schema/json/node.class.hub.json#");
+ public static final NodeType THERMOSTAT = new NodeType("http://alertme.com/schema/json/node.class.thermostat.json#");
+ public static final NodeType THERMOSTAT_UI = new NodeType("http://alertme.com/schema/json/node.class.thermostatui.json#");
+ public static final NodeType RADIATOR_VALVE = new NodeType("http://alertme.com/schema/json/node.class.trv.json#");
+
+ public NodeType(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OnOffMode.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OnOffMode.java
new file mode 100644
index 0000000000000..18650909179ec
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OnOffMode.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public enum OnOffMode {
+ ON,
+ OFF
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OperatingMode.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OperatingMode.java
new file mode 100644
index 0000000000000..12b72b0d13d95
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OperatingMode.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public enum OperatingMode {
+ SCHEDULE,
+ MANUAL
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OverrideMode.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OverrideMode.java
new file mode 100644
index 0000000000000..b34337ca39338
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/OverrideMode.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public enum OverrideMode {
+ NONE,
+ TRANSIENT
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Password.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Password.java
new file mode 100644
index 0000000000000..19e62a69df0f0
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Password.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A password value type.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class Password extends StringTypeBase {
+ public Password(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/ProductType.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/ProductType.java
new file mode 100644
index 0000000000000..fb05531498631
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/ProductType.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class ProductType extends StringTypeBase {
+ public static final ProductType BOILER_MODULE = new ProductType("BOILER_MODULE");
+ public static final ProductType DAYLIGHT_SD = new ProductType("DAYLIGHT_SD");
+ public static final ProductType HEATING = new ProductType("HEATING");
+ public static final ProductType HOT_WATER = new ProductType("HOT_WATER");
+ public static final ProductType HUB = new ProductType("HUB");
+ public static final ProductType THERMOSTAT_UI = new ProductType("THERMOSTAT_UI");
+ public static final ProductType TRV = new ProductType("TRV");
+ public static final ProductType TRV_GROUP = new ProductType("TRV_GROUP");
+
+ public ProductType(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Protocol.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Protocol.java
new file mode 100644
index 0000000000000..86a516f8d0285
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Protocol.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class Protocol extends StringTypeBase {
+ public static final Protocol SYNTHETIC = new Protocol("SYNTHETIC");
+ public static final Protocol ZIGBEE = new Protocol("ZIGBEE");
+ public static final Protocol PROXIED = new Protocol("PROXIED");
+ public static final Protocol MQTT = new Protocol("MQTT");
+ public static final Protocol XMPP = new Protocol("XMPP");
+ public static final Protocol VIRTUAL = new Protocol("VIRTUAL");
+
+ public static final Protocol NONE = new Protocol("NONE");
+
+ public Protocol(String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Session.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Session.java
new file mode 100644
index 0000000000000..d6e3870fd1330
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Session.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class Session {
+ private SessionId sessionId;
+ private UserId userId;
+
+ public Session(
+ final SessionId sessionId,
+ final UserId userId
+ ) {
+ Objects.requireNonNull(sessionId);
+ Objects.requireNonNull(userId);
+
+ this.sessionId = sessionId;
+ this.userId = userId;
+ }
+
+ public SessionId getSessionId() {
+ return sessionId;
+ }
+
+ public UserId getUserId() {
+ return userId;
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final Session session = (Session) o;
+ return sessionId.equals(session.sessionId) &&
+ userId.equals(session.userId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(sessionId, userId);
+ }
+
+ @Override
+ public String toString() {
+ return "Session{" +
+ "sessionId=" + sessionId +
+ ", userId=" + userId +
+ '}';
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SessionAuthenticationManager.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SessionAuthenticationManager.java
new file mode 100644
index 0000000000000..29a1081d59351
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SessionAuthenticationManager.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+interface SessionAuthenticationManager {
+ void clearSession();
+ @Nullable Session getSession();
+ void setSession(Session session);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SessionId.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SessionId.java
new file mode 100644
index 0000000000000..bc95c3f019d85
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SessionId.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class SessionId extends StringTypeBase {
+ public SessionId(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SettableFeatureAttribute.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SettableFeatureAttribute.java
new file mode 100644
index 0000000000000..f5ed8ac7e6b1d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/SettableFeatureAttribute.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class SettableFeatureAttribute extends FeatureAttribute {
+ private @Nullable T targetValue = null;
+
+ SettableFeatureAttribute(
+ final T reportedValue,
+ final Instant changedTime,
+ final Instant receivedTime,
+ final T displayValue
+ ) {
+ super(reportedValue,changedTime,receivedTime,displayValue);
+ }
+
+ public void setTargetValue(final T targetValue) {
+ Objects.requireNonNull(targetValue);
+
+ this.targetValue = targetValue;
+ }
+
+ public void clearTargetValue() {
+ this.targetValue = null;
+ }
+
+ public @Nullable T getTargetValue() {
+ return this.targetValue;
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/StringTypeBase.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/StringTypeBase.java
new file mode 100644
index 0000000000000..045089fe51e3d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/StringTypeBase.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+abstract class StringTypeBase extends FromStringTypeBase {
+ public StringTypeBase(final String stringValue) {
+ super(stringValue);
+ }
+
+ protected String transform(final String stringValue) {
+ if (stringValue.isEmpty()) {
+ throw new IllegalArgumentException("Value must not be empty");
+ }
+
+ return stringValue;
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Temperature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Temperature.java
new file mode 100644
index 0000000000000..d72e1ccc35661
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Temperature.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class Temperature {
+ public enum Unit {
+ CELSIUS
+ }
+
+ private final BigDecimal value;
+ private final Unit unit;
+
+ /**
+ * Create a new temperature using the default unit of Celsius.
+ *
+ * @param value
+ * The value of the temperature.
+ */
+ public Temperature(final BigDecimal value) {
+ this.value = value;
+ this.unit = Unit.CELSIUS;
+ }
+
+ public BigDecimal getValue() {
+ return this.value;
+ }
+
+ public Unit getUnit() {
+ return this.unit;
+ }
+
+ @Override
+ public String toString() {
+ String out = this.value.toString();
+
+ switch (this.unit) {
+ case CELSIUS:
+ out += "\u00B0C";
+ break;
+ }
+
+ return out;
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final Temperature that = (Temperature) o;
+ return value.equals(that.value) &&
+ unit == that.unit;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value, unit);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/URITypeBase.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/URITypeBase.java
new file mode 100644
index 0000000000000..f41bc3b8d322d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/URITypeBase.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+abstract class URITypeBase extends FromStringTypeBase {
+ public URITypeBase(final String stringValue) {
+ super(stringValue);
+ }
+
+ protected URI transform(final String stringValue) {
+ try {
+ return new URI(stringValue);
+ } catch (final URISyntaxException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/UserId.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/UserId.java
new file mode 100644
index 0000000000000..aac459b5eb459
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/UserId.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class UserId extends UuidTypeBase {
+ public UserId(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Username.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Username.java
new file mode 100644
index 0000000000000..26ab106299e03
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/Username.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A username value type.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class Username extends StringTypeBase {
+ public Username(final String stringValue) {
+ super(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/UuidTypeBase.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/UuidTypeBase.java
new file mode 100644
index 0000000000000..531236a1ee624
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/UuidTypeBase.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.util.UUID;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+abstract class UuidTypeBase extends FromStringTypeBase {
+ public UuidTypeBase(final String stringValue) {
+ super(stringValue);
+ }
+
+ protected UUID transform(final String stringValue) {
+ return UUID.fromString(stringValue);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ActionTypeGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ActionTypeGsonAdapter.java
new file mode 100644
index 0000000000000..f635c65a792c7
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ActionTypeGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.ActionType;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class ActionTypeGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public ActionType read(final JsonReader in) throws IOException {
+ return new ActionType(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/AttributeNameGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/AttributeNameGsonAdapter.java
new file mode 100644
index 0000000000000..759b9ef186955
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/AttributeNameGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.AttributeName;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class AttributeNameGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public AttributeName read(final JsonReader in) throws IOException {
+ return new AttributeName(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/FeatureTypeGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/FeatureTypeGsonAdapter.java
new file mode 100644
index 0000000000000..ead1e027f95c3
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/FeatureTypeGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.FeatureType;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class FeatureTypeGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public FeatureType read(final JsonReader in) throws IOException {
+ return new FeatureType(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/HiveApiInstantGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/HiveApiInstantGsonAdapter.java
new file mode 100644
index 0000000000000..63271dae86748
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/HiveApiInstantGsonAdapter.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.dto.HiveApiInstant;
+
+import java.io.IOException;
+import java.time.Instant;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveApiInstantGsonAdapter extends TypeAdapter {
+ @NonNullByDefault({})
+ @Override
+ public void write(final JsonWriter out, final @Nullable HiveApiInstant hiveApiInstant) throws IOException {
+ if (hiveApiInstant != null) {
+ out.value(hiveApiInstant.asInstant().toEpochMilli());
+ } else {
+ out.nullValue();
+ }
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public HiveApiInstant read(final JsonReader in) throws IOException {
+ return new HiveApiInstant(Instant.ofEpochMilli(in.nextLong()));
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeIdGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeIdGsonAdapter.java
new file mode 100644
index 0000000000000..266a4ec39017d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeIdGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.NodeId;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class NodeIdGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public NodeId read(final JsonReader in) throws IOException {
+ return new NodeId(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeNameGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeNameGsonAdapter.java
new file mode 100644
index 0000000000000..a3bc6fe864c2d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeNameGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.NodeName;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class NodeNameGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public NodeName read(final JsonReader in) throws IOException {
+ return new NodeName(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeTypeGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeTypeGsonAdapter.java
new file mode 100644
index 0000000000000..edca05261fdfd
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/NodeTypeGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.NodeType;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class NodeTypeGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public NodeType read(final JsonReader in) throws IOException {
+ return new NodeType(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/PasswordGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/PasswordGsonAdapter.java
new file mode 100644
index 0000000000000..2b19790f95b2a
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/PasswordGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.Password;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class PasswordGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public Password read(final JsonReader in) throws IOException {
+ return new Password(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ProductTypeGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ProductTypeGsonAdapter.java
new file mode 100644
index 0000000000000..b18fdc68fe9cf
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ProductTypeGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.ProductType;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class ProductTypeGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public ProductType read(final JsonReader in) throws IOException {
+ return new ProductType(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/SessionIdGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/SessionIdGsonAdapter.java
new file mode 100644
index 0000000000000..fabebed60b629
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/SessionIdGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.SessionId;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class SessionIdGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public SessionId read(final JsonReader in) throws IOException {
+ return new SessionId(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/SimpleGsonTypeAdapterBase.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/SimpleGsonTypeAdapterBase.java
new file mode 100644
index 0000000000000..540847bb24d88
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/SimpleGsonTypeAdapterBase.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonWriter;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+abstract class SimpleGsonTypeAdapterBase extends TypeAdapter {
+ @NonNullByDefault({})
+ @Override
+ public void write(final JsonWriter out, final @Nullable T value) throws IOException {
+ if (value != null) {
+ out.value(value.toString());
+ } else {
+ out.nullValue();
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/UserIdGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/UserIdGsonAdapter.java
new file mode 100644
index 0000000000000..40b6aa9c79625
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/UserIdGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.UserId;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class UserIdGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public UserId read(final JsonReader in) throws IOException {
+ return new UserId(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/UsernameGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/UsernameGsonAdapter.java
new file mode 100644
index 0000000000000..2008311b2ec41
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/UsernameGsonAdapter.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.stream.JsonReader;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.Username;
+
+import java.io.IOException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class UsernameGsonAdapter extends SimpleGsonTypeAdapterBase {
+ @NonNullByDefault({})
+ @Override
+ public Username read(final JsonReader in) throws IOException {
+ return new Username(in.nextString());
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ZonedDateTimeGsonAdapter.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ZonedDateTimeGsonAdapter.java
new file mode 100644
index 0000000000000..7b9e22e18ad68
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/adapter/ZonedDateTimeGsonAdapter.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.adapter;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ *2020-03-04T19:13:47.462+0000
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class ZonedDateTimeGsonAdapter extends TypeAdapter {
+ private static final DateTimeFormatter HIVE_DATETIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+ @NonNullByDefault({})
+ @Override
+ public void write(final JsonWriter out, final @Nullable ZonedDateTime zonedDateTime) throws IOException {
+ if (zonedDateTime != null) {
+ out.value(zonedDateTime.format(HIVE_DATETIME_FORMAT));
+ } else {
+ out.nullValue();
+ }
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public ZonedDateTime read(final JsonReader in) throws IOException {
+ return ZonedDateTime.parse(in.nextString(), HIVE_DATETIME_FORMAT);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/ActionDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/ActionDto.java
new file mode 100644
index 0000000000000..131f180774c14
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/ActionDto.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.ActionType;
+import org.openhab.binding.hive.internal.client.AttributeName;
+import org.openhab.binding.hive.internal.client.FeatureType;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class ActionDto {
+ public ActionType actionType;
+ public FeatureType featureType;
+ public AttributeName attribute;
+ public String value;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/BatteryDeviceV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/BatteryDeviceV1FeatureDto.java
new file mode 100644
index 0000000000000..28d0d0b038127
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/BatteryDeviceV1FeatureDto.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.math.BigDecimal;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class BatteryDeviceV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto batteryLevel;
+ public FeatureAttributeDto batteryState;
+ public FeatureAttributeDto batteryVoltage;
+ public FeatureAttributeDto notificationState;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/DeviceManagementV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/DeviceManagementV1FeatureDto.java
new file mode 100644
index 0000000000000..d6d12d2636abf
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/DeviceManagementV1FeatureDto.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.ProductType;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class DeviceManagementV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto productType;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeatureAttributeDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeatureAttributeDto.java
new file mode 100644
index 0000000000000..0edb36af26575
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeatureAttributeDto.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class FeatureAttributeDto {
+ public T reportedValue;
+ public T targetValue;
+ public T displayValue;
+
+ public HiveApiInstant reportReceivedTime;
+ public HiveApiInstant reportChangedTime;
+ public HiveApiInstant targetSetTime;
+ public HiveApiInstant targetExpiryTime;
+
+ public String targetSetTXId;
+ public String propertyStatus;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeatureDtoBase.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeatureDtoBase.java
new file mode 100644
index 0000000000000..0a8dcf2ab6662
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeatureDtoBase.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.FeatureType;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public abstract class FeatureDtoBase {
+ public FeatureAttributeDto featureType;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeaturesDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeaturesDto.java
new file mode 100644
index 0000000000000..4bc17eb5105cf
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/FeaturesDto.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class FeaturesDto {
+ public BatteryDeviceV1FeatureDto battery_device_v1;
+ public DeviceManagementV1FeatureDto device_management_v1;
+ public HeatingThermostatV1FeatureDto heating_thermostat_v1;
+ public OnOffDeviceV1FeatureDto on_off_device_v1;
+ public TemperatureSensorV1FeatureDto temperature_sensor_v1;
+ public TransientModeV1FeatureDto transient_mode_v1;
+ public WaterHeaterV1FeatureDto water_heater_v1;
+ public ZigbeeDeviceV1FeatureDto zigbee_device_v1;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/HeatingThermostatV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/HeatingThermostatV1FeatureDto.java
new file mode 100644
index 0000000000000..e5234091c7acb
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/HeatingThermostatV1FeatureDto.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.OperatingMode;
+import org.openhab.binding.hive.internal.client.OverrideMode;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class HeatingThermostatV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto operatingMode;
+ public FeatureAttributeDto operatingState;
+ public FeatureAttributeDto temporaryOperatingModeOverride;
+ public FeatureAttributeDto targetHeatTemperature;
+ public FeatureAttributeDto maxHeatTargetTemperature;
+ public FeatureAttributeDto minHeatTargetTemperature;
+
+ public FeatureAttributeDto> previousConfiguration;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/HiveApiInstant.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/HiveApiInstant.java
new file mode 100644
index 0000000000000..c55b985bd3cdf
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/HiveApiInstant.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * A class representing absolute times returned by the Hive API.
+ *
+ *
+ * N.B. This should only be used by DTOs.
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveApiInstant {
+ private Instant instant;
+
+ public HiveApiInstant(final Instant instant) {
+ Objects.requireNonNull(instant);
+
+ this.instant = instant;
+ }
+
+ public Instant asInstant() {
+ return this.instant;
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final HiveApiInstant that = (HiveApiInstant) o;
+ return instant.equals(that.instant);
+ }
+
+ @NonNullByDefault({})
+ @Override
+ public int hashCode() {
+ return Objects.hash(instant);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/NodeDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/NodeDto.java
new file mode 100644
index 0000000000000..8e888caa8e37f
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/NodeDto.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.NodeId;
+import org.openhab.binding.hive.internal.client.NodeName;
+import org.openhab.binding.hive.internal.client.NodeType;
+import org.openhab.binding.hive.internal.client.UserId;
+
+/**
+ * A model of a "Node"
+ *
+ * Based on the Hive API Swagger model.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class NodeDto {
+ /**
+ * address (string, optional): Internal node ID, read-only.
+ * The first part of node ID can be hub ID or node address.
+ * Hub ID conforms following pattern: ([0-9]{19}).
+ * Node address conforms following pattern: ::([a-f0-9]{1,4}):([a-f0-9]{1,4}):([a-f0-9]{1,4}):([a-f0-9]{1,4}).
+ * Admin access required.
+ */
+ public String address;
+
+ /*
+ * brokers (inline_model_26, optional): Brokers address map
+ * e.g.: {"greengrass": {"url": "mqtt://10.0.0.7:8883", "topic": "hub/deviceid/userid"}}
+ */
+
+ /**
+ * createdOn (integer, optional): Timestamp of when the node state was created.
+ * UTC Unix timestamp (in milliseconds)
+ */
+ public HiveApiInstant createdOn;
+
+ public FeaturesDto features;
+
+ /**
+ * firstInstall (integer, optional): Timestamp of when the device was discovered.
+ * UTC Unix timestamp (in milliseconds)
+ */
+ public HiveApiInstant firstInstall;
+
+ /**
+ * homeId (string, optional): Home ID, read-only
+ */
+ public String homeId;
+
+ /**
+ * href (string, optional, read only): URL of the API call for retrieving this object
+ */
+ public String href;
+
+ /**
+ * id (string, optional): Object identifier
+ */
+ public NodeId id;
+
+ /**
+ * lastSeen (integer, optional): Timestamp of when the node was last seen.
+ * UTC Unix timestamp (in milliseconds)
+ */
+ public HiveApiInstant lastSeen;
+
+ /**
+ * lastUpgradeSucceeded (boolean, optional): Last upgrade succeeded
+ */
+ public Boolean lastUpgradeSucceeded;
+
+ /*
+ * links (inline_model_27, optional): URL Templates for links to other entities
+ * e.g.: "users.nodes": "https://api.example.com/nodes/{users.nodes}"
+ */
+
+ /**
+ * name (string, optional): Node name
+ */
+ public NodeName name;
+
+ /**
+ * nodeType (string, optional): Node type
+ */
+ public NodeType nodeType;
+
+ /**
+ * ownerId (string, optional): Original owner UUID
+ */
+ public UserId ownerId;
+
+ /**
+ * parentNodeId (string, optional): Parent node UUID
+ */
+ public NodeId parentNodeId;
+
+ /**
+ * protocol (string, optional): Node protocol, optional, read-only
+ * ['SYNTHETIC', 'ZIGBEE', 'PROXIED', 'MQTT', 'XMPP', 'VIRTUAL', 'any other provided by hub']
+ */
+ public String protocol;
+
+ /*
+ * relationships (inline_model_28, optional): Node relationships
+ * e.g.: "boundNodes" : [{ "type" : "node", "id" : "1b3b3c30-740a-11e5-8a3b-f46d042e952c"}] ,
+ */
+
+ /**
+ * upgradeAvailable (boolean, optional): Is upgrade available
+ */
+ public Boolean upgradeAvailable;
+
+ /**
+ * upgradeProgress (number, optional): Upgrade progress in percentages
+ */
+ public Double upgradeProgress;
+
+ /**
+ * upgradeStatus (string, optional): Upgrade status, read-only
+ * ['PENDING', 'DEFERRED', 'STARTING', 'IN_PROGRESS', 'COMPLETE', 'FAILED', 'UNSUPPORTED', 'INVALID']
+ */
+ public String upgradeStatus;
+
+ /**
+ * userId (string, optional): User UUID
+ */
+ public UserId userId;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/NodesDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/NodesDto.java
new file mode 100644
index 0000000000000..ec4a3fbbe6519
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/NodesDto.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.util.List;
+
+/**
+ * A model of a "NodeRequestEntity"/"NodeResponseEntity"
+ *
+ * Based on the Hive API Swagger model.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class NodesDto {
+ /*
+ * linked (object, optional): Linked entities grouped by entity type. Used when side-loading entities
+ */
+
+ /*
+ * links (inline_model_34, optional): URL Templates for links to other entities e.g.: "users.nodes": "https://api.example.com/nodes/{users.nodes}" ,
+ */
+
+ /*
+ * meta (object, optional): Meta information about this entity
+ */
+
+ /**
+ * sessions (Array[Session], optional): List of sessions
+ */
+ public List nodes;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/OnOffDeviceV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/OnOffDeviceV1FeatureDto.java
new file mode 100644
index 0000000000000..cfacb410784d3
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/OnOffDeviceV1FeatureDto.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.OnOffMode;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class OnOffDeviceV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto mode;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/SessionDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/SessionDto.java
new file mode 100644
index 0000000000000..f80074748293c
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/SessionDto.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.Password;
+import org.openhab.binding.hive.internal.client.SessionId;
+import org.openhab.binding.hive.internal.client.UserId;
+import org.openhab.binding.hive.internal.client.Username;
+
+/**
+ * A model of a "Session"
+ *
+ * Based on the Hive API Swagger model.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class SessionDto {
+ /**
+ * extCustomerLevel (integer, optional): The user's account level in a third party's database (default value 1)
+ */
+ public Integer extCustomerLevel;
+
+ /**
+ * href (string, optional, read only): URL of the API call for retrieving this object
+ */
+ public String href;
+
+ /**
+ * id (string, optional)
+ */
+ public SessionId id;
+
+ /**
+ * latestSupportedApiVersion (string, optional): The API version which the user's account and data are compatible with
+ */
+ public String latestSupportedApiVersion;
+
+ /*
+ * links (inline_model_33, optional): URL Templates for links to other entities e.g.: "users.nodes": "https://api.example.com/nodes/{users.nodes}" ,
+ */
+
+ /**
+ * password (string, optional): Password
+ */
+ public Password password;
+
+ /**
+ * sessionDuration (integer, optional): Session duration in minutes
+ */
+ public Integer sessionDuration;
+
+ /**
+ * sessionId (string, optional): UUID of this session
+ */
+ public SessionId sessionId;
+
+ /**
+ * userId (string, optional): User UUID
+ */
+ public UserId userId;
+
+ /**
+ * username (string, optional): Username
+ */
+ public Username username;
+
+ /**
+ * uuid (string, optional): User UUID
+ */
+ public UserId uuid;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/SessionsDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/SessionsDto.java
new file mode 100644
index 0000000000000..c547379cce658
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/SessionsDto.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.util.List;
+
+/**
+ * A model of a "SessionRequestEntity"/"SessionResponseEntity"
+ *
+ * Based on the Hive API Swagger model.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class SessionsDto {
+ /*
+ * linked (object, optional): Linked entities grouped by entity type. Used when side-loading entities
+ */
+
+ /*
+ * links (inline_model_34, optional): URL Templates for links to other entities e.g.: "users.nodes": "https://api.example.com/nodes/{users.nodes}" ,
+ */
+
+ /*
+ * meta (object, optional): Meta information about this entity
+ */
+
+ /**
+ * sessions (Array[Session], optional): List of sessions
+ */
+ public List sessions;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/TemperatureSensorV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/TemperatureSensorV1FeatureDto.java
new file mode 100644
index 0000000000000..f634bdc6950ac
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/TemperatureSensorV1FeatureDto.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.math.BigDecimal;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class TemperatureSensorV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto temperature;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/TransientModeV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/TransientModeV1FeatureDto.java
new file mode 100644
index 0000000000000..7a33522394e9e
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/TransientModeV1FeatureDto.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class TransientModeV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto> actions;
+ public FeatureAttributeDto duration;
+ public FeatureAttributeDto isEnabled;
+ public FeatureAttributeDto> previousConfiguration;
+ public FeatureAttributeDto startDatetime;
+ public FeatureAttributeDto endDatetime;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/WaterHeaterV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/WaterHeaterV1FeatureDto.java
new file mode 100644
index 0000000000000..85e438d952b23
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/WaterHeaterV1FeatureDto.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.OperatingMode;
+import org.openhab.binding.hive.internal.client.OverrideMode;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class WaterHeaterV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto operatingMode;
+ public FeatureAttributeDto isOn;
+ public FeatureAttributeDto temporaryOperatingModeOverride;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/ZigbeeDeviceV1FeatureDto.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/ZigbeeDeviceV1FeatureDto.java
new file mode 100644
index 0000000000000..bdd2ecc9b3066
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/dto/ZigbeeDeviceV1FeatureDto.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault({})
+public final class ZigbeeDeviceV1FeatureDto extends FeatureDtoBase {
+ public FeatureAttributeDto averageLQI;
+ public FeatureAttributeDto lastKnownLQI;
+ public FeatureAttributeDto averageRSSI;
+ public FeatureAttributeDto lastKnownRSSI;
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiAuthenticationException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiAuthenticationException.java
new file mode 100644
index 0000000000000..e2f623e63507e
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiAuthenticationException.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown to indicate something has gone wrong while authenticating with the
+ * Hive API.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveApiAuthenticationException extends HiveApiException {
+ public HiveApiAuthenticationException() {
+ super();
+ }
+
+ public HiveApiAuthenticationException(final String message) {
+ super(message);
+ }
+
+ public HiveApiAuthenticationException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiException.java
new file mode 100644
index 0000000000000..a52fc86a341aa
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiException.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown to indicate something has gone wrong with a call to the Hive API.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public abstract class HiveApiException extends RuntimeException {
+ public static final long serialVersionUID = 1L;
+
+ public HiveApiException() {
+ super();
+ }
+
+ public HiveApiException(final String message) {
+ super(message);
+ }
+
+ public HiveApiException(final Throwable cause) {
+ super(cause);
+ }
+
+ public HiveApiException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiNotAuthorisedException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiNotAuthorisedException.java
new file mode 100644
index 0000000000000..923998863327d
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiNotAuthorisedException.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown to indicate a call to the Hive API has failed because the client is not authorised.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveApiNotAuthorisedException extends HiveApiException {
+ public HiveApiNotAuthorisedException() {
+ super();
+ }
+
+ public HiveApiNotAuthorisedException(final String message) {
+ super(message);
+ }
+
+ public HiveApiNotAuthorisedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiUnknownException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiUnknownException.java
new file mode 100644
index 0000000000000..1c4768c04797e
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveApiUnknownException.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown to indicate something of an unknown nature has gone wrong while
+ * making a call to the Hive API (and it is the client's fault).
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveApiUnknownException extends HiveApiException {
+ public static final long serialVersionUID = 1L;
+
+ public HiveApiUnknownException() {
+ super();
+ }
+
+ public HiveApiUnknownException(final String message) {
+ super(message);
+ }
+
+ public HiveApiUnknownException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientException.java
new file mode 100644
index 0000000000000..b603e48b296f2
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientException.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public abstract class HiveClientException extends RuntimeException {
+ public HiveClientException() {
+ super();
+ }
+
+ public HiveClientException(final String message) {
+ super(message);
+ }
+
+ public HiveClientException(final Throwable cause) {
+ super(cause);
+ }
+
+ public HiveClientException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientRequestException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientRequestException.java
new file mode 100644
index 0000000000000..a8854642a2340
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientRequestException.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown to indicate a HTTP request to the Hive API has failed before it
+ * could be completed.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveClientRequestException extends HiveClientException {
+ public HiveClientRequestException() {
+ super();
+ }
+
+ public HiveClientRequestException(final String message) {
+ super(message);
+ }
+
+ public HiveClientRequestException(final Throwable cause) {
+ super(cause);
+ }
+
+ public HiveClientRequestException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientResponseException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientResponseException.java
new file mode 100644
index 0000000000000..eb35ac2027435
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientResponseException.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown to indicate that the {@link org.openhab.binding.hive.internal.client.HiveClient}
+ * got a "success" response from the Hive API but either does not understand
+ * the response or thinks it is malformed.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveClientResponseException extends HiveClientException {
+ public HiveClientResponseException() {
+ super();
+ }
+
+ public HiveClientResponseException(final String message) {
+ super(message);
+ }
+
+ public HiveClientResponseException(final Throwable cause) {
+ super(cause);
+ }
+
+ public HiveClientResponseException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientUnknownException.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientUnknownException.java
new file mode 100644
index 0000000000000..4024b17c37746
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/exception/HiveClientUnknownException.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.exception;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Thrown to indicate that the {@link org.openhab.binding.hive.internal.client.HiveClient}
+ * got a "success" response from the Hive API but either does not understand
+ * the response or thinks it is malformed.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HiveClientUnknownException extends HiveClientException {
+ public HiveClientUnknownException() {
+ super();
+ }
+
+ public HiveClientUnknownException(final String message) {
+ super(message);
+ }
+
+ public HiveClientUnknownException(final Throwable cause) {
+ super(cause);
+ }
+
+ public HiveClientUnknownException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/BatteryDeviceFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/BatteryDeviceFeature.java
new file mode 100644
index 0000000000000..28f91f4e044b7
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/BatteryDeviceFeature.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.BatteryLevel;
+import org.openhab.binding.hive.internal.client.FeatureAttribute;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class BatteryDeviceFeature implements Feature {
+ private final FeatureAttribute batteryLevel;
+ private final FeatureAttribute batteryState;
+
+ public BatteryDeviceFeature(
+ final FeatureAttribute batteryLevel,
+ final FeatureAttribute batteryState
+ ) {
+ Objects.requireNonNull(batteryLevel);
+ Objects.requireNonNull(batteryState);
+
+ this.batteryLevel = batteryLevel;
+ this.batteryState = batteryState;
+ }
+
+ public FeatureAttribute getBatteryLevelAttribute() {
+ return this.batteryLevel;
+ }
+
+ public BatteryLevel getBatteryLevel() {
+ return this.batteryLevel.getDisplayValue();
+ }
+
+ public FeatureAttribute getBatteryStateAttribute() {
+ return this.batteryState;
+ }
+
+ public String getBatteryState() {
+ return this.batteryState.getDisplayValue();
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/Feature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/Feature.java
new file mode 100644
index 0000000000000..967f10531d682
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/Feature.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface Feature {}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/HeatingThermostatFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/HeatingThermostatFeature.java
new file mode 100644
index 0000000000000..55337e3c3c966
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/HeatingThermostatFeature.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.*;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HeatingThermostatFeature implements Feature {
+ private final SettableFeatureAttribute operatingMode;
+ private final FeatureAttribute operatingState;
+ private final SettableFeatureAttribute targetHeatTemperature;
+ private final SettableFeatureAttribute temporaryOperatingModeOverride;
+
+ public HeatingThermostatFeature(
+ final SettableFeatureAttribute operatingMode,
+ final FeatureAttribute operatingState,
+ final SettableFeatureAttribute targetHeatTemperature,
+ final SettableFeatureAttribute temporaryOperatingModeOverride
+ ) {
+ Objects.requireNonNull(operatingMode);
+ Objects.requireNonNull(operatingState);
+ Objects.requireNonNull(targetHeatTemperature);
+ Objects.requireNonNull(temporaryOperatingModeOverride);
+
+ this.operatingMode = operatingMode;
+ this.operatingState = operatingState;
+ this.targetHeatTemperature = targetHeatTemperature;
+ this.temporaryOperatingModeOverride = temporaryOperatingModeOverride;
+ }
+
+ public SettableFeatureAttribute getOperatingModeAttribute() {
+ return this.operatingMode;
+ }
+
+ public OperatingMode getOperatingMode() {
+ return this.operatingMode.getDisplayValue();
+ }
+
+ public void setOperatingMode(final OperatingMode operatingMode) {
+ Objects.requireNonNull(operatingMode);
+
+ this.operatingMode.setTargetValue(operatingMode);
+ }
+
+ public FeatureAttribute getOperatingStateAttribute() {
+ return this.operatingState;
+ }
+
+ public String getOperatingState() {
+ return this.operatingState.getDisplayValue();
+ }
+
+ public SettableFeatureAttribute getTargetHeatTemperatureAttribute() {
+ return this.targetHeatTemperature;
+ }
+
+ public Temperature getTargetHeatTemperature() {
+ return this.targetHeatTemperature.getDisplayValue();
+ }
+
+ public void setTargetHeatTemperature(final Temperature targetHeatTemperature) {
+ Objects.requireNonNull(targetHeatTemperature);
+
+ this.targetHeatTemperature.setTargetValue(targetHeatTemperature);
+ }
+
+ public SettableFeatureAttribute getTemporaryOperatingModeOverrideAttribute() {
+ return this.temporaryOperatingModeOverride;
+ }
+
+ public OverrideMode getTemporaryOperatingModeOverride() {
+ return this.temporaryOperatingModeOverride.getDisplayValue();
+ }
+
+ public void setTemporaryOperatingModeOverride(final OverrideMode overrideMode) {
+ Objects.requireNonNull(overrideMode);
+
+ this.temporaryOperatingModeOverride.setTargetValue(overrideMode);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/HeatingTransientModeFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/HeatingTransientModeFeature.java
new file mode 100644
index 0000000000000..89a555ad62232
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/HeatingTransientModeFeature.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.FeatureAttribute;
+import org.openhab.binding.hive.internal.client.SettableFeatureAttribute;
+import org.openhab.binding.hive.internal.client.Temperature;
+
+import java.time.Duration;
+import java.time.ZonedDateTime;
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class HeatingTransientModeFeature extends TransientModeFeature {
+ private final SettableFeatureAttribute boostTargetTemperature;
+
+ public HeatingTransientModeFeature(
+ final SettableFeatureAttribute boostTargetTemperature,
+ final SettableFeatureAttribute duration,
+ final FeatureAttribute isEnabled,
+ final FeatureAttribute startDatetime,
+ final FeatureAttribute endDatetime
+ ) {
+ super(
+ duration,
+ isEnabled,
+ startDatetime,
+ endDatetime
+ );
+
+ Objects.requireNonNull(boostTargetTemperature);
+
+ this.boostTargetTemperature = boostTargetTemperature;
+ }
+
+ public final SettableFeatureAttribute getBoostTargetTemperatureAttribute() {
+ return this.boostTargetTemperature;
+ }
+
+ public final Temperature getBoostTargetTemperature() {
+ return this.boostTargetTemperature.getDisplayValue();
+ }
+
+ public final void setBoostTargetTemperature(final Temperature boostTargetTemperature) {
+ Objects.requireNonNull(boostTargetTemperature);
+
+ this.boostTargetTemperature.setTargetValue(boostTargetTemperature);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/OnOffDeviceFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/OnOffDeviceFeature.java
new file mode 100644
index 0000000000000..b9702bb0030b5
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/OnOffDeviceFeature.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.OnOffMode;
+import org.openhab.binding.hive.internal.client.SettableFeatureAttribute;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class OnOffDeviceFeature implements Feature {
+ private final SettableFeatureAttribute mode;
+
+ public OnOffDeviceFeature(
+ final SettableFeatureAttribute mode
+ ) {
+ Objects.requireNonNull(mode);
+
+ this.mode = mode;
+ }
+
+ public SettableFeatureAttribute getModeAttribute() {
+ return this.mode;
+ }
+
+ public OnOffMode getMode() {
+ return this.mode.getDisplayValue();
+ }
+
+ public void setMode(final OnOffMode mode) {
+ Objects.requireNonNull(mode);
+
+ this.mode.setTargetValue(mode);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/TemperatureSensorFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/TemperatureSensorFeature.java
new file mode 100644
index 0000000000000..e25404e4d0e89
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/TemperatureSensorFeature.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.FeatureAttribute;
+import org.openhab.binding.hive.internal.client.Temperature;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class TemperatureSensorFeature implements Feature {
+ private final FeatureAttribute temperature;
+
+ public TemperatureSensorFeature(
+ final FeatureAttribute temperature
+ ) {
+ Objects.requireNonNull(temperature);
+
+ this.temperature = temperature;
+ }
+
+ public FeatureAttribute getTemperatureAttribute() {
+ return this.temperature;
+ }
+
+ public Temperature getTemperature() {
+ return this.temperature.getDisplayValue();
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/TransientModeFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/TransientModeFeature.java
new file mode 100644
index 0000000000000..72bdc0afbac12
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/TransientModeFeature.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.FeatureAttribute;
+import org.openhab.binding.hive.internal.client.SettableFeatureAttribute;
+
+import java.time.Duration;
+import java.time.ZonedDateTime;
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public class TransientModeFeature implements Feature {
+ private final SettableFeatureAttribute duration;
+ private final FeatureAttribute isEnabled;
+ private final FeatureAttribute startDatetime;
+ private final FeatureAttribute endDatetime;
+
+ public TransientModeFeature(
+ final SettableFeatureAttribute duration,
+ final FeatureAttribute isEnabled,
+ final FeatureAttribute startDatetime,
+ final FeatureAttribute endDatetime
+ ) {
+ Objects.requireNonNull(duration);
+ Objects.requireNonNull(isEnabled);
+ Objects.requireNonNull(startDatetime);
+ Objects.requireNonNull(endDatetime);
+
+ this.duration = duration;
+ this.isEnabled = isEnabled;
+ this.startDatetime = startDatetime;
+ this.endDatetime = endDatetime;
+ }
+
+ public final SettableFeatureAttribute getDurationAttribute() {
+ return this.duration;
+ }
+
+ public final Duration getDuration() {
+ return this.duration.getDisplayValue();
+ }
+
+ public final void setDuration(final Duration duration) {
+ Objects.requireNonNull(duration);
+
+ this.duration.setTargetValue(duration);
+ }
+
+ public final FeatureAttribute getIsEnabledAttribute() {
+ return this.isEnabled;
+ }
+
+ public final boolean getIsEnabled() {
+ return this.isEnabled.getDisplayValue();
+ }
+
+ public final FeatureAttribute getStartDatetimeAttribute() {
+ return this.startDatetime;
+ }
+
+ public final ZonedDateTime getStartDatetime() {
+ return this.startDatetime.getDisplayValue();
+ }
+
+ public final FeatureAttribute getEndDatetimeAttribute() {
+ return this.endDatetime;
+ }
+
+ public final ZonedDateTime getEndDatetime() {
+ return this.endDatetime.getDisplayValue();
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/WaterHeaterFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/WaterHeaterFeature.java
new file mode 100644
index 0000000000000..dd331eb389595
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/WaterHeaterFeature.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.FeatureAttribute;
+import org.openhab.binding.hive.internal.client.OperatingMode;
+import org.openhab.binding.hive.internal.client.OverrideMode;
+import org.openhab.binding.hive.internal.client.SettableFeatureAttribute;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class WaterHeaterFeature implements Feature {
+ private final SettableFeatureAttribute operatingMode;
+ private final FeatureAttribute isOn;
+ private final SettableFeatureAttribute temporaryOperatingModeOverride;
+
+ public WaterHeaterFeature(
+ final SettableFeatureAttribute operatingMode,
+ final FeatureAttribute isOn,
+ final SettableFeatureAttribute temporaryOperatingModeOverride
+ ) {
+ Objects.requireNonNull(operatingMode);
+ Objects.requireNonNull(isOn);
+ Objects.requireNonNull(temporaryOperatingModeOverride);
+
+ this.operatingMode = operatingMode;
+ this.isOn = isOn;
+ this.temporaryOperatingModeOverride = temporaryOperatingModeOverride;
+ }
+
+ public SettableFeatureAttribute getOperatingModeAttribute() {
+ return this.operatingMode;
+ }
+
+ public OperatingMode getOperatingMode() {
+ return this.operatingMode.getDisplayValue();
+ }
+
+ public void setOperatingMode(final OperatingMode operatingMode) {
+ Objects.requireNonNull(operatingMode);
+
+ this.operatingMode.setTargetValue(operatingMode);
+ }
+
+ public FeatureAttribute getIsOnAttribute() {
+ return this.isOn;
+ }
+
+ public boolean isOn() {
+ return this.isOn.getDisplayValue();
+ }
+
+ public SettableFeatureAttribute getTemporaryOperatingModeOverrideAttribute() {
+ return this.temporaryOperatingModeOverride;
+ }
+
+ public OverrideMode getTemporaryOperatingModeOverride() {
+ return this.temporaryOperatingModeOverride.getDisplayValue();
+ }
+
+ public void setTemporaryOperatingModeOverride(final OverrideMode overrideMode) {
+ Objects.requireNonNull(overrideMode);
+
+ this.temporaryOperatingModeOverride.setTargetValue(overrideMode);
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/ZigbeeDeviceFeature.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/ZigbeeDeviceFeature.java
new file mode 100644
index 0000000000000..7e9d7ec1cc979
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/feature/ZigbeeDeviceFeature.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.feature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hive.internal.client.FeatureAttribute;
+
+import java.util.Objects;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class ZigbeeDeviceFeature implements Feature {
+ private final FeatureAttribute averageLQI;
+ private final FeatureAttribute lastKnownLQI;
+ private final FeatureAttribute averageRSSI;
+ private final FeatureAttribute lastKnownRSSI;
+
+ public ZigbeeDeviceFeature(
+ final FeatureAttribute averageLQI,
+ final FeatureAttribute lastKnownLQI,
+ final FeatureAttribute averageRSSI,
+ final FeatureAttribute lastKnownRSSI
+ ) {
+ Objects.requireNonNull(averageLQI);
+ Objects.requireNonNull(lastKnownLQI);
+ Objects.requireNonNull(averageRSSI);
+ Objects.requireNonNull(lastKnownRSSI);
+
+ this.averageLQI = averageLQI;
+ this.lastKnownLQI = lastKnownLQI;
+ this.averageRSSI = averageRSSI;
+ this.lastKnownRSSI = lastKnownRSSI;
+ }
+
+ public FeatureAttribute getAverageLQIAttribute() {
+ return this.averageLQI;
+ }
+
+ public int getAverageLQI() {
+ return this.averageLQI.getDisplayValue();
+ }
+
+ public FeatureAttribute getLastKnownLQIAttribute() {
+ return this.lastKnownLQI;
+ }
+
+ public int getLastKnownLQI() {
+ return this.lastKnownLQI.getDisplayValue();
+ }
+
+ public FeatureAttribute getAverageRSSIAttribute() {
+ return this.averageRSSI;
+ }
+
+ public int getAverageRSSI() {
+ return this.averageRSSI.getDisplayValue();
+ }
+
+ public FeatureAttribute getLastKnownRSSIAttribute() {
+ return this.lastKnownRSSI;
+ }
+
+ public int getLastKnownRSSI() {
+ return this.lastKnownRSSI.getDisplayValue();
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/DefaultNodeRepository.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/DefaultNodeRepository.java
new file mode 100644
index 0000000000000..e56aaed5eb66e
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/DefaultNodeRepository.java
@@ -0,0 +1,361 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.repository;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.*;
+import org.openhab.binding.hive.internal.client.dto.*;
+import org.openhab.binding.hive.internal.client.exception.HiveApiNotAuthorisedException;
+import org.openhab.binding.hive.internal.client.exception.HiveApiUnknownException;
+import org.openhab.binding.hive.internal.client.exception.HiveClientResponseException;
+import org.openhab.binding.hive.internal.client.feature.*;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.time.Duration;
+import java.util.*;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class DefaultNodeRepository implements NodeRepository {
+ private final HiveApiRequestFactory requestFactory;
+
+ public DefaultNodeRepository(
+ final HiveApiRequestFactory requestFactory
+ ) {
+ Objects.requireNonNull(requestFactory);
+
+ this.requestFactory = requestFactory;
+ }
+
+ private URI getEndpointPathForNode(final NodeId nodeId) {
+ return HiveApiConstants.ENDPOINT_NODE.resolve(nodeId.toString());
+ }
+
+ private BatteryDeviceFeature getBatteryDeviceFeature(
+ final BatteryDeviceV1FeatureDto batteryDeviceV1FeatureDto
+ ) {
+ return new BatteryDeviceFeature(
+ FeatureAttributeFactory.getReadOnlyFromDtoWithAdapter(
+ BatteryLevel::new,
+ batteryDeviceV1FeatureDto.batteryLevel
+ ),
+ FeatureAttributeFactory.getReadOnlyFromDto(batteryDeviceV1FeatureDto.batteryState)
+ );
+ }
+
+ private OnOffDeviceFeature getOnOffDeviceFeature(
+ final OnOffDeviceV1FeatureDto onOffDeviceV1FeatureDto
+ ) {
+ return new OnOffDeviceFeature(
+ FeatureAttributeFactory.getSettableFromDto(onOffDeviceV1FeatureDto.mode)
+ );
+ }
+
+ private HeatingThermostatFeature getHeatingThermostatFeature(
+ final HeatingThermostatV1FeatureDto heatingThermostatV1FeatureDto
+ ) {
+ return new HeatingThermostatFeature(
+ FeatureAttributeFactory.getSettableFromDto(heatingThermostatV1FeatureDto.operatingMode),
+ FeatureAttributeFactory.getReadOnlyFromDto(heatingThermostatV1FeatureDto.operatingState),
+ FeatureAttributeFactory.getSettableFromDtoWithAdapter(
+ Temperature::new,
+ heatingThermostatV1FeatureDto.targetHeatTemperature
+ ),
+ FeatureAttributeFactory.getSettableFromDto(heatingThermostatV1FeatureDto.temporaryOperatingModeOverride)
+ );
+ }
+
+ private TemperatureSensorFeature getTemperatureSensorFeature(
+ final TemperatureSensorV1FeatureDto temperatureSensorV1FeatureDto
+ ) {
+ return new TemperatureSensorFeature(
+ FeatureAttributeFactory.getReadOnlyFromDtoWithAdapter(
+ Temperature::new,
+ temperatureSensorV1FeatureDto.temperature
+ )
+ );
+ }
+
+ private TransientModeFeature getTransientModeFeature(
+ final TransientModeV1FeatureDto transientModeV1FeatureDto
+ ) {
+ return new TransientModeFeature(
+ FeatureAttributeFactory.getSettableFromDtoWithAdapter(
+ Duration::ofSeconds,
+ transientModeV1FeatureDto.duration
+ ),
+ FeatureAttributeFactory.getReadOnlyFromDto(transientModeV1FeatureDto.isEnabled),
+ FeatureAttributeFactory.getReadOnlyFromDto(transientModeV1FeatureDto.startDatetime),
+ FeatureAttributeFactory.getReadOnlyFromDto(transientModeV1FeatureDto.endDatetime)
+ );
+ }
+
+ private HeatingTransientModeFeature getHeatingTransientModeFeature(
+ final TransientModeV1FeatureDto transientModeV1FeatureDto
+ ) {
+ // FIXME: Check the DTO is valid before blindly using it.
+ final Temperature targetHeatTemperature = new Temperature(new BigDecimal(transientModeV1FeatureDto.actions.reportedValue.get(0).value));
+
+ return new HeatingTransientModeFeature(
+ FeatureAttributeFactory.getSettableFromDtoWithAdapter(
+ (val) -> targetHeatTemperature,
+ transientModeV1FeatureDto.actions
+ ),
+ FeatureAttributeFactory.getSettableFromDtoWithAdapter(
+ Duration::ofSeconds,
+ transientModeV1FeatureDto.duration
+ ),
+ FeatureAttributeFactory.getReadOnlyFromDto(transientModeV1FeatureDto.isEnabled),
+ FeatureAttributeFactory.getReadOnlyFromDto(transientModeV1FeatureDto.startDatetime),
+ FeatureAttributeFactory.getReadOnlyFromDto(transientModeV1FeatureDto.endDatetime)
+ );
+ }
+
+ private WaterHeaterFeature getWaterHeaterFeature(
+ final WaterHeaterV1FeatureDto waterHeaterV1FeatureDto
+ ) {
+ return new WaterHeaterFeature(
+ FeatureAttributeFactory.getSettableFromDto(waterHeaterV1FeatureDto.operatingMode),
+ FeatureAttributeFactory.getReadOnlyFromDto(waterHeaterV1FeatureDto.isOn),
+ FeatureAttributeFactory.getSettableFromDto(waterHeaterV1FeatureDto.temporaryOperatingModeOverride)
+ );
+ }
+
+ private ZigbeeDeviceFeature getZigbeeDeviceFeature(
+ final ZigbeeDeviceV1FeatureDto zigbeeDeviceV1FeatureDto
+ ) {
+ return new ZigbeeDeviceFeature(
+ FeatureAttributeFactory.getReadOnlyFromDto(zigbeeDeviceV1FeatureDto.averageLQI),
+ FeatureAttributeFactory.getReadOnlyFromDto(zigbeeDeviceV1FeatureDto.lastKnownLQI),
+ FeatureAttributeFactory.getReadOnlyFromDto(zigbeeDeviceV1FeatureDto.averageRSSI),
+ FeatureAttributeFactory.getReadOnlyFromDto(zigbeeDeviceV1FeatureDto.lastKnownRSSI)
+ );
+ }
+
+ private Set parseNodesDto(
+ final NodesDto nodesDto
+ ) {
+ final Set nodes = new HashSet<>();
+ // For each node
+ for (NodeDto nodeDto : nodesDto.nodes) {
+ final Protocol protocol;
+ if (nodeDto.protocol != null) {
+ protocol = new Protocol(nodeDto.protocol);
+ } else {
+ protocol = Protocol.NONE;
+ }
+
+ final Map, Feature> features = new HashMap<>();
+
+ if (nodeDto.features.battery_device_v1 != null) {
+ features.put(BatteryDeviceFeature.class, getBatteryDeviceFeature(nodeDto.features.battery_device_v1));
+ }
+ if (nodeDto.features.heating_thermostat_v1 != null) {
+ features.put(HeatingThermostatFeature.class, getHeatingThermostatFeature(nodeDto.features.heating_thermostat_v1));
+ }
+ if (nodeDto.features.on_off_device_v1 != null) {
+ features.put(OnOffDeviceFeature.class, getOnOffDeviceFeature(nodeDto.features.on_off_device_v1));
+ }
+ if (nodeDto.features.temperature_sensor_v1 != null) {
+ features.put(TemperatureSensorFeature.class, getTemperatureSensorFeature(nodeDto.features.temperature_sensor_v1));
+ }
+ if (nodeDto.features.transient_mode_v1 != null) {
+ if (nodeDto.features.heating_thermostat_v1 != null) {
+ features.put(HeatingTransientModeFeature.class, getHeatingTransientModeFeature(nodeDto.features.transient_mode_v1));
+ } else {
+ features.put(TransientModeFeature.class, getTransientModeFeature(nodeDto.features.transient_mode_v1));
+ }
+ }
+ if (nodeDto.features.water_heater_v1 != null) {
+ features.put(WaterHeaterFeature.class, getWaterHeaterFeature(nodeDto.features.water_heater_v1));
+ }
+ if (nodeDto.features.zigbee_device_v1 != null) {
+ features.put(ZigbeeDeviceFeature.class, getZigbeeDeviceFeature(nodeDto.features.zigbee_device_v1));
+ }
+
+ nodes.add(new DefaultNode(
+ nodeDto.id,
+ nodeDto.name,
+ nodeDto.nodeType,
+ nodeDto.features.device_management_v1.productType.reportedValue,
+ protocol,
+ nodeDto.parentNodeId,
+ features
+ ));
+ }
+
+ return Collections.unmodifiableSet(nodes);
+ }
+
+ @Override
+ public Set getAllNodes() {
+ /* Send our get nodes request to the Hive API. */
+ final HiveApiResponse response = this.requestFactory.newRequest(HiveApiConstants.ENDPOINT_NODES)
+ .accept(MediaType.API_V6_5_0_JSON)
+ .get();
+
+ if (response.getStatusCode() == 401) {
+ throw new HiveApiNotAuthorisedException();
+ } else if (response.getStatusCode() != 200) {
+ throw new HiveApiUnknownException("Getting nodes failed for an unknown reason");
+ }
+
+ final NodesDto nodesDto = response.getContent(NodesDto.class);
+
+ return parseNodesDto(nodesDto);
+ }
+
+ @Override
+ public @Nullable Node getNode(final NodeId nodeId) {
+ /* Send our get node request to the Hive API. */
+ final HiveApiResponse response = this.requestFactory.newRequest(getEndpointPathForNode(nodeId))
+ .accept(MediaType.API_V6_5_0_JSON)
+ .get();
+
+ if (response.getStatusCode() == 401) {
+ throw new HiveApiNotAuthorisedException();
+ } else if (response.getStatusCode() != 200) {
+ throw new HiveApiUnknownException("Getting nodes failed for an unknown reason");
+ }
+
+ final NodesDto nodesDto = response.getContent(NodesDto.class);
+ final Set nodes = parseNodesDto(nodesDto);
+
+ if (nodes.isEmpty()) {
+ // There is no node with that ID
+ return null;
+ } else if (nodes.size() == 1) {
+ return nodes.iterator().next();
+ } else {
+ // Something has gone wrong.
+ throw new HiveClientResponseException("Got multiple nodes when requesting node by ID!");
+ }
+ }
+
+ @Override
+ public @Nullable Node updateNode(final Node node) {
+ /* Prepare DTO to send Hive API */
+ final NodeDto nodeDto = new NodeDto();
+ nodeDto.features = new FeaturesDto();
+
+ for (final Feature feature : node.getFeatures()) {
+ final Class extends Feature> featureClass = feature.getClass();
+
+ if (featureClass.equals(OnOffDeviceFeature.class)) {
+ final OnOffDeviceFeature onOffDeviceFeature = (OnOffDeviceFeature) feature;
+
+ if (onOffDeviceFeature.getModeAttribute().getTargetValue() != null) {
+ nodeDto.features.on_off_device_v1 = new OnOffDeviceV1FeatureDto();
+
+ nodeDto.features.on_off_device_v1.mode = new FeatureAttributeDto<>();
+ nodeDto.features.on_off_device_v1.mode.targetValue = onOffDeviceFeature.getModeAttribute().getTargetValue();
+ }
+ } else if (featureClass.equals(HeatingThermostatFeature.class)) {
+ final HeatingThermostatFeature heatingThermostatFeature = (HeatingThermostatFeature) feature;
+
+ if (heatingThermostatFeature.getOperatingModeAttribute().getTargetValue() != null
+ || heatingThermostatFeature.getTargetHeatTemperatureAttribute().getTargetValue() != null
+ || heatingThermostatFeature.getTemporaryOperatingModeOverrideAttribute().getTargetValue() != null
+ ) {
+ nodeDto.features.heating_thermostat_v1 = new HeatingThermostatV1FeatureDto();
+
+ if (heatingThermostatFeature.getOperatingModeAttribute().getTargetValue() != null) {
+ nodeDto.features.heating_thermostat_v1.operatingMode = new FeatureAttributeDto<>();
+ nodeDto.features.heating_thermostat_v1.operatingMode.targetValue = heatingThermostatFeature.getOperatingModeAttribute().getTargetValue();
+ }
+
+ if (heatingThermostatFeature.getTargetHeatTemperatureAttribute().getTargetValue() != null) {
+ // FIXME: Check temperature units.
+ nodeDto.features.heating_thermostat_v1.targetHeatTemperature = new FeatureAttributeDto<>();
+ nodeDto.features.heating_thermostat_v1.targetHeatTemperature.targetValue = heatingThermostatFeature.getTargetHeatTemperatureAttribute().getTargetValue().getValue();
+ }
+
+ if (heatingThermostatFeature.getTemporaryOperatingModeOverrideAttribute().getTargetValue() != null) {
+ nodeDto.features.heating_thermostat_v1.temporaryOperatingModeOverride = new FeatureAttributeDto<>();
+ nodeDto.features.heating_thermostat_v1.temporaryOperatingModeOverride.targetValue = heatingThermostatFeature.getTemporaryOperatingModeOverrideAttribute().getTargetValue();
+ }
+ }
+ } else if (featureClass.equals(TransientModeFeature.class) || featureClass.equals(HeatingTransientModeFeature.class)) {
+ final TransientModeFeature transientModeFeature = (TransientModeFeature) feature;
+
+ if (transientModeFeature.getDurationAttribute().getTargetValue() != null) {
+ nodeDto.features.transient_mode_v1 = new TransientModeV1FeatureDto();
+
+ nodeDto.features.transient_mode_v1.duration = new FeatureAttributeDto<>();
+ nodeDto.features.transient_mode_v1.duration.targetValue = Math.max(1, transientModeFeature.getDurationAttribute().getTargetValue().getSeconds());
+ }
+
+ if (featureClass.equals(HeatingTransientModeFeature.class)) {
+ final HeatingTransientModeFeature heatingTransientModeFeature = (HeatingTransientModeFeature) feature;
+
+ if (heatingTransientModeFeature.getBoostTargetTemperatureAttribute().getTargetValue() != null) {
+ if (nodeDto.features.transient_mode_v1 == null) {
+ nodeDto.features.transient_mode_v1 = new TransientModeV1FeatureDto();
+ }
+
+ final ActionDto actionDto = new ActionDto();
+ actionDto.actionType = ActionType.GENERIC;
+ actionDto.featureType = FeatureType.HEATING_THERMOSTAT_V1;
+ actionDto.attribute = AttributeName.ATTRIBUTE_NAME_TARGET_HEAT_TEMPERATURE;
+ actionDto.value = heatingTransientModeFeature.getBoostTargetTemperatureAttribute().getTargetValue().getValue().toString();
+
+ nodeDto.features.transient_mode_v1.actions = new FeatureAttributeDto<>();
+ nodeDto.features.transient_mode_v1.actions.targetValue = Collections.singletonList(actionDto);
+ }
+ }
+ } else if (featureClass.equals(WaterHeaterFeature.class)) {
+ final WaterHeaterFeature waterHeaterFeature = (WaterHeaterFeature) feature;
+
+ if (waterHeaterFeature.getOperatingModeAttribute().getTargetValue() != null) {
+ nodeDto.features.water_heater_v1 = new WaterHeaterV1FeatureDto();
+
+ nodeDto.features.water_heater_v1.operatingMode.targetValue = waterHeaterFeature.getOperatingModeAttribute().getTargetValue();
+ }
+
+ if (waterHeaterFeature.getTemporaryOperatingModeOverrideAttribute().getTargetValue() != null) {
+ nodeDto.features.water_heater_v1.temporaryOperatingModeOverride = new FeatureAttributeDto<>();
+ nodeDto.features.water_heater_v1.temporaryOperatingModeOverride.targetValue = waterHeaterFeature.getTemporaryOperatingModeOverrideAttribute().getTargetValue();
+ }
+ }
+ }
+
+ final NodesDto nodesDto = new NodesDto();
+ nodesDto.nodes = Collections.singletonList(nodeDto);
+
+ final HiveApiResponse response = this.requestFactory.newRequest(getEndpointPathForNode(node.getId()))
+ .accept(MediaType.API_V6_5_0_JSON)
+ .put(nodesDto);
+
+ if (response.getStatusCode() == 401) {
+ throw new HiveApiNotAuthorisedException();
+ } else if (response.getStatusCode() != 200) {
+ throw new HiveApiUnknownException("Updating node failed for an unknown reason. Response code: " + response.getStatusCode());
+ }
+
+ final NodesDto responseNodesDto = response.getContent(NodesDto.class);
+ final Set responseNodes = parseNodesDto(responseNodesDto);
+
+ if (responseNodes.size() == 1) {
+ return responseNodes.iterator().next();
+ } else {
+ // Something has gone wrong.
+ throw new HiveClientResponseException("The parsed response is malformed.");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/DefaultSessionRepository.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/DefaultSessionRepository.java
new file mode 100644
index 0000000000000..881d466fcbed4
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/DefaultSessionRepository.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.repository;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.*;
+import org.openhab.binding.hive.internal.client.dto.SessionDto;
+import org.openhab.binding.hive.internal.client.dto.SessionsDto;
+import org.openhab.binding.hive.internal.client.exception.HiveApiAuthenticationException;
+import org.openhab.binding.hive.internal.client.exception.HiveApiNotAuthorisedException;
+import org.openhab.binding.hive.internal.client.exception.HiveApiUnknownException;
+import org.openhab.binding.hive.internal.client.exception.HiveClientUnknownException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * Default implementation of {@link SessionRepository}.
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public final class DefaultSessionRepository implements SessionRepository {
+ private final Logger logger = LoggerFactory.getLogger(DefaultSessionRepository.class);
+
+ private final HiveApiRequestFactory requestFactory;
+
+ public DefaultSessionRepository(
+ final HiveApiRequestFactory requestFactory
+ ) {
+ Objects.requireNonNull(requestFactory);
+
+ this.requestFactory = requestFactory;
+ }
+
+ private URI getEndpointPathForSession(final SessionId sessionId) {
+ return HiveApiConstants.ENDPOINT_SESSION.resolve(sessionId.toString());
+ }
+
+ @Override
+ public Session createSession(final Username username, final Password password) {
+ /* Build our request entity with our user credentials */
+ final SessionDto credentials = new SessionDto();
+ credentials.username = username;
+ credentials.password = password;
+
+ final SessionsDto requestEntity = new SessionsDto();
+ requestEntity.sessions = Collections.singletonList(credentials);
+
+ /* Send our new session request to the Hive API. */
+ final HiveApiResponse response = this.requestFactory.newRequest(HiveApiConstants.ENDPOINT_SESSIONS)
+ .accept(MediaType.API_V6_5_0_JSON)
+ .post(requestEntity);
+
+ if (response.getStatusCode() == 400) {
+ throw new HiveApiAuthenticationException(
+ "Creating a new session failed because an incorrect username or password was provided"
+ );
+ } else if (response.getStatusCode() != 200) {
+ throw new HiveApiUnknownException("Creating a new session failed with response code: " + response.getStatusCode());
+ }
+
+ /* Try to convert DTO into a domain object */
+ final SessionsDto responseEntity = response.getContent(SessionsDto.class);
+ try {
+ final SessionDto sessionDto = responseEntity.sessions.get(0);
+
+ logger.debug("Created new Hive API session with sessionId: {}", sessionDto.sessionId);
+
+ return new Session(
+ sessionDto.sessionId,
+ sessionDto.userId
+ );
+ } catch (NullPointerException | IllegalArgumentException | IndexOutOfBoundsException ex) {
+ throw new HiveClientUnknownException("Something went wrong parsing the Hive API response.", ex);
+ }
+ }
+
+ @Override
+ public void deleteSession(final Session session) {
+ /* Send our delete session request to the Hive API. */
+ final HiveApiResponse response = this.requestFactory.newRequest(getEndpointPathForSession(session.getSessionId()))
+ .accept(MediaType.API_V6_5_0_JSON)
+ .delete();
+
+ if (response.getStatusCode() == 401) {
+ throw new HiveApiNotAuthorisedException();
+ } else if (response.getStatusCode() != 200) {
+ throw new HiveApiUnknownException("Deleting session failed with response code: " + response.getStatusCode());
+ }
+ }
+
+ @Override
+ public boolean isValidSession(final @Nullable Session session) {
+ if (session == null) {
+ return false;
+ }
+
+ /* Try to get our own session from the Hive API. */
+ final HiveApiResponse response = this.requestFactory.newRequest(getEndpointPathForSession(session.getSessionId()))
+ .accept(MediaType.API_V6_5_0_JSON)
+ .get();
+
+ if (response.getStatusCode() == 200) {
+ // If we succeeded the session is still valid.
+ return true;
+ } else if (response.getStatusCode() == 401 || response.getStatusCode() == 403) {
+ // If we failed our session has expired (or was never valid).
+ return false;
+ }
+
+ throw new HiveApiUnknownException("Checking session failed for an unknown reason");
+ }
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/NodeRepository.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/NodeRepository.java
new file mode 100644
index 0000000000000..009d661838c93
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/NodeRepository.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.repository;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.Node;
+import org.openhab.binding.hive.internal.client.NodeId;
+
+import java.util.Set;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface NodeRepository {
+ Set getAllNodes();
+
+ @Nullable Node getNode(NodeId nodeId);
+ @Nullable Node updateNode(Node node);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/SessionRepository.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/SessionRepository.java
new file mode 100644
index 0000000000000..fa1dc8bc5752c
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/client/repository/SessionRepository.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.client.repository;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hive.internal.client.Password;
+import org.openhab.binding.hive.internal.client.Session;
+import org.openhab.binding.hive.internal.client.Username;
+import org.openhab.binding.hive.internal.client.exception.HiveApiAuthenticationException;
+import org.openhab.binding.hive.internal.client.exception.HiveApiNotAuthorisedException;
+import org.openhab.binding.hive.internal.client.exception.HiveApiUnknownException;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public interface SessionRepository {
+ /**
+ * Open a new session using a username and password.
+ *
+ * @param username
+ * The user's Hive username (email).
+ *
+ * @param password
+ * The user's Hive password.
+ *
+ * @return
+ * The the newly created Session.
+ *
+ * @throws HiveApiAuthenticationException
+ * If the provided username and password are not valid.
+ *
+ * @throws HiveApiUnknownException
+ * If the call to the Hive API fails for an unknown reason.
+ *
+ * @throws IllegalStateException
+ * If the call the the Hive API was a success but we do not understand
+ * the response.
+ */
+ Session createSession(Username username, Password password);
+
+ /**
+ * Delete a session with a given ID.
+ *
+ *
+ * N.B. This method does not seem to work how I expect.
+ * The API returns 200 but the session still works.
+ *
+ *
+ * @param session
+ * The session to delete.
+ *
+ * @throws HiveApiNotAuthorisedException
+ * If you are not authorised to delete the provided session.
+ *
+ * @throws HiveApiUnknownException
+ * If the call to the Hive API fails for an unknown reason.
+ */
+ void deleteSession(Session session);
+
+ /**
+ * Checks if a session is valid.
+ *
+ * @param session
+ * The session to check.
+ *
+ * @return
+ * {@code true} if the session is valid or {@code false} if it is not.
+ *
+ * @throws HiveApiUnknownException
+ * If the call to the Hive API fails for an unknown reason.
+ */
+ boolean isValidSession(@Nullable Session session);
+}
diff --git a/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/discovery/HiveDiscoveryService.java b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/discovery/HiveDiscoveryService.java
new file mode 100644
index 0000000000000..b9fa4f4982c59
--- /dev/null
+++ b/bundles/org.openhab.binding.hive/src/main/java/org/openhab/binding/hive/internal/discovery/HiveDiscoveryService.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2010-2020 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.hive.internal.discovery;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService;
+import org.eclipse.smarthome.config.discovery.DiscoveryResult;
+import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.openhab.binding.hive.internal.HiveBindingConstants;
+import org.openhab.binding.hive.internal.client.Node;
+import org.openhab.binding.hive.internal.client.NodeId;
+import org.openhab.binding.hive.internal.client.ProductType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Instant;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ *
+ *
+ * @author Ross Brown - Initial contribution
+ */
+@NonNullByDefault
+public class HiveDiscoveryService extends AbstractDiscoveryService {
+ private static final String NODE_ID = "nodeId";
+
+ /**
+ * Number of seconds before discovery should timeout.
+ *
+ *
+ * N.B. Set to 0 because I want to manually mark when discovery is
+ * finished.
+ *
+ */
+ private static final int DISCOVERY_TIMEOUT_SECONDS = 0;
+
+ private final Logger logger = LoggerFactory.getLogger(HiveDiscoveryService.class);
+
+ private final ThingUID bridgeUid;
+ private final AtomicReference