added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.opensprinkler/.classpath
Normal file
32
bundles/org.openhab.binding.opensprinkler/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.opensprinkler/.project
Normal file
23
bundles/org.openhab.binding.opensprinkler/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.opensprinkler</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
20
bundles/org.openhab.binding.opensprinkler/NOTICE
Normal file
20
bundles/org.openhab.binding.opensprinkler/NOTICE
Normal file
@@ -0,0 +1,20 @@
|
||||
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
|
||||
|
||||
== Third-party Content
|
||||
|
||||
pi4j
|
||||
* License: LGPL v3.0 License
|
||||
* Project: http://pi4j.com
|
||||
* Source: https://github.com/Pi4J/pi4j
|
||||
115
bundles/org.openhab.binding.opensprinkler/README.md
Normal file
115
bundles/org.openhab.binding.opensprinkler/README.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# OpenSprinkler Binding
|
||||
|
||||
This binding allows allows basic control of the OpenSprinkler devices.
|
||||
Stations can be controlled to be turned on or off and rain sensor state can be read.
|
||||
|
||||
## Supported Bridges
|
||||
|
||||
* HTTP (`http`) - The http bridge allows to communicate with an OpenSprinkler device through the network
|
||||
|
||||
## Supported Things
|
||||
|
||||
* OpenSprinkler Station (`station`) - to control a single station of a device, e.g. to turn it on or off
|
||||
* OpenSprinkler Device (`device`) - for getting device-specific infos, e.g. if rain was detected
|
||||
|
||||
## Discovery
|
||||
|
||||
OpenSprinkler devices can be manually discovered by sending a request to every IP on the network.
|
||||
Discovery needs to be run manually as this is a brute force method of finding devices that can saturate network or device available bandwidth.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
OpenSprinkler using the HTTP interface
|
||||
|
||||
```
|
||||
Bridge opensprinkler:http:http [hostname="127.0.0.1", port=80, password="opendoor", refresh=60] {
|
||||
Thing station 01 [stationIndex=1]
|
||||
}
|
||||
```
|
||||
|
||||
- hostname: Hostname or IP address of the OpenSprinkler HTTP API.
|
||||
- port: Port the OpenSprinkler device is listening on. Usually 80.
|
||||
- password: Admin password of the API. Factory default is: opendoor
|
||||
- refresh: Number of seconds in between refreshing the Thing state with the API.
|
||||
- basicUsername: (optional) Only needed when the OpenSprinkler device is behind a basic auth enforcing reverse proxy.
|
||||
- basicPassword: (optional) Only needed when the OpenSprinkler device is behind a basic auth enforcing reverse proxy.
|
||||
|
||||
### Station Thing Configuration
|
||||
|
||||
The `station` thing can be used with both bridge and has the following configuration properties:
|
||||
|
||||
- stationIndex: The index of the station to communicate with, starting with 0 for the first station
|
||||
|
||||
## Channels
|
||||
|
||||
The following channel is supported by the `station` thing.
|
||||
|
||||
| Channel Type ID | Item Type | | Description |
|
||||
|--------------------|-------------|----|----------------------------------------------------------|
|
||||
| stationState | Switch | RW | This channel indicates whether station 01 is on or off. |
|
||||
| remainingWaterTime | Number:Time | R | The time the station remains to be open. |
|
||||
| nextDuration | Number:Time | RW | A configuration item, which time, if linked, will be |
|
||||
| | | | used as the time the station will be kept open when |
|
||||
| | | | switched on. It is advised to add persistence for items |
|
||||
| | | | linked to this channel, the binding does not persist |
|
||||
| | | | values of it. |
|
||||
| queued | Switch | RW | Indicates that the station is queued to be turned on. |
|
||||
| | | | The channel cannot be turned on, only turning it off is |
|
||||
| | | | supported (which removes the station from the queue). |
|
||||
|
||||
When using the `nextDuration` channel, it is advised to setup persistence (e.g. MapDB) in order to persist the value through restarts.
|
||||
|
||||
The following is supported by the `device` thing, but only when connected using the http interface.
|
||||
|
||||
| Channel Type ID | Item Type | | Description |
|
||||
|-----------------|------------------------|----|------------------------------------------------------------------------------------|
|
||||
| rainsensor | Switch | RO | This channel indicates whether rain is detected by the device or not. |
|
||||
| currentDraw | Number:ElectricCurrent | RO | Shows the current draw of the device. If the device does not have sensors |
|
||||
| | | | for this metric, the channel will not be available. |
|
||||
| waterlevel | Number:Dimensionless | RO | This channel shows the current water level in percent (0-250%). The water level is |
|
||||
| | | | calculated based on the weather and influences the duration of the water programs. |
|
||||
|
||||
## Example
|
||||
|
||||
demo.Things:
|
||||
|
||||
```
|
||||
Bridge opensprinkler:http:http [hostname="127.0.0.1", port=81, password="opendoor"] {
|
||||
Thing station 01 [stationIndex=0]
|
||||
Thing station 02 [stationIndex=1]
|
||||
Thing station 03 [stationIndex=2]
|
||||
Thing station 04 [stationIndex=3]
|
||||
Thing station 05 [stationIndex=4]
|
||||
Thing station 06 [stationIndex=5]
|
||||
Thing device device
|
||||
}
|
||||
```
|
||||
|
||||
demo.items:
|
||||
|
||||
```
|
||||
Group stations
|
||||
Switch Station01 (stations) { channel="opensprinkler:station:http:01:stationState" }
|
||||
Number:Time Station01RaminingTime { channel="opensprinkler:station:http:01:remainingWaterTime" }
|
||||
Switch Station02 (stations) { channel="opensprinkler:station:http:02:stationState" }
|
||||
Switch Station03 (stations) { channel="opensprinkler:station:http:03:stationState" }
|
||||
Number:Time Station03NextDuration { channel="opensprinkler:station:http:03:nextDuration" }
|
||||
Switch Station04 (stations) { channel="opensprinkler:station:http:04:stationState" }
|
||||
Switch Station05 (stations) { channel="opensprinkler:station:http:05:stationState" }
|
||||
Switch Station06 (stations) { channel="opensprinkler:station:http:06:stationState" }
|
||||
|
||||
Switch RainSensor { channel="opensprinkler:device:http:device:rainsensor" }
|
||||
Number:ElectricCurrent CurrentDraw {channel="opensprinkler:device:http:device:currentDraw"}
|
||||
```
|
||||
|
||||
demo.sitemap:
|
||||
|
||||
```
|
||||
sitemap demo label="Main Menu"
|
||||
{
|
||||
Frame {
|
||||
Switch item=Station01
|
||||
Selection item=Station03NextDuration mappings=[300="5 min", 600="10 min"]
|
||||
}
|
||||
}
|
||||
```
|
||||
16
bundles/org.openhab.binding.opensprinkler/pom.xml
Normal file
16
bundles/org.openhab.binding.opensprinkler/pom.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.opensprinkler</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: OpenSprinkler Binding</name>
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.opensprinkler-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-opensprinkler" description="OpenSprinkler Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.opensprinkler/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
* @author Florian Schmidt - Split channels to their own things
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenSprinklerBindingConstants {
|
||||
public static final String BINDING_ID = "opensprinkler";
|
||||
|
||||
// List of all Thing ids
|
||||
public static final String HTTP_BRIDGE = "http";
|
||||
public static final String PI_BRIDGE = "pi";
|
||||
public static final String STATION_THING = "station";
|
||||
public static final String DEVICE_THING = "device";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID OPENSPRINKLER_HTTP_BRIDGE = new ThingTypeUID(BINDING_ID, HTTP_BRIDGE);
|
||||
public static final ThingTypeUID OPENSPRINKLER_STATION = new ThingTypeUID(BINDING_ID, STATION_THING);
|
||||
public static final ThingTypeUID OPENSPRINKLER_DEVICE = new ThingTypeUID(BINDING_ID, DEVICE_THING);
|
||||
|
||||
public static final int DEFAULT_WAIT_BEFORE_INITIAL_REFRESH = 30;
|
||||
public static final int DEFAULT_REFRESH_RATE = 60;
|
||||
public static final int DISCOVERY_THREAD_POOL_SIZE = 15;
|
||||
public static final boolean DISCOVERY_DEFAULT_AUTO_DISCOVER = false;
|
||||
public static final int DISCOVERY_DEFAULT_TIMEOUT_RATE = 500;
|
||||
public static final int DISCOVERY_DEFAULT_IP_TIMEOUT_RATE = 750;
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String SENSOR_RAIN = "rainsensor";
|
||||
public static final String SENSOR_WATERLEVEL = "waterlevel";
|
||||
public static final String SENSOR_CURRENT_DRAW = "currentDraw";
|
||||
public static final String STATION_STATE = "stationState";
|
||||
public static final String STATION_QUEUED = "queued";
|
||||
public static final String REMAINING_WATER_TIME = "remainingWaterTime";
|
||||
public static final String NEXT_DURATION = "nextDuration";
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiFactory;
|
||||
import org.openhab.binding.opensprinkler.internal.handler.OpenSprinklerDeviceHandler;
|
||||
import org.openhab.binding.opensprinkler.internal.handler.OpenSprinklerHttpBridgeHandler;
|
||||
import org.openhab.binding.opensprinkler.internal.handler.OpenSprinklerStationHandler;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
* @author Florian Schmidt - Split channels to their own things
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.opensprinkler")
|
||||
public class OpenSprinklerHandlerFactory extends BaseThingHandlerFactory {
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(
|
||||
Arrays.asList(OPENSPRINKLER_HTTP_BRIDGE, OPENSPRINKLER_STATION, OPENSPRINKLER_DEVICE));
|
||||
private OpenSprinklerApiFactory apiFactory;
|
||||
|
||||
@Activate
|
||||
public OpenSprinklerHandlerFactory(@Reference OpenSprinklerApiFactory apiFactory) {
|
||||
this.apiFactory = apiFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(OPENSPRINKLER_HTTP_BRIDGE)) {
|
||||
return new OpenSprinklerHttpBridgeHandler((Bridge) thing, this.apiFactory);
|
||||
} else if (thingTypeUID.equals(OPENSPRINKLER_STATION)) {
|
||||
return new OpenSprinklerStationHandler(thing);
|
||||
} else if (thingTypeUID.equals(OPENSPRINKLER_DEVICE)) {
|
||||
return new OpenSprinklerDeviceHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.model.NoCurrentDrawSensorException;
|
||||
import org.openhab.binding.opensprinkler.internal.model.StationProgram;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerApi} interface defines the functions which are
|
||||
* controllable on the OpenSprinkler API interface.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public interface OpenSprinklerApi {
|
||||
/**
|
||||
* Whether the device entered manual mode and accepts API requests to control the stations.
|
||||
*
|
||||
* @return True if this API interface is connected to the Open Sprinkler API. False otherwise.
|
||||
*/
|
||||
public abstract boolean isManualModeEnabled();
|
||||
|
||||
/**
|
||||
* Enters the "manual" mode of the device so that API requests are accepted.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void enterManualMode() throws CommunicationApiException;
|
||||
|
||||
/**
|
||||
* Disables the manual mode, if it is enabled.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void leaveManualMode() throws CommunicationApiException;
|
||||
|
||||
/**
|
||||
* Starts a station on the OpenSprinkler device for the specified duration.
|
||||
*
|
||||
* @param station Index of the station to open starting at 0.
|
||||
* @param duration The duration in seconds for how long the station should be turned on.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void openStation(int station, BigDecimal duration)
|
||||
throws CommunicationApiException, GeneralApiException;
|
||||
|
||||
/**
|
||||
* Closes a station on the OpenSprinkler device.
|
||||
*
|
||||
* @param station Index of the station to open starting at 0.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void closeStation(int station) throws CommunicationApiException, GeneralApiException;
|
||||
|
||||
/**
|
||||
* Returns the state of a station on the OpenSprinkler device.
|
||||
*
|
||||
* @param station Index of the station to open starting at 0.
|
||||
* @return True if the station is open, false if it is closed or cannot determine.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract boolean isStationOpen(int station) throws GeneralApiException, CommunicationApiException;
|
||||
|
||||
/**
|
||||
* Returns the current program data of the requested station.
|
||||
*
|
||||
* @param station Index of the station to request data from
|
||||
* @return StationProgram
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract StationProgram retrieveProgram(int station) throws CommunicationApiException;
|
||||
|
||||
/**
|
||||
* Returns the state of rain detection on the OpenSprinkler device.
|
||||
*
|
||||
* @return True if rain is detected, false if not or cannot determine.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract boolean isRainDetected() throws CommunicationApiException;
|
||||
|
||||
/**
|
||||
* Returns the current draw of all connected zones of the OpenSprinkler device in milliamperes.
|
||||
*
|
||||
* @return current draw in milliamperes
|
||||
* @throws CommunicationApiException
|
||||
* @throws
|
||||
*/
|
||||
public abstract int currentDraw() throws CommunicationApiException, NoCurrentDrawSensorException;
|
||||
|
||||
/**
|
||||
* Returns the water level in %.
|
||||
*
|
||||
* @return waterLevel in %
|
||||
* @throws CommunicationApiException
|
||||
* @throws
|
||||
*/
|
||||
public abstract int waterLevel() throws CommunicationApiException;
|
||||
|
||||
/**
|
||||
* Returns the number of total stations that are controllable from the OpenSprinkler
|
||||
* device.
|
||||
*
|
||||
* @return Number of stations as an int.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract int getNumberOfStations() throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the firmware version number.
|
||||
*
|
||||
* @return The firmware version of the OpenSprinkler device as an int.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract int getFirmwareVersion() throws CommunicationApiException;
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerApiContents} class defines common constants, which are
|
||||
* used across OpenSprinkler API classes.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenSprinklerApiConstants {
|
||||
public static final String HTTP_REQUEST_URL_PREFIX = "http://";
|
||||
public static final String HTTPS_REQUEST_URL_PREFIX = "https://";
|
||||
|
||||
public static final String DEFAULT_ADMIN_PASSWORD = "opendoor";
|
||||
public static final int DEFAULT_API_PORT = 80;
|
||||
public static final int DEFAULT_STATION_COUNT = 8;
|
||||
|
||||
public static final String CMD_ENABLE_MANUAL_MODE = "mm=1";
|
||||
public static final String CMD_DISABLE_MANUAL_MODE = "mm=0";
|
||||
public static final String CMD_PASSWORD = "pw=";
|
||||
public static final String CMD_STATION = "sid=";
|
||||
public static final String CMD_STATION_ENABLE = "en=1";
|
||||
public static final String CMD_STATION_DISABLE = "en=0";
|
||||
|
||||
public static final String CMD_STATUS_INFO = "jc";
|
||||
public static final String CMD_OPTIONS_INFO = "jo";
|
||||
public static final String CMD_STATION_INFO = "js";
|
||||
public static final String CMD_STATION_CONTROL = "cm";
|
||||
|
||||
public static final String JSON_OPTION_FIRMWARE_VERSION = "fwv";
|
||||
public static final String JSON_OPTION_RAINSENSOR = "rs";
|
||||
public static final String JSON_OPTION_STATION = "sn";
|
||||
public static final String JSON_OPTION_STATION_COUNT = "nstations";
|
||||
|
||||
public static final String JSON_OPTION_RESULT = "result";
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterfaceConfig;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerApiFactory} class is used for creating instances of
|
||||
* the OpenSprinkler API classes to interact with the OpenSprinklers HTTP or
|
||||
* GPIO API's.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
* @author Florian Schmidt - Refactoring
|
||||
*/
|
||||
@Component(service = OpenSprinklerApiFactory.class)
|
||||
public class OpenSprinklerApiFactory {
|
||||
|
||||
private @NonNull HttpClient httpClient;
|
||||
|
||||
@Activate
|
||||
public OpenSprinklerApiFactory(@Reference HttpClientFactory httpClientFactory) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to determine what version of the API is in use at the
|
||||
* OpenSprinkler API and return the proper class for control of the device.
|
||||
*
|
||||
* @param hostname Hostname or IP address as a String of the OpenSprinkler device.
|
||||
* @param port The port number the OpenSprinkler API is listening on.
|
||||
* @param password Admin password for the OpenSprinkler device.
|
||||
* @param basicUsername Used when basic auth is required
|
||||
* @param basicPassword Used when basic auth is required
|
||||
* @return OpenSprinkler HTTP API class for control of the device.
|
||||
* @throws Exception
|
||||
*/
|
||||
public OpenSprinklerApi getHttpApi(OpenSprinklerHttpInterfaceConfig config)
|
||||
throws CommunicationApiException, GeneralApiException {
|
||||
int version = -1;
|
||||
|
||||
OpenSprinklerApi lowestSupportedApi = new OpenSprinklerHttpApiV100(this.httpClient, config);
|
||||
try {
|
||||
version = lowestSupportedApi.getFirmwareVersion();
|
||||
} catch (CommunicationApiException exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
if (version >= 210 && version < 213) {
|
||||
return new OpenSprinklerHttpApiV210(this.httpClient, config);
|
||||
} else if (version >= 213) {
|
||||
return new OpenSprinklerHttpApiV213(this.httpClient, config);
|
||||
} else {
|
||||
/* Need to make sure we have an older OpenSprinkler device by checking the first station. */
|
||||
try {
|
||||
lowestSupportedApi.isStationOpen(0);
|
||||
} catch (GeneralApiException | CommunicationApiException exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: "
|
||||
+ exp.getMessage());
|
||||
}
|
||||
|
||||
return lowestSupportedApi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.api;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
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.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterfaceConfig;
|
||||
import org.openhab.binding.opensprinkler.internal.model.NoCurrentDrawSensorException;
|
||||
import org.openhab.binding.opensprinkler.internal.model.StationProgram;
|
||||
import org.openhab.binding.opensprinkler.internal.util.Parse;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerHttpApiV100} class is used for communicating with the
|
||||
* OpenSprinkler API for firmware versions less than 2.1.0
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
* @author Florian Schmidt - Allow https URLs and basic auth
|
||||
*/
|
||||
class OpenSprinklerHttpApiV100 implements OpenSprinklerApi {
|
||||
protected final String hostname;
|
||||
protected final int port;
|
||||
protected final String password;
|
||||
protected final String basicUsername;
|
||||
protected final String basicPassword;
|
||||
|
||||
protected int firmwareVersion = -1;
|
||||
protected int numberOfStations = DEFAULT_STATION_COUNT;
|
||||
|
||||
protected boolean isInManualMode = false;
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
protected HttpRequestSender http;
|
||||
|
||||
/**
|
||||
* Constructor for the OpenSprinkler API class to create a connection to the
|
||||
* OpenSprinkler device for control and obtaining status info.
|
||||
*
|
||||
* @param hostname Hostname or IP address as a String of the OpenSprinkler
|
||||
* device.
|
||||
* @param port The port number the OpenSprinkler API is listening on.
|
||||
* @param password Admin password for the OpenSprinkler device.
|
||||
* @param basicUsername only needed if basic auth is required
|
||||
* @param basicPassword only needed if basic auth is required
|
||||
* @throws Exception
|
||||
*/
|
||||
OpenSprinklerHttpApiV100(final HttpClient httpClient, final OpenSprinklerHttpInterfaceConfig config)
|
||||
throws GeneralApiException {
|
||||
if (config.hostname == null) {
|
||||
throw new GeneralApiException("The given url is null.");
|
||||
}
|
||||
if (config.port < 1 || config.port > 65535) {
|
||||
throw new GeneralApiException("The given port is invalid.");
|
||||
}
|
||||
if (config.password == null) {
|
||||
throw new GeneralApiException("The given password is null.");
|
||||
}
|
||||
|
||||
if (config.hostname.startsWith(HTTP_REQUEST_URL_PREFIX)
|
||||
|| config.hostname.startsWith(HTTPS_REQUEST_URL_PREFIX)) {
|
||||
this.hostname = config.hostname;
|
||||
} else {
|
||||
this.hostname = HTTP_REQUEST_URL_PREFIX + config.hostname;
|
||||
}
|
||||
this.port = config.port;
|
||||
this.password = config.password;
|
||||
this.basicUsername = config.basicUsername;
|
||||
this.basicPassword = config.basicPassword;
|
||||
this.http = new HttpRequestSender(httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isManualModeEnabled() {
|
||||
return isInManualMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterManualMode() throws CommunicationApiException {
|
||||
try {
|
||||
http.sendHttpGet(getBaseUrl(), getRequestRequiredOptions() + "&" + CMD_ENABLE_MANUAL_MODE);
|
||||
} catch (Exception exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
this.firmwareVersion = getFirmwareVersion();
|
||||
this.numberOfStations = getNumberOfStations();
|
||||
|
||||
isInManualMode = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveManualMode() throws CommunicationApiException {
|
||||
isInManualMode = false;
|
||||
|
||||
try {
|
||||
http.sendHttpGet(getBaseUrl(), getRequestRequiredOptions() + "&" + CMD_DISABLE_MANUAL_MODE);
|
||||
} catch (Exception exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openStation(int station, BigDecimal duration) throws CommunicationApiException, GeneralApiException {
|
||||
if (station < 0 || station >= numberOfStations) {
|
||||
throw new GeneralApiException("This OpenSprinkler device only has " + this.numberOfStations
|
||||
+ " but station " + station + " was requested to be opened.");
|
||||
}
|
||||
|
||||
try {
|
||||
http.sendHttpGet(getBaseUrl() + "sn" + station + "=1&t=" + duration, null);
|
||||
} catch (Exception exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeStation(int station) throws CommunicationApiException, GeneralApiException {
|
||||
if (station < 0 || station >= numberOfStations) {
|
||||
throw new GeneralApiException("This OpenSprinkler device only has " + this.numberOfStations
|
||||
+ " but station " + station + " was requested to be closed.");
|
||||
}
|
||||
|
||||
http.sendHttpGet(getBaseUrl() + "sn" + station + "=0", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStationOpen(int station) throws GeneralApiException, CommunicationApiException {
|
||||
String returnContent;
|
||||
|
||||
if (station < 0 || station >= numberOfStations) {
|
||||
throw new GeneralApiException("This OpenSprinkler device only has " + this.numberOfStations
|
||||
+ " but station " + station + " was requested for a status update.");
|
||||
}
|
||||
|
||||
try {
|
||||
returnContent = http.sendHttpGet(getBaseUrl() + "sn" + station, null);
|
||||
} catch (Exception exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
return returnContent != null && returnContent.equals("1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRainDetected() throws CommunicationApiException {
|
||||
if (statusInfo().rs == 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int currentDraw() throws CommunicationApiException, NoCurrentDrawSensorException {
|
||||
JcResponse info = statusInfo();
|
||||
if (info.curr == null) {
|
||||
throw new NoCurrentDrawSensorException();
|
||||
}
|
||||
return info.curr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int waterLevel() throws CommunicationApiException {
|
||||
JoResponse info = getOptions();
|
||||
return info.wl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfStations() throws CommunicationApiException {
|
||||
String returnContent;
|
||||
|
||||
try {
|
||||
returnContent = http.sendHttpGet(getBaseUrl() + CMD_STATION_INFO, getRequestRequiredOptions());
|
||||
} catch (Exception exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
this.numberOfStations = Parse.jsonInt(returnContent, JSON_OPTION_STATION_COUNT);
|
||||
|
||||
return this.numberOfStations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirmwareVersion() throws CommunicationApiException {
|
||||
|
||||
try {
|
||||
JoResponse info = getOptions();
|
||||
this.firmwareVersion = info.fwv;
|
||||
} catch (Exception exp) {
|
||||
this.firmwareVersion = -1;
|
||||
}
|
||||
|
||||
return this.firmwareVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hostname and port formatted URL as a String.
|
||||
*
|
||||
* @return String representation of the OpenSprinkler API URL.
|
||||
*/
|
||||
protected String getBaseUrl() {
|
||||
return hostname + ":" + port + "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the required URL parameters required for every API call.
|
||||
*
|
||||
* @return String representation of the parameters needed during an API call.
|
||||
*/
|
||||
protected String getRequestRequiredOptions() {
|
||||
return CMD_PASSWORD + this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StationProgram retrieveProgram(int station) throws CommunicationApiException {
|
||||
JcResponse resp = statusInfo();
|
||||
return resp.ps.stream().map(values -> new StationProgram(values.get(1))).collect(Collectors.toList())
|
||||
.get(station);
|
||||
}
|
||||
|
||||
private JcResponse statusInfo() throws CommunicationApiException {
|
||||
String returnContent;
|
||||
|
||||
try {
|
||||
returnContent = http.sendHttpGet(getBaseUrl() + CMD_STATUS_INFO, getRequestRequiredOptions());
|
||||
} catch (CommunicationApiException exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
JcResponse resp = gson.fromJson(returnContent, JcResponse.class);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private static class JcResponse {
|
||||
public List<List<Integer>> ps;
|
||||
@SerializedName(value = "sn1", alternate = "rs")
|
||||
public int rs;
|
||||
public Integer curr;
|
||||
}
|
||||
|
||||
private JoResponse getOptions() throws CommunicationApiException {
|
||||
String returnContent;
|
||||
|
||||
try {
|
||||
returnContent = http.sendHttpGet(getBaseUrl() + CMD_OPTIONS_INFO, getRequestRequiredOptions());
|
||||
} catch (CommunicationApiException exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
JoResponse resp = gson.fromJson(returnContent, JoResponse.class);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private static class JoResponse {
|
||||
public int wl;
|
||||
public int fwv;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class contains helper methods for communicating HTTP GET and HTTP POST
|
||||
* requests.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
* @author Florian Schmidt - Reduce visibility of Http communication to Api
|
||||
*/
|
||||
protected class HttpRequestSender {
|
||||
private static final int HTTP_OK_CODE = 200;
|
||||
private static final String USER_AGENT = "Mozilla/5.0";
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public HttpRequestSender(HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a URL and a set parameters, send a HTTP GET request to the URL location
|
||||
* created by the URL and parameters.
|
||||
*
|
||||
* @param url The URL to send a GET request to.
|
||||
* @param urlParameters List of parameters to use in the URL for the GET
|
||||
* request. Null if no parameters.
|
||||
* @return String contents of the response for the GET request.
|
||||
* @throws Exception
|
||||
*/
|
||||
public String sendHttpGet(String url, String urlParameters) throws CommunicationApiException {
|
||||
String location = null;
|
||||
|
||||
if (urlParameters != null) {
|
||||
location = url + "?" + urlParameters;
|
||||
} else {
|
||||
location = url;
|
||||
}
|
||||
|
||||
ContentResponse response;
|
||||
try {
|
||||
response = withGeneralProperties(httpClient.newRequest(location)).method(HttpMethod.GET).send();
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
throw new CommunicationApiException("Request to OpenSprinkler device failed: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (response.getStatus() != HTTP_OK_CODE) {
|
||||
throw new CommunicationApiException(
|
||||
"Error sending HTTP GET request to " + url + ". Got response code: " + response.getStatus());
|
||||
}
|
||||
|
||||
return response.getContentAsString();
|
||||
}
|
||||
|
||||
private Request withGeneralProperties(Request request) {
|
||||
request.header(HttpHeader.USER_AGENT, USER_AGENT);
|
||||
if (basicUsername != null && basicPassword != null) {
|
||||
String encoded = Base64.getEncoder()
|
||||
.encodeToString((basicUsername + ":" + basicPassword).getBytes(StandardCharsets.UTF_8));
|
||||
request.header(HttpHeader.AUTHORIZATION, "Basic " + encoded);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a URL and a set parameters, send a HTTP POST request to the URL
|
||||
* location created by the URL and parameters.
|
||||
*
|
||||
* @param url The URL to send a POST request to.
|
||||
* @param urlParameters List of parameters to use in the URL for the POST
|
||||
* request. Null if no parameters.
|
||||
* @return String contents of the response for the POST request.
|
||||
* @throws Exception
|
||||
*/
|
||||
public String sendHttpPost(String url, String urlParameters) throws CommunicationApiException {
|
||||
ContentResponse response;
|
||||
try {
|
||||
response = withGeneralProperties(httpClient.newRequest(url)).method(HttpMethod.POST)
|
||||
.content(new StringContentProvider(urlParameters)).send();
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
throw new CommunicationApiException("Request to OpenSprinkler device failed: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (response.getStatus() != HTTP_OK_CODE) {
|
||||
throw new CommunicationApiException(
|
||||
"Error sending HTTP POST request to " + url + ". Got response code: " + response.getStatus());
|
||||
}
|
||||
|
||||
return response.getContentAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.api;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.DataFormatErrorApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.DataMissingApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.MismatchApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.NotPermittedApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.OutOfRangeApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.PageNotFoundApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.UnauthorizedApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.UnknownApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterfaceConfig;
|
||||
import org.openhab.binding.opensprinkler.internal.util.Parse;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerHttpApiV210} class is used for communicating with
|
||||
* the OpenSprinkler API for firmware versions 2.1.0, 2.1.1 and 2.1.1
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
* @author Florian Schmidt - Refactor class visibility
|
||||
*/
|
||||
class OpenSprinklerHttpApiV210 extends OpenSprinklerHttpApiV100 {
|
||||
/**
|
||||
* Constructor for the OpenSprinkler API class to create a connection to the OpenSprinkler
|
||||
* device for control and obtaining status info.
|
||||
*
|
||||
* @param hostname Hostname or IP address as a String of the OpenSprinkler device.
|
||||
* @param port The port number the OpenSprinkler API is listening on.
|
||||
* @param password Admin password for the OpenSprinkler device.
|
||||
* @param basicUsername only needed if basic auth is required
|
||||
* @param basicPassword only needed if basic auth is required
|
||||
* @throws Exception
|
||||
*/
|
||||
OpenSprinklerHttpApiV210(final HttpClient httpClient, final OpenSprinklerHttpInterfaceConfig config)
|
||||
throws GeneralApiException {
|
||||
super(httpClient, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStationOpen(int station) throws GeneralApiException, CommunicationApiException {
|
||||
String returnContent;
|
||||
int stationStatus = -1;
|
||||
|
||||
if (station < 0 || station >= numberOfStations) {
|
||||
throw new GeneralApiException("This OpenSprinkler device only has " + this.numberOfStations
|
||||
+ " but station " + station + " was requested for a status update.");
|
||||
}
|
||||
try {
|
||||
returnContent = http.sendHttpGet(getBaseUrl() + CMD_STATION_INFO, getRequestRequiredOptions());
|
||||
} catch (CommunicationApiException exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
stationStatus = Parse.jsonIntAtArrayIndex(returnContent, JSON_OPTION_STATION, station);
|
||||
} catch (Exception exp) {
|
||||
throw new GeneralApiException("There was a problem parsing the station status for station " + station
|
||||
+ ". Got the error: " + exp.getMessage());
|
||||
}
|
||||
|
||||
if (stationStatus == 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openStation(int station, BigDecimal duration) throws CommunicationApiException, GeneralApiException {
|
||||
String returnContent;
|
||||
|
||||
if (station < 0 || station >= numberOfStations) {
|
||||
throw new GeneralApiException("This OpenSprinkler device only has " + this.numberOfStations
|
||||
+ " but station " + station + " was requested to be opened.");
|
||||
}
|
||||
|
||||
try {
|
||||
returnContent = http.sendHttpGet(getBaseUrl() + CMD_STATION_CONTROL, getRequestRequiredOptions() + "&"
|
||||
+ CMD_STATION + station + "&" + CMD_STATION_ENABLE + "&t=" + duration);
|
||||
} catch (CommunicationApiException exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
resultParser(returnContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeStation(int station) throws CommunicationApiException, GeneralApiException {
|
||||
String returnContent;
|
||||
|
||||
if (station < 0 || station > numberOfStations) {
|
||||
throw new GeneralApiException("This OpenSprinkler device only has " + this.numberOfStations
|
||||
+ " but station " + station + " was requested to be closed.");
|
||||
}
|
||||
|
||||
try {
|
||||
returnContent = http.sendHttpGet(getBaseUrl() + CMD_STATION_CONTROL,
|
||||
getRequestRequiredOptions() + "&" + CMD_STATION + station + "&" + CMD_STATION_DISABLE);
|
||||
} catch (Exception exp) {
|
||||
throw new CommunicationApiException(
|
||||
"There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
|
||||
}
|
||||
|
||||
resultParser(returnContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void enterManualMode() throws CommunicationApiException {
|
||||
this.firmwareVersion = getFirmwareVersion();
|
||||
this.numberOfStations = getNumberOfStations();
|
||||
|
||||
isInManualMode = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveManualMode() {
|
||||
isInManualMode = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom exception based on a result code from the OpenSprinkler device. This is a
|
||||
* formatted response from the API as {"result: : ##}.
|
||||
*
|
||||
* @param returnContent String value of the return content from the OpenSprinkler device when
|
||||
* an action result is returned from the API.
|
||||
* @throws Exception Returns a custom exception based on the result key.
|
||||
*/
|
||||
protected void resultParser(String returnContent) throws GeneralApiException {
|
||||
int returnCode;
|
||||
|
||||
try {
|
||||
returnCode = Parse.jsonInt(returnContent, JSON_OPTION_RESULT);
|
||||
} catch (Exception exp) {
|
||||
returnCode = -1;
|
||||
}
|
||||
|
||||
switch (returnCode) {
|
||||
case -1:
|
||||
throw new UnknownApiException(
|
||||
"The OpenSprinkler API returnd an result that was not parseable: " + returnContent);
|
||||
case 1:
|
||||
return;
|
||||
case 2:
|
||||
throw new UnauthorizedApiException("The OpenSprinkler API returned Unauthorized response code.");
|
||||
case 3:
|
||||
throw new MismatchApiException("The OpenSprinkler API returned Mismatch response code.");
|
||||
case 16:
|
||||
throw new DataMissingApiException("The OpenSprinkler API returned Data Missing response code.");
|
||||
case 17:
|
||||
throw new OutOfRangeApiException("The OpenSprinkler API returned Out of Range response code.");
|
||||
case 18:
|
||||
throw new DataFormatErrorApiException(
|
||||
"The OpenSprinkler API returned Data Format Error response code.");
|
||||
case 32:
|
||||
throw new PageNotFoundApiException("The OpenSprinkler API returned Page Not Found response code.");
|
||||
case 48:
|
||||
throw new NotPermittedApiException("The OpenSprinkler API returned Not Permitted response code.");
|
||||
default:
|
||||
throw new UnknownApiException("Unknown response code from OpenSprinkler API: " + returnCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterfaceConfig;
|
||||
import org.openhab.binding.opensprinkler.internal.util.Hash;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerHttpApiV213} class is used for communicating with
|
||||
* the OpenSprinkler API for firmware versions 2.1.3 and up.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
class OpenSprinklerHttpApiV213 extends OpenSprinklerHttpApiV210 {
|
||||
/**
|
||||
* Constructor for the OpenSprinkler API class to create a connection to the OpenSprinkler
|
||||
* device for control and obtaining status info.
|
||||
*
|
||||
* @param hostname Hostname or IP address as a String of the OpenSprinkler device.
|
||||
* @param port The port number the OpenSprinkler API is listening on.
|
||||
* @param password Admin password for the OpenSprinkler device.
|
||||
* @param basicUsername only needed if basic auth is required
|
||||
* @param basicPassword only needed if basic auth is required
|
||||
* @throws Exception
|
||||
*/
|
||||
OpenSprinklerHttpApiV213(final HttpClient httpClient, final OpenSprinklerHttpInterfaceConfig config)
|
||||
throws GeneralApiException {
|
||||
super(httpClient, withHashedPassword(config));
|
||||
}
|
||||
|
||||
private static OpenSprinklerHttpInterfaceConfig withHashedPassword(final OpenSprinklerHttpInterfaceConfig config) {
|
||||
config.password = Hash.getMD5Hash(config.password);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link CommunicationApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is problems communicating with the controller.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class CommunicationApiException extends Exception {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = 3030757939495216449L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public CommunicationApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommunicationApiException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link DataFormatErrorApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is "result" : 18.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class DataFormatErrorApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = -6690218834582334405L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public DataFormatErrorApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link DataMissingApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is "result" : 16.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class DataMissingApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = 5544694019051691391L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public DataMissingApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link GeneralApiException} exception is thrown when problems
|
||||
* working with the OpenSprinkler API arise.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class GeneralApiException extends Exception {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = -2222937111494475441L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public GeneralApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link MismatchApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is "result" : 3.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class MismatchApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public MismatchApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link NotPermittedApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is "result" : 48.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class NotPermittedApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = -4873603990171470019L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public NotPermittedApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link OutOfRangeApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is "result" : 17.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class OutOfRangeApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = 928567037902289026L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public OutOfRangeApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link PageNotFoundApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is "result" : 32.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class PageNotFoundApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = -6395534685500451473L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public PageNotFoundApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link UnauthorizedApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API is "result" : 2.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class UnauthorizedApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = 3509446091331584139L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public UnauthorizedApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.api.exception;
|
||||
|
||||
/**
|
||||
* The {@link UnknownApiException} exception is thrown when result from the OpenSprinkler
|
||||
* API returns an unknown result.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class UnknownApiException extends GeneralApiException {
|
||||
/**
|
||||
* Serial ID of this error class.
|
||||
*/
|
||||
private static final long serialVersionUID = -1166330604786956132L;
|
||||
|
||||
/**
|
||||
* Basic constructor allowing the storing of a single message.
|
||||
*
|
||||
* @param message Descriptive message about the error.
|
||||
*/
|
||||
public UnknownApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.config;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.DEFAULT_REFRESH_RATE;
|
||||
import static org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiConstants.*;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerHttpInterfaceConfig} class defines the configuration options
|
||||
* for the OpenSprinkler Thing.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class OpenSprinklerHttpInterfaceConfig {
|
||||
/**
|
||||
* Hostname of the OpenSprinkler API.
|
||||
*/
|
||||
public String hostname = null;
|
||||
|
||||
/**
|
||||
* The port the OpenSprinkler API is listening on.
|
||||
*/
|
||||
public int port = DEFAULT_API_PORT;
|
||||
|
||||
/**
|
||||
* The password to connect to the OpenSprinkler API.
|
||||
*/
|
||||
public String password = DEFAULT_ADMIN_PASSWORD;
|
||||
|
||||
/**
|
||||
* Number of seconds in between refreshes from the OpenSprinkler device.
|
||||
*/
|
||||
public int refresh = DEFAULT_REFRESH_RATE;
|
||||
/**
|
||||
* The basic auth username to use when the OpenSprinkler device is behind a reverse proxy with basic auth enabled.
|
||||
*/
|
||||
public String basicUsername = null;
|
||||
/**
|
||||
* The basic auth password to use when the OpenSprinkler device is behind a reverse proxy with basic auth enabled.
|
||||
*/
|
||||
public String basicPassword = null;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.config;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.DEFAULT_REFRESH_RATE;
|
||||
import static org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiConstants.DEFAULT_STATION_COUNT;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerPiConfig} class defines the configuration options
|
||||
* for the OpenSprinkler PI Thing.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class OpenSprinklerPiConfig {
|
||||
/**
|
||||
* Number of stations to control.
|
||||
*/
|
||||
public int stations = DEFAULT_STATION_COUNT;
|
||||
|
||||
/**
|
||||
* Number of seconds in between refreshes from the OpenSprinkler device.
|
||||
*/
|
||||
public int refresh = DEFAULT_REFRESH_RATE;
|
||||
}
|
||||
@@ -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.opensprinkler.internal.config;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerStationConfig} class defines the configuration options
|
||||
* for the OpenSprinkler Thing.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class OpenSprinklerStationConfig {
|
||||
/**
|
||||
* The index of the station the thing is configured to control, starting with 0.
|
||||
*/
|
||||
public int stationIndex = -1;
|
||||
}
|
||||
@@ -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.opensprinkler.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.DISCOVERY_DEFAULT_IP_TIMEOUT_RATE;
|
||||
import static org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApi;
|
||||
import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterfaceConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerDiscoveryJob} class allow manual discovery of
|
||||
* OpenSprinkler devices for a single IP address. This is used
|
||||
* for threading to make discovery faster.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class OpenSprinklerDiscoveryJob implements Runnable {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenSprinklerDiscoveryJob.class);
|
||||
|
||||
private OpenSprinklerDiscoveryService discoveryClass;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public OpenSprinklerDiscoveryJob(OpenSprinklerDiscoveryService service, String ip) {
|
||||
this.discoveryClass = service;
|
||||
this.ipAddress = ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (hasOpenSprinklerDevice(this.ipAddress)) {
|
||||
discoveryClass.submitDiscoveryResults(this.ipAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an OpenSprinkler device is available at a given IP address.
|
||||
*
|
||||
* @param ip IP address of the OpenSprinkler device as a string.
|
||||
* @return True if a device is found, false if not.
|
||||
*/
|
||||
private boolean hasOpenSprinklerDevice(String ip) {
|
||||
try {
|
||||
InetAddress address = InetAddress.getByName(ip);
|
||||
|
||||
if (canEstablishConnection(address, DEFAULT_API_PORT)) {
|
||||
OpenSprinklerHttpInterfaceConfig config = new OpenSprinklerHttpInterfaceConfig();
|
||||
config.hostname = ip;
|
||||
config.port = DEFAULT_API_PORT;
|
||||
config.password = DEFAULT_ADMIN_PASSWORD;
|
||||
OpenSprinklerApi openSprinkler = discoveryClass.getApiFactory().getHttpApi(config);
|
||||
|
||||
return (openSprinkler != null);
|
||||
} else {
|
||||
logger.trace("No OpenSprinkler device found at IP address ({})", ip);
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (Exception exp) {
|
||||
logger.debug("No OpenSprinkler device found at IP address ({}) because of error: {}", ip, exp.getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to establish a connection to a hostname and port.
|
||||
*
|
||||
* @param host Hostname or IP address to connect to.
|
||||
* @param port Port to attempt to connect to.
|
||||
* @return True if a connection can be established, false if not.
|
||||
*/
|
||||
private boolean canEstablishConnection(InetAddress host, int port) {
|
||||
boolean reachable = false;
|
||||
|
||||
try (Socket socket = new Socket()) {
|
||||
socket.connect(new InetSocketAddress(host, port), DISCOVERY_DEFAULT_IP_TIMEOUT_RATE);
|
||||
|
||||
reachable = true;
|
||||
} catch (IOException e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
return reachable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.*;
|
||||
import static org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiConstants.*;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.commons.net.util.SubnetUtils;
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiFactory;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenSprinklerDiscoveryService} class allow manual discovery of
|
||||
* OpenSprinkler devices.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.opensprinkler")
|
||||
public class OpenSprinklerDiscoveryService extends AbstractDiscoveryService {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenSprinklerDiscoveryService.class);
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(
|
||||
Arrays.asList(OPENSPRINKLER_HTTP_BRIDGE));
|
||||
|
||||
private ExecutorService discoverySearchPool;
|
||||
private OpenSprinklerApiFactory apiFactory;
|
||||
|
||||
@Activate
|
||||
public OpenSprinklerDiscoveryService(@Reference OpenSprinklerApiFactory apiFactory) {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_DEFAULT_TIMEOUT_RATE, DISCOVERY_DEFAULT_AUTO_DISCOVER);
|
||||
this.apiFactory = apiFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||
return SUPPORTED_THING_TYPES_UIDS;
|
||||
}
|
||||
|
||||
OpenSprinklerApiFactory getApiFactory() {
|
||||
return this.apiFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Starting discovery of OpenSprinkler devices.");
|
||||
|
||||
try {
|
||||
List<String> ipList = getIpAddressScanList();
|
||||
|
||||
discoverySearchPool = Executors.newFixedThreadPool(DISCOVERY_THREAD_POOL_SIZE);
|
||||
|
||||
for (String ip : ipList) {
|
||||
discoverySearchPool.execute(new OpenSprinklerDiscoveryJob(this, ip));
|
||||
}
|
||||
|
||||
discoverySearchPool.shutdown();
|
||||
} catch (Exception exp) {
|
||||
logger.debug("OpenSprinkler discovery service encountered an error while scanning for devices: {}",
|
||||
exp.getMessage());
|
||||
}
|
||||
|
||||
logger.debug("Completed discovery of OpenSprinkler devices.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Thing with an IP address given. Uses default port and password.
|
||||
*
|
||||
* @param ip IP address of the OpenSprinkler device as a string.
|
||||
*/
|
||||
public void submitDiscoveryResults(String ip) {
|
||||
ThingUID uid = new ThingUID(OPENSPRINKLER_HTTP_BRIDGE, ip.replace('.', '_'));
|
||||
|
||||
HashMap<String, Object> properties = new HashMap<>();
|
||||
|
||||
properties.put("hostname", ip);
|
||||
properties.put("port", DEFAULT_API_PORT);
|
||||
properties.put("password", DEFAULT_ADMIN_PASSWORD);
|
||||
properties.put("refresh", DEFAULT_REFRESH_RATE);
|
||||
|
||||
thingDiscovered(
|
||||
DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel("OpenSprinkler").build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a string list of all the IP addresses associated with the network interfaces on
|
||||
* this machine.
|
||||
*
|
||||
* @return String list of IP addresses.
|
||||
* @throws UnknownHostException
|
||||
* @throws SocketException
|
||||
*/
|
||||
private List<String> getIpAddressScanList() throws UnknownHostException, SocketException {
|
||||
List<String> results = new ArrayList<>();
|
||||
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(localHost);
|
||||
|
||||
for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
|
||||
InetAddress ipAddress = address.getAddress();
|
||||
|
||||
String cidrSubnet = ipAddress.getHostAddress() + "/" + address.getNetworkPrefixLength();
|
||||
|
||||
/* Apache Subnet Utils only supports IP v4 for creating string list of IP's */
|
||||
if (ipAddress instanceof Inet4Address) {
|
||||
logger.debug("Found interface IPv4 address to scan: {}", cidrSubnet);
|
||||
|
||||
SubnetUtils utils = new SubnetUtils(cidrSubnet);
|
||||
|
||||
results.addAll(Arrays.asList(utils.getInfo().getAllAddresses()));
|
||||
} else if (ipAddress instanceof Inet6Address) {
|
||||
logger.debug("Found interface IPv6 address to scan: {}", cidrSubnet);
|
||||
} else {
|
||||
logger.debug("Found interface unknown IP type address to scan: {}", cidrSubnet);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.handler;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.DEFAULT_WAIT_BEFORE_INITIAL_REFRESH;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApi;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Refactoring
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class OpenSprinklerBaseBridgeHandler extends BaseBridgeHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenSprinklerBaseBridgeHandler.class);
|
||||
|
||||
@Nullable
|
||||
private ScheduledFuture<?> pollingJob;
|
||||
@Nullable
|
||||
protected OpenSprinklerApi openSprinklerDevice;
|
||||
|
||||
public OpenSprinklerBaseBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
public OpenSprinklerApi getApi() {
|
||||
OpenSprinklerApi api = openSprinklerDevice;
|
||||
if (api == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(this::refreshStations, DEFAULT_WAIT_BEFORE_INITIAL_REFRESH,
|
||||
getRefreshInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
protected abstract long getRefreshInterval();
|
||||
|
||||
private void refreshStations() {
|
||||
if (openSprinklerDevice != null) {
|
||||
if (openSprinklerDevice.isManualModeEnabled()) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
||||
this.getThing().getThings().forEach(thing -> {
|
||||
OpenSprinklerBaseHandler handler = (OpenSprinklerBaseHandler) thing.getHandler();
|
||||
if (handler != null) {
|
||||
handler.updateChannels();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not sync status with the OpenSprinkler.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (openSprinklerDevice != null) {
|
||||
try {
|
||||
openSprinklerDevice.leaveManualMode();
|
||||
} catch (CommunicationApiException e) {
|
||||
logger.error("Could not close connection on teardown.", e);
|
||||
}
|
||||
openSprinklerDevice = null;
|
||||
}
|
||||
|
||||
if (pollingJob != null) {
|
||||
pollingJob.cancel(true);
|
||||
pollingJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Nothing to do for the bridge handler
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApi;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Refactoring
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class OpenSprinklerBaseHandler extends BaseThingHandler {
|
||||
public OpenSprinklerBaseHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected OpenSprinklerApi getApi() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
return null;
|
||||
}
|
||||
BridgeHandler handler = bridge.getHandler();
|
||||
if (!(handler instanceof OpenSprinklerBaseBridgeHandler)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return ((OpenSprinklerBaseBridgeHandler) handler).getApi();
|
||||
} catch (IllegalStateException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateChannels() {
|
||||
this.getThing().getChannels().forEach(channel -> {
|
||||
updateChannel(channel.getUID());
|
||||
});
|
||||
if (getApi() != null) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void updateChannel(ChannelUID uid);
|
||||
}
|
||||
@@ -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.opensprinkler.internal.handler;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.*;
|
||||
import static org.openhab.core.library.unit.MetricPrefix.MILLI;
|
||||
import static org.openhab.core.library.unit.SmartHomeUnits.PERCENT;
|
||||
|
||||
import javax.measure.quantity.ElectricCurrent;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.model.NoCurrentDrawSensorException;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Refactoring
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenSprinklerDeviceHandler extends OpenSprinklerBaseHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenSprinklerDeviceHandler.class);
|
||||
|
||||
public OpenSprinklerDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChannel(ChannelUID channel) {
|
||||
try {
|
||||
switch (channel.getIdWithoutGroup()) {
|
||||
case SENSOR_RAIN:
|
||||
if (getApi().isRainDetected()) {
|
||||
updateState(channel, OnOffType.ON);
|
||||
} else {
|
||||
updateState(channel, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case SENSOR_WATERLEVEL:
|
||||
updateState(channel, QuantityType.valueOf(getApi().waterLevel(), PERCENT));
|
||||
break;
|
||||
case SENSOR_CURRENT_DRAW:
|
||||
updateState(channel,
|
||||
new QuantityType<ElectricCurrent>(getApi().currentDraw(), MILLI(SmartHomeUnits.AMPERE)));
|
||||
break;
|
||||
default:
|
||||
logger.debug("Not updating unknown channel {}", channel);
|
||||
}
|
||||
} catch (CommunicationApiException | NoCurrentDrawSensorException e) {
|
||||
logger.debug("Could not update {}", channel, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
ChannelUID currentDraw = new ChannelUID(thing.getUID(), "currentDraw");
|
||||
if (thing.getChannel(currentDraw) == null) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
try {
|
||||
getApi().currentDraw();
|
||||
|
||||
Channel currentDrawChannel = ChannelBuilder.create(currentDraw, "Number:ElectricCurrent")
|
||||
.withType(new ChannelTypeUID(BINDING_ID, SENSOR_CURRENT_DRAW)).withLabel("Current Draw")
|
||||
.withDescription("Provides the current draw.").build();
|
||||
thingBuilder.withChannel(currentDrawChannel);
|
||||
|
||||
updateThing(thingBuilder.build());
|
||||
} catch (NoCurrentDrawSensorException e) {
|
||||
if (thing.getChannel(currentDraw) != null) {
|
||||
thingBuilder.withoutChannel(currentDraw);
|
||||
}
|
||||
updateThing(thingBuilder.build());
|
||||
} catch (CommunicationApiException e) {
|
||||
logger.debug("Could not query current draw. Not removing channel as it could be temporary.", e);
|
||||
}
|
||||
}
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// nothing to do here
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.handler;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.DEFAULT_REFRESH_RATE;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApi;
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApiFactory;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterfaceConfig;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Refactoring
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenSprinklerHttpBridgeHandler extends OpenSprinklerBaseBridgeHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenSprinklerHttpBridgeHandler.class);
|
||||
|
||||
@Nullable
|
||||
private OpenSprinklerHttpInterfaceConfig openSprinklerConfig;
|
||||
private OpenSprinklerApiFactory apiFactory;
|
||||
|
||||
public OpenSprinklerHttpBridgeHandler(Bridge bridge, OpenSprinklerApiFactory apiFactory) {
|
||||
super(bridge);
|
||||
this.apiFactory = apiFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
OpenSprinklerHttpInterfaceConfig openSprinklerConfig = getConfig().as(OpenSprinklerHttpInterfaceConfig.class);
|
||||
this.openSprinklerConfig = openSprinklerConfig;
|
||||
|
||||
logger.debug("Initializing OpenSprinkler with config (Hostname: {}, Port: {}, Refresh: {}).",
|
||||
openSprinklerConfig.hostname, openSprinklerConfig.port, openSprinklerConfig.refresh);
|
||||
|
||||
OpenSprinklerApi openSprinklerDevice;
|
||||
try {
|
||||
openSprinklerDevice = apiFactory.getHttpApi(openSprinklerConfig);
|
||||
this.openSprinklerDevice = openSprinklerDevice;
|
||||
} catch (CommunicationApiException | GeneralApiException exp) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not create API connection to the OpenSprinkler device. Error received: " + exp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Successfully created API connection to the OpenSprinkler device.");
|
||||
|
||||
try {
|
||||
openSprinklerDevice.enterManualMode();
|
||||
} catch (CommunicationApiException exp) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not open API connection to the OpenSprinkler device. Error received: " + exp);
|
||||
}
|
||||
|
||||
if (openSprinklerDevice.isManualModeEnabled()) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not initialize the connection to the OpenSprinkler.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getRefreshInterval() {
|
||||
OpenSprinklerHttpInterfaceConfig openSprinklerConfig = this.openSprinklerConfig;
|
||||
if (openSprinklerConfig == null) {
|
||||
return DEFAULT_REFRESH_RATE;
|
||||
}
|
||||
return openSprinklerConfig.refresh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.handler;
|
||||
|
||||
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.opensprinkler.internal.api.OpenSprinklerApi;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
|
||||
import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerStationConfig;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import tec.uom.se.unit.Units;
|
||||
|
||||
/**
|
||||
* @author Florian Schmidt - Refactoring
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenSprinklerStationHandler extends OpenSprinklerBaseHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenSprinklerStationHandler.class);
|
||||
|
||||
@Nullable
|
||||
private OpenSprinklerStationConfig config;
|
||||
@Nullable
|
||||
private BigDecimal nextDurationTime;
|
||||
|
||||
public OpenSprinklerStationHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfig().as(OpenSprinklerStationConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
OpenSprinklerApi api = getApi();
|
||||
if (api == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "OpenSprinkler bridge has no initialized API.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (command != RefreshType.REFRESH) {
|
||||
switch (channelUID.getIdWithoutGroup()) {
|
||||
case NEXT_DURATION:
|
||||
handleNextDurationCommand(channelUID, command);
|
||||
break;
|
||||
case STATION_STATE:
|
||||
handleStationStateCommand(api, command);
|
||||
break;
|
||||
case STATION_QUEUED:
|
||||
handleQueuedCommand(api, command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateChannels();
|
||||
}
|
||||
|
||||
private void handleNextDurationCommand(ChannelUID channelUID, Command command) {
|
||||
if (!(command instanceof QuantityType<?>)) {
|
||||
logger.info("Ignoring implausible non-QuantityType command for NEXT_DURATION");
|
||||
return;
|
||||
}
|
||||
QuantityType<?> quantity = (QuantityType<?>) command;
|
||||
this.nextDurationTime = quantity.toBigDecimal();
|
||||
updateState(channelUID, quantity);
|
||||
}
|
||||
|
||||
private void handleStationStateCommand(OpenSprinklerApi api, Command command) {
|
||||
if (!(command instanceof OnOffType)) {
|
||||
logger.error("Received invalid command type for OpenSprinkler station ({}).", command);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (command == OnOffType.ON) {
|
||||
api.openStation(this.getStationIndex(), nextStationDuration());
|
||||
} else {
|
||||
api.closeStation(this.getStationIndex());
|
||||
}
|
||||
} catch (CommunicationApiException | GeneralApiException exp) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not control the station channel " + (this.getStationIndex() + 1)
|
||||
+ " for the OpenSprinkler. Error: " + exp.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleQueuedCommand(OpenSprinklerApi api, Command command) {
|
||||
if (command == OnOffType.ON) {
|
||||
return;
|
||||
}
|
||||
handleStationStateCommand(api, command);
|
||||
}
|
||||
|
||||
private BigDecimal nextStationDuration() {
|
||||
BigDecimal nextDurationItemValue = nextDurationValue();
|
||||
Channel nextDuration = getThing().getChannel(NEXT_DURATION);
|
||||
if (nextDuration != null && isLinked(nextDuration.getUID()) && nextDurationItemValue != null) {
|
||||
return nextDurationItemValue;
|
||||
}
|
||||
return new BigDecimal(64800);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles determining a channel's current state from the OpenSprinkler device.
|
||||
*
|
||||
* @param stationId Int of the station to control. Starts at 0.
|
||||
* @return State representation for the channel.
|
||||
*/
|
||||
@Nullable
|
||||
private OnOffType getStationState(int stationId) {
|
||||
boolean stationOn = false;
|
||||
OpenSprinklerApi api = getApi();
|
||||
if (api == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
|
||||
"OpenSprinkler bridge has no initialized API.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
stationOn = api.isStationOpen(stationId);
|
||||
} catch (GeneralApiException | CommunicationApiException exp) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not get the station channel " + stationId
|
||||
+ " current state from the OpenSprinkler thing. Error: " + exp.getMessage());
|
||||
}
|
||||
|
||||
if (stationOn) {
|
||||
return OnOffType.ON;
|
||||
} else {
|
||||
return OnOffType.OFF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles determining a channel's current state from the OpenSprinkler device.
|
||||
*
|
||||
* @param stationId Int of the station to control. Starts at 0.
|
||||
* @return State representation for the channel.
|
||||
*/
|
||||
private @Nullable QuantityType<Time> getRemainingWaterTime(int stationId) {
|
||||
long remainingWaterTime = 0;
|
||||
OpenSprinklerApi api = getApi();
|
||||
if (api == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
|
||||
"OpenSprinkler bridge has no initialized API.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
remainingWaterTime = api.retrieveProgram(stationId).remainingWaterTime;
|
||||
} catch (CommunicationApiException exp) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Could not get current state of station channel " + stationId
|
||||
+ " for the OpenSprinkler device. Exception received: " + exp);
|
||||
}
|
||||
|
||||
return new QuantityType<>(remainingWaterTime, Units.SECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateChannel(@NonNull ChannelUID channel) {
|
||||
OnOffType currentDeviceState = getStationState(this.getStationIndex());
|
||||
QuantityType<Time> remainingWaterTime = getRemainingWaterTime(config.stationIndex);
|
||||
switch (channel.getIdWithoutGroup()) {
|
||||
case STATION_STATE:
|
||||
if (currentDeviceState != null) {
|
||||
updateState(channel, currentDeviceState);
|
||||
}
|
||||
break;
|
||||
case REMAINING_WATER_TIME:
|
||||
if (remainingWaterTime != null) {
|
||||
updateState(channel, remainingWaterTime);
|
||||
}
|
||||
break;
|
||||
case NEXT_DURATION:
|
||||
BigDecimal duration = nextDurationValue();
|
||||
if (duration != null) {
|
||||
updateState(channel, new DecimalType(duration));
|
||||
}
|
||||
break;
|
||||
case STATION_QUEUED:
|
||||
if (remainingWaterTime != null && currentDeviceState != null && currentDeviceState == OnOffType.OFF
|
||||
&& remainingWaterTime.intValue() != 0) {
|
||||
updateState(channel, OnOffType.ON);
|
||||
} else {
|
||||
updateState(channel, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Not updating unknown channel {}", channel);
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable BigDecimal nextDurationValue() {
|
||||
return nextDurationTime;
|
||||
}
|
||||
|
||||
private int getStationIndex() {
|
||||
OpenSprinklerStationConfig config = this.config;
|
||||
if (config == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return config.stationIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.model;
|
||||
|
||||
/**
|
||||
* Indicates, that a device is missing a sensor to measure the current draw of itself.
|
||||
*
|
||||
* @author Florian Schmidt - Initial contribution
|
||||
*/
|
||||
public class NoCurrentDrawSensorException extends Exception {
|
||||
private static final long serialVersionUID = 2251925316743442346L;
|
||||
}
|
||||
@@ -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.opensprinkler.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link StationProgram} class corresponds to the program set in the station.
|
||||
*
|
||||
* @author Florian Schmidt - Initial contribution
|
||||
*/
|
||||
public class StationProgram {
|
||||
public final long remainingWaterTime;
|
||||
|
||||
public StationProgram(int remainingWaterTime) {
|
||||
this.remainingWaterTime = remainingWaterTime;
|
||||
}
|
||||
}
|
||||
@@ -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.opensprinkler.internal.util;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* The {@link Hash} class contains static methods for creating hashes
|
||||
* of strings. Usually for password hashing.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class Hash {
|
||||
private static final String MD5_HASH_ALGORITHM = "MD5";
|
||||
private static final String UTF8_CHAR_SET = "UTF-8";
|
||||
|
||||
/**
|
||||
* Given a string, return the MD5 hash of the String.
|
||||
*
|
||||
* @param unhashed The string contents to be hashed.
|
||||
* @return MD5 Hashed value of the String. Null if there is a problem hashing the String.
|
||||
*/
|
||||
public static String getMD5Hash(String unhashed) {
|
||||
try {
|
||||
byte[] bytesOfMessage = unhashed.getBytes(UTF8_CHAR_SET);
|
||||
|
||||
MessageDigest md5 = MessageDigest.getInstance(MD5_HASH_ALGORITHM);
|
||||
|
||||
byte[] hash = md5.digest(bytesOfMessage);
|
||||
|
||||
StringBuilder sb = new StringBuilder(2 * hash.length);
|
||||
|
||||
for (byte b : hash) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
|
||||
String digest = sb.toString();
|
||||
|
||||
return digest;
|
||||
} catch (Exception exp) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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.opensprinkler.internal.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* The {@link Parse} class contains static methods for parsing JSON
|
||||
* output based on key names.
|
||||
*
|
||||
* @author Chris Graham - Initial contribution
|
||||
*/
|
||||
public class Parse {
|
||||
private static JsonParser jsonParser = new JsonParser();
|
||||
|
||||
/**
|
||||
* Parses an integer from a JSON string given its key name.
|
||||
*
|
||||
* @param jsonData The JSON formatted string to parse from.
|
||||
* @param keyName The name of the object data to return.
|
||||
* @return int value of the objects data.
|
||||
*/
|
||||
public static int jsonInt(String jsonData, String keyName) {
|
||||
JsonElement jelement = jsonParser.parse(jsonData);
|
||||
JsonObject jobject = jelement.getAsJsonObject();
|
||||
return jobject.get(keyName).getAsInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string from a JSON string given its key name.
|
||||
*
|
||||
* @param jsonData The JSON formatted string to parse from.
|
||||
* @param keyName The name of the object data to return.
|
||||
* @return String value of the objects data.
|
||||
*/
|
||||
public static String jsonString(String jsonData, String keyName) {
|
||||
JsonElement jelement = jsonParser.parse(jsonData);
|
||||
JsonObject jobject = jelement.getAsJsonObject();
|
||||
return jobject.get(keyName).getAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an int from a JSON array given its key name in the JSON string.
|
||||
*
|
||||
* @param jsonData The JSON formatted string to parse from.
|
||||
* @param keyName The name of the object array to search through.
|
||||
* @param index Index (starting at 0) number of the item in the JSON array to return.
|
||||
* @return int value of the objects data.
|
||||
*/
|
||||
public static int jsonIntAtArrayIndex(String jsonData, String keyName, int index) {
|
||||
JsonElement jelement = jsonParser.parse(jsonData);
|
||||
JsonObject jobject = jelement.getAsJsonObject();
|
||||
JsonArray jarray = jobject.get(keyName).getAsJsonArray();
|
||||
return jarray.get(index).getAsInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String from a JSON array given its key name in the JSON string.
|
||||
*
|
||||
* @param jsonData The JSON formatted string to parse from.
|
||||
* @param keyName The name of the object array to search through.
|
||||
* @param index Index (starting at 0) number of the item in the JSON array to return.
|
||||
* @return String value of the objects data.
|
||||
*/
|
||||
public static String jsonStringAtArrayIndex(String jsonData, String keyName, int index) {
|
||||
JsonElement jelement = jsonParser.parse(jsonData);
|
||||
JsonObject jobject = jelement.getAsJsonObject();
|
||||
JsonArray jarray = jobject.get(keyName).getAsJsonArray();
|
||||
return jarray.get(index).getAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an int array from a JSON string given its key name.
|
||||
*
|
||||
* @param jsonData The JSON formatted string to parse from.
|
||||
* @param keyName The name of the object array to return.
|
||||
* @return List of Integers with the values of a JSON Array.
|
||||
*/
|
||||
public static List<Integer> jsonIntArray(String jsonData, String keyName) {
|
||||
List<Integer> returnList = new ArrayList<>();
|
||||
|
||||
JsonElement jelement = jsonParser.parse(jsonData);
|
||||
JsonObject jobject = jelement.getAsJsonObject();
|
||||
JsonArray jarray = jobject.get(keyName).getAsJsonArray();
|
||||
|
||||
for (int i = 0; i < jarray.size(); i++) {
|
||||
returnList.add(jarray.get(i).getAsInt());
|
||||
}
|
||||
|
||||
return returnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an String array from a JSON string given its key name.
|
||||
*
|
||||
* @param jsonData The JSON formatted string to parse from.
|
||||
* @param keyName The name of the object array to search through.
|
||||
* @return List of Strings with the values of a JSON Array.
|
||||
*/
|
||||
public static List<String> jsonStringArray(String jsonData, String keyName) {
|
||||
List<String> returnList = new ArrayList<>();
|
||||
|
||||
JsonElement jelement = jsonParser.parse(jsonData);
|
||||
JsonObject jobject = jelement.getAsJsonObject();
|
||||
JsonArray jarray = jobject.get(keyName).getAsJsonArray();
|
||||
|
||||
for (int i = 0; i < jarray.size(); i++) {
|
||||
returnList.add(jarray.get(i).getAsString());
|
||||
}
|
||||
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="opensprinkler" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>OpenSprinkler Binding</name>
|
||||
<description>This is the binding for OpenSprinkler.</description>
|
||||
<author>Chris Graham</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="opensprinkler"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="http">
|
||||
<label>OpenSprinkler HTTP Interface</label>
|
||||
<description>A connection to a stand alone OpenSprinkler device which communicates over HTTP.</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="hostname" type="text">
|
||||
<label>Hostname</label>
|
||||
<description>The host name or IP address of the OpenSprinkler Web API interface. It may or may not start with the
|
||||
protocol, e.g. in order to use https:// instead of the default http://.</description>
|
||||
<default>localhost</default>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" min="1" max="65535">
|
||||
<label>Port</label>
|
||||
<description>Port of the OpenSprinkler Web API interface.</description>
|
||||
<default>80</default>
|
||||
</parameter>
|
||||
<parameter name="password" type="text">
|
||||
<label>Password</label>
|
||||
<description>The admin password used to access the Web API interface.</description>
|
||||
<default>opendoor</default>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the refresh interval in seconds.</description>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
<parameter name="basicUsername" type="text">
|
||||
<label>Basic Auth Username</label>
|
||||
<description>Used if the OpenSprinkler device is behind a basic auth protected reverse proxy.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="basicPassword" type="text">
|
||||
<label>Basic Auth Password</label>
|
||||
<description>Used if the OpenSprinkler device is behind a basic auth protected reverse proxy.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="station">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="http"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>OpenSprinkler Station</label>
|
||||
<description>Controls a station connected to the OpenSprinkler device.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="stationState" typeId="stationState"></channel>
|
||||
<channel id="queued" typeId="queued"></channel>
|
||||
<channel id="remainingWaterTime" typeId="remainingWaterTime"></channel>
|
||||
<channel id="nextDuration" typeId="nextDuration"></channel>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="stationIndex" type="integer" required="true">
|
||||
<label>Station Index</label>
|
||||
<description>The index of the station, starting with 0, of the station.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="device">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="http"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>OpenSprinkler Device</label>
|
||||
<description>A thing that receives data from the OpenSprinkler device directly.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="rainsensor" typeId="rainsensor"></channel>
|
||||
<channel id="waterlevel" typeId="waterlevel"></channel>
|
||||
</channels>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="rainsensor">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Rain</label>
|
||||
<description>Provides feedback on whether the OpenSprinkler device has detected rain or not.</description>
|
||||
<category>Sensor</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
|
||||
<channel-type id="waterlevel">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Water Level</label>
|
||||
<description>The current water level in percent</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="stationState">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Station</label>
|
||||
<description>Controls a station on the OpenSprinkler device.</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="queued">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Queued</label>
|
||||
<description>Indicates if the station is queued to be turned on. Can be removed from the queue by turning off. ON is
|
||||
read-only.</description>
|
||||
<category>Switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="remainingWaterTime">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Remaining Water Time</label>
|
||||
<description>Read-only property of the remaining water time of the station.</description>
|
||||
<state readOnly="true" pattern="%.0f min"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="nextDuration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Next Open Duration</label>
|
||||
<description>The duration the station will be opened the next time it is switched on.</description>
|
||||
<state readOnly="false" pattern="%.0f s"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user