[orbitbhyve] Initial contribution (#10426)
* [orbitbhyve] initial contribution Signed-off-by: Ondrej Pecta <opecta@gmail.com> * [orbitbhyve] improvements based on code review Signed-off-by: Ondrej Pecta <opecta@gmail.com> * [orbitbhyve] next bunch of fixes related to code review Signed-off-by: Ondrej Pecta <opecta@gmail.com> * Update bundles/org.openhab.binding.orbitbhyve/src/main/java/org/openhab/binding/orbitbhyve/internal/OrbitBhyveHandlerFactory.java Signed-off-by: Fabian Wolter <github@fabian-wolter.de> Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
parent
882374fbcc
commit
677804c485
|
@ -222,6 +222,7 @@
|
||||||
/bundles/org.openhab.binding.openweathermap/ @cweitkamp
|
/bundles/org.openhab.binding.openweathermap/ @cweitkamp
|
||||||
/bundles/org.openhab.binding.openwebnet/ @mvalla
|
/bundles/org.openhab.binding.openwebnet/ @mvalla
|
||||||
/bundles/org.openhab.binding.oppo/ @mlobstein
|
/bundles/org.openhab.binding.oppo/ @mlobstein
|
||||||
|
/bundles/org.openhab.binding.orbitbhyve/ @octa22
|
||||||
/bundles/org.openhab.binding.orvibo/ @tavalin
|
/bundles/org.openhab.binding.orvibo/ @tavalin
|
||||||
/bundles/org.openhab.binding.paradoxalarm/ @theater
|
/bundles/org.openhab.binding.paradoxalarm/ @theater
|
||||||
/bundles/org.openhab.binding.pentair/ @jsjames
|
/bundles/org.openhab.binding.pentair/ @jsjames
|
||||||
|
|
|
@ -1091,6 +1091,11 @@
|
||||||
<artifactId>org.openhab.binding.oppo</artifactId>
|
<artifactId>org.openhab.binding.oppo</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.binding.orbitbhyve</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.addons.bundles</groupId>
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
<artifactId>org.openhab.binding.orvibo</artifactId>
|
<artifactId>org.openhab.binding.orvibo</artifactId>
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
This content is produced and maintained by the openHAB project.
|
||||||
|
|
||||||
|
* Project home: https://www.openhab.org
|
||||||
|
|
||||||
|
== Declared Project Licenses
|
||||||
|
|
||||||
|
This program and the accompanying materials are made available under the terms
|
||||||
|
of the Eclipse Public License 2.0 which is available at
|
||||||
|
https://www.eclipse.org/legal/epl-2.0/.
|
||||||
|
|
||||||
|
== Source Code
|
||||||
|
|
||||||
|
https://github.com/openhab/openhab-addons
|
|
@ -0,0 +1,84 @@
|
||||||
|
# Orbit B-hyve Binding
|
||||||
|
|
||||||
|
This is the binding for the [Orbit B-hyve](https://bhyve.orbitonline.com/) wi-fi sprinklers.
|
||||||
|
|
||||||
|
## Supported Things
|
||||||
|
|
||||||
|
This binding should support all the sprinklers which can be controlled by the Orbit B-hyve mobile application.
|
||||||
|
So far only the [Orbit B-hyve 8-zone Indoor Timer](https://bhyve.orbitonline.com/indoor-timer/) has been confirmed working. (Hardware version WT24-0001)
|
||||||
|
|
||||||
|
## Discovery
|
||||||
|
|
||||||
|
This binding supports the auto discovery of the sprinklers bound to your Orbit B-hyve account.
|
||||||
|
To start the discovery you need to create a bridge thing and enter valid credentials to your Orbit B-hyve cloud account.
|
||||||
|
|
||||||
|
## Thing Configuration
|
||||||
|
|
||||||
|
The bridge thing requires a manual configuration. You have to enter valid credentials to your Orbit B-hyve account, and you can also set the refresh time in seconds for polling data from the Orbit cloud.
|
||||||
|
There is no user configuration related to sprinkler things. Sprinklers do need a configuration property _id_ identifying the device, but the only way how to retrieve it is to let the bridge to auto discover sprinklers.
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
This binding automatically detects all zones and programs for each sprinkler and creates these dynamic channels:
|
||||||
|
|
||||||
|
| channel | type | description |
|
||||||
|
|------------------|--------|------------------------------------------------------------------|
|
||||||
|
| zone_% | Switch | This channel controls the manual zone watering (ON/OFF) |
|
||||||
|
| program_% | Switch | This channel controls the manual program watering (ON/OFF) |
|
||||||
|
| enable_program_% | Switch | This channel controls the automatic program scheduling (ON/OFF) |
|
||||||
|
|
||||||
|
Beside the dynamic channels each sprinkler thing provides these standard channels:
|
||||||
|
|
||||||
|
| channel | type | description |
|
||||||
|
|----------------|-------------|--------------------------------------------------------------------|
|
||||||
|
| mode | String | This channel represents the mode of sprinkler device (auto/manual) |
|
||||||
|
| next_start | DateTime | This channel represents the start time of the next watering |
|
||||||
|
| rain_delay | Number:Time | This channel manages the current rain delay in hours |
|
||||||
|
| watering_time | Number:Time | This channel manages the manual zone watering time in minutes |
|
||||||
|
| control | Switch | This channel controls the sprinkler (ON/OFF) |
|
||||||
|
| smart_watering | Switch | This channel controls the smart watering (ON/OFF) |
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
_*.things example_
|
||||||
|
|
||||||
|
```
|
||||||
|
Bridge orbitbhyve:bridge:mybridge "Orbit Bridge" [ email="your@ema.il", password="yourPass", refresh=30 ] {
|
||||||
|
Thing sprinkler indoor_timer "Sprinkler" [ id="4cab55704e0d7ddf98c1cc37" ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
_*.items example_
|
||||||
|
|
||||||
|
```
|
||||||
|
Switch IrrigationControl "Irrigation active" <bhyve> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:control" }
|
||||||
|
Switch IrrigationSmartWatering "Smart watering" <bhyve> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:smart_watering" }
|
||||||
|
Switch Irrigation1 "Zone 1" <water> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:zone_1" }
|
||||||
|
Switch Irrigation2 "Zone 2" <water> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:zone_2" }
|
||||||
|
Switch Irrigation3 "Zone 3" <water> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:zone_3" }
|
||||||
|
Switch Irrigation4 "Zone 4" <water> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:zone_4" }
|
||||||
|
Switch IrrigationP1 "Run program A" <program> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:program_a" }
|
||||||
|
Switch IrrigationP1Enable "Schedule program A" <program> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:enable_program_a" }
|
||||||
|
String IrrigationMode "Irrigation mode [%s]" <water> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:mode" }
|
||||||
|
Number IrrigationTime "Irrigation time [%d min]" <clock> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:watering_time" }
|
||||||
|
Number IrrigationRainDelay "Rain delay [%d h]" <hourglass> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:rain_delay" }
|
||||||
|
DateTime IrrigationNextStart "Next start A [%1$td.%1$tm.%1$tY %1$tR]" <clock> (Out_Irrigation) { channel="orbitbhyve:sprinkler:mybridge:indoor_timer:next_start" }
|
||||||
|
```
|
||||||
|
|
||||||
|
_*.sitemap example_
|
||||||
|
|
||||||
|
```
|
||||||
|
Switch item=IrrigationControl
|
||||||
|
Switch item=IrrigationSmartWatering
|
||||||
|
Switch item=Irrigation1
|
||||||
|
Switch item=Irrigation2
|
||||||
|
Switch item=Irrigation3
|
||||||
|
Switch item=Irrigation4
|
||||||
|
Setpoint item=IrrigationTime minValue=1 maxValue=240 step=1
|
||||||
|
Switch item=IrrigationP1
|
||||||
|
Switch item=IrrigationP1Enable
|
||||||
|
Text item=IrrigationMode
|
||||||
|
Text item=IrrigationRainDelay
|
||||||
|
Switch item=IrrigationRainDelay mappings=[0="OFF", 24="24", 48="48", 72="72"]
|
||||||
|
Text item=IrrigationNextStart visibility=[IrrigationP1Enable==ON]
|
||||||
|
```
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||||
|
<version>3.2.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.binding.orbitbhyve</artifactId>
|
||||||
|
|
||||||
|
<name>openHAB Add-ons :: Bundles :: Orbit B-hyve Binding</name>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<features name="org.openhab.binding.orbitbhyve-${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-orbitbhyve" description="Orbit B-hyve Binding" version="${project.version}">
|
||||||
|
<feature>openhab-runtime-base</feature>
|
||||||
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.orbitbhyve/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
</features>
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveBindingConstants} class defines common constants, which are
|
||||||
|
* used across the whole binding.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveBindingConstants {
|
||||||
|
|
||||||
|
public static final String BINDING_ID = "orbitbhyve";
|
||||||
|
|
||||||
|
// List of all Thing Type UIDs
|
||||||
|
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
|
||||||
|
public static final ThingTypeUID THING_TYPE_SPRINKLER = new ThingTypeUID(BINDING_ID, "sprinkler");
|
||||||
|
|
||||||
|
// List of all Channel ids
|
||||||
|
public static final String CHANNEL_CONTROL = "control";
|
||||||
|
public static final String CHANNEL_MODE = "mode";
|
||||||
|
public static final String CHANNEL_SMART_WATERING = "smart_watering";
|
||||||
|
public static final String CHANNEL_NEXT_START = "next_start";
|
||||||
|
public static final String CHANNEL_RAIN_DELAY = "rain_delay";
|
||||||
|
public static final String CHANNEL_WATERING_TIME = "watering_time";
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
public static final String AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36";
|
||||||
|
public static final String BHYVE_API = "https://api.orbitbhyve.com/v1/";
|
||||||
|
public static final String BHYVE_SESSION = BHYVE_API + "session";
|
||||||
|
public static final String BHYVE_DEVICES = BHYVE_API + "devices";
|
||||||
|
public static final String BHYVE_PROGRAMS = BHYVE_API + "sprinkler_timer_programs";
|
||||||
|
public static final String BHYVE_WS_URL = "wss://api.orbitbhyve.com/v1/events";
|
||||||
|
public static final int BHYVE_TIMEOUT = 5;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveConfiguration} class contains fields mapping thing configuration parameters.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sample configuration parameter. Replace with your own.
|
||||||
|
*/
|
||||||
|
public String email = "";
|
||||||
|
public String password = "";
|
||||||
|
public int refresh = 30;
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.orbitbhyve.internal.OrbitBhyveBindingConstants.THING_TYPE_BRIDGE;
|
||||||
|
import static org.openhab.binding.orbitbhyve.internal.OrbitBhyveBindingConstants.THING_TYPE_SPRINKLER;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.handler.OrbitBhyveBridgeHandler;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.handler.OrbitBhyveSprinklerHandler;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.openhab.core.io.net.http.WebSocketFactory;
|
||||||
|
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 OrbitBhyveHandlerFactory} is responsible for creating things and thing
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(configurationPid = "binding.orbitbhyve", service = ThingHandlerFactory.class)
|
||||||
|
public class OrbitBhyveHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE, THING_TYPE_SPRINKLER);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the shared http client
|
||||||
|
*/
|
||||||
|
private HttpClient httpClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the shared web socket client
|
||||||
|
*/
|
||||||
|
private WebSocketClient webSocketClient;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public OrbitBhyveHandlerFactory(@Reference HttpClientFactory httpClientFactory,
|
||||||
|
@Reference WebSocketFactory webSocketFactory) {
|
||||||
|
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
this.webSocketClient = webSocketFactory.getCommonWebSocketClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
|
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
|
if (THING_TYPE_BRIDGE.equals(thingTypeUID)) {
|
||||||
|
return new OrbitBhyveBridgeHandler((Bridge) thing, httpClient, webSocketClient);
|
||||||
|
}
|
||||||
|
if (THING_TYPE_SPRINKLER.equals(thingTypeUID)) {
|
||||||
|
return new OrbitBhyveSprinklerHandler(thing);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.discovery;
|
||||||
|
|
||||||
|
import static org.openhab.binding.orbitbhyve.internal.OrbitBhyveBindingConstants.THING_TYPE_SPRINKLER;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
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.orbitbhyve.internal.handler.OrbitBhyveBridgeHandler;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveDevice;
|
||||||
|
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.Thing;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveDiscoveryService} discovers sprinklers
|
||||||
|
* associated with your Orbit B-Hyve cloud account.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveDiscoveryService extends AbstractDiscoveryService
|
||||||
|
implements DiscoveryService, ThingHandlerService {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(OrbitBhyveDiscoveryService.class);
|
||||||
|
|
||||||
|
private @Nullable OrbitBhyveBridgeHandler bridgeHandler;
|
||||||
|
|
||||||
|
private @Nullable ScheduledFuture<?> discoveryJob;
|
||||||
|
|
||||||
|
private static final int DISCOVERY_TIMEOUT_SEC = 10;
|
||||||
|
private static final int DISCOVERY_REFRESH_SEC = 1800;
|
||||||
|
|
||||||
|
public OrbitBhyveDiscoveryService() {
|
||||||
|
super(DISCOVERY_TIMEOUT_SEC);
|
||||||
|
logger.debug("Creating discovery service");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startScan() {
|
||||||
|
runDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void activate() {
|
||||||
|
super.activate(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deactivate() {
|
||||||
|
super.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setThingHandler(@Nullable ThingHandler thingHandler) {
|
||||||
|
if (thingHandler instanceof OrbitBhyveBridgeHandler) {
|
||||||
|
bridgeHandler = (OrbitBhyveBridgeHandler) thingHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ThingHandler getThingHandler() {
|
||||||
|
return bridgeHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startBackgroundDiscovery() {
|
||||||
|
logger.debug("Starting Orbit B-Hyve background discovery");
|
||||||
|
|
||||||
|
ScheduledFuture<?> localDiscoveryJob = discoveryJob;
|
||||||
|
if (localDiscoveryJob == null || localDiscoveryJob.isCancelled()) {
|
||||||
|
discoveryJob = scheduler.scheduleWithFixedDelay(this::runDiscovery, 10, DISCOVERY_REFRESH_SEC,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stopBackgroundDiscovery() {
|
||||||
|
logger.debug("Stopping Orbit B-Hyve background discovery");
|
||||||
|
ScheduledFuture<?> localDiscoveryJob = discoveryJob;
|
||||||
|
if (localDiscoveryJob != null && !localDiscoveryJob.isCancelled()) {
|
||||||
|
localDiscoveryJob.cancel(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void runDiscovery() {
|
||||||
|
OrbitBhyveBridgeHandler localBridgeHandler = bridgeHandler;
|
||||||
|
if (localBridgeHandler != null && ThingStatus.ONLINE == localBridgeHandler.getThing().getStatus()) {
|
||||||
|
List<OrbitBhyveDevice> devices = localBridgeHandler.getDevices();
|
||||||
|
logger.debug("Discovered total of {} devices", devices.size());
|
||||||
|
for (OrbitBhyveDevice device : devices) {
|
||||||
|
sprinklerDiscovered(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sprinklerDiscovered(OrbitBhyveDevice device) {
|
||||||
|
OrbitBhyveBridgeHandler localBridgeHandler = bridgeHandler;
|
||||||
|
if (localBridgeHandler != null) {
|
||||||
|
Map<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put("id", device.getId());
|
||||||
|
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, device.getFwVersion());
|
||||||
|
properties.put(Thing.PROPERTY_HARDWARE_VERSION, device.getHwVersion());
|
||||||
|
properties.put(Thing.PROPERTY_MAC_ADDRESS, device.getMacAddress());
|
||||||
|
properties.put(Thing.PROPERTY_MODEL_ID, device.getType());
|
||||||
|
properties.put("Zones", device.getNumStations());
|
||||||
|
properties.put("Active zones", device.getZones().size());
|
||||||
|
|
||||||
|
ThingUID thingUID = new ThingUID(THING_TYPE_SPRINKLER, localBridgeHandler.getThing().getUID(),
|
||||||
|
device.getId());
|
||||||
|
|
||||||
|
logger.debug("Detected a/an {} - label: {} id: {}", THING_TYPE_SPRINKLER.getId(), device.getName(),
|
||||||
|
device.getId());
|
||||||
|
thingDiscovered(DiscoveryResultBuilder.create(thingUID).withThingType(THING_TYPE_SPRINKLER)
|
||||||
|
.withProperties(properties).withRepresentationProperty("id").withLabel(device.getName())
|
||||||
|
.withBridge(localBridgeHandler.getThing().getUID()).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||||
|
return Collections.singleton(THING_TYPE_SPRINKLER);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,588 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.handler;
|
||||||
|
|
||||||
|
import static org.openhab.binding.orbitbhyve.internal.OrbitBhyveBindingConstants.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.OrbitBhyveConfiguration;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.discovery.OrbitBhyveDiscoveryService;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveDevice;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveProgram;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveSessionResponse;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveSocketEvent;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.net.OrbitBhyveSocket;
|
||||||
|
import org.openhab.core.config.core.status.ConfigStatusMessage;
|
||||||
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
import org.openhab.core.library.types.StringType;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.Channel;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveBridgeHandler} is responsible for handling commands, which are
|
||||||
|
* sent to one of the channels.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveBridgeHandler extends ConfigStatusBridgeHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(OrbitBhyveBridgeHandler.class);
|
||||||
|
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
|
private final WebSocketClient webSocketClient;
|
||||||
|
|
||||||
|
private @Nullable ScheduledFuture<?> future = null;
|
||||||
|
|
||||||
|
private @Nullable Session session;
|
||||||
|
|
||||||
|
private @Nullable String sessionToken = null;
|
||||||
|
|
||||||
|
private OrbitBhyveConfiguration config = new OrbitBhyveConfiguration();
|
||||||
|
|
||||||
|
private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||||
|
|
||||||
|
// Gson & parser
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
|
public OrbitBhyveBridgeHandler(Bridge thing, HttpClient httpClient, WebSocketClient webSocketClient) {
|
||||||
|
super(thing);
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
this.webSocketClient = webSocketClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigStatusMessage> getConfigStatus() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||||
|
return Collections.singleton(OrbitBhyveDiscoveryService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
config = getConfigAs(OrbitBhyveConfiguration.class);
|
||||||
|
httpClient.setFollowRedirects(false);
|
||||||
|
|
||||||
|
scheduler.execute(() -> {
|
||||||
|
login();
|
||||||
|
future = scheduler.scheduleWithFixedDelay(this::ping, 0, config.refresh, TimeUnit.SECONDS);
|
||||||
|
});
|
||||||
|
logger.debug("Finished initializing!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
ScheduledFuture<?> localFuture = future;
|
||||||
|
if (localFuture != null) {
|
||||||
|
localFuture.cancel(true);
|
||||||
|
}
|
||||||
|
closeSession();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean login() {
|
||||||
|
try {
|
||||||
|
String urlParameters = "{\"session\":{\"email\":\"" + config.email + "\",\"password\":\"" + config.password
|
||||||
|
+ "\"}}";
|
||||||
|
ContentResponse response = httpClient.newRequest(BHYVE_SESSION).method(HttpMethod.POST).agent(AGENT)
|
||||||
|
.content(new StringContentProvider(urlParameters), "application/json; charset=utf-8")
|
||||||
|
.timeout(BHYVE_TIMEOUT, TimeUnit.SECONDS).send();
|
||||||
|
if (response.getStatus() == 200) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("response: {}", response.getContentAsString());
|
||||||
|
}
|
||||||
|
OrbitBhyveSessionResponse session = gson.fromJson(response.getContentAsString(),
|
||||||
|
OrbitBhyveSessionResponse.class);
|
||||||
|
sessionToken = session.getOrbitSessionToken();
|
||||||
|
logger.debug("token: {}", sessionToken);
|
||||||
|
initializeWebSocketSession();
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||||
|
"Login response status:" + response.getStatus());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Exception during login");
|
||||||
|
return false;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Exception during login");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void ping() {
|
||||||
|
if (ThingStatus.OFFLINE == thing.getStatus()) {
|
||||||
|
login();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ThingStatus.ONLINE == thing.getStatus()) {
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession == null || !localSession.isOpen()) {
|
||||||
|
initializeWebSocketSession();
|
||||||
|
}
|
||||||
|
localSession = session;
|
||||||
|
if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
|
||||||
|
try {
|
||||||
|
logger.debug("Sending ping");
|
||||||
|
localSession.getRemote().sendString("{\"event\":\"ping\"}");
|
||||||
|
updateAllStatuses();
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Error sending ping to a web socket");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Web socket creation error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OrbitBhyveDevice> getDevices() {
|
||||||
|
try {
|
||||||
|
ContentResponse response = sendRequestBuilder(BHYVE_DEVICES, HttpMethod.GET).send();
|
||||||
|
if (response.getStatus() == 200) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Devices response: {}", response.getContentAsString());
|
||||||
|
}
|
||||||
|
OrbitBhyveDevice[] devices = gson.fromJson(response.getContentAsString(), OrbitBhyveDevice[].class);
|
||||||
|
return Arrays.asList(devices);
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Get devices returned response status: " + response.getStatus());
|
||||||
|
}
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting devices");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting devices");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Request sendRequestBuilder(String uri, HttpMethod method) {
|
||||||
|
return httpClient.newRequest(uri).method(method).agent(AGENT).header("Orbit-Session-Token", sessionToken)
|
||||||
|
.timeout(BHYVE_TIMEOUT, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable OrbitBhyveDevice getDevice(String deviceId) {
|
||||||
|
try {
|
||||||
|
ContentResponse response = sendRequestBuilder(BHYVE_DEVICES + "/" + deviceId, HttpMethod.GET).send();
|
||||||
|
if (response.getStatus() == 200) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Device response: {}", response.getContentAsString());
|
||||||
|
}
|
||||||
|
OrbitBhyveDevice device = gson.fromJson(response.getContentAsString(), OrbitBhyveDevice.class);
|
||||||
|
return device;
|
||||||
|
} else {
|
||||||
|
logger.debug("Returned status: {}", response.getStatus());
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Returned status: " + response.getStatus());
|
||||||
|
}
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Error during getting device info: " + deviceId);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Error during getting device info: " + deviceId);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void processStatusResponse(String content) {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
logger.trace("Got message: {}", content);
|
||||||
|
OrbitBhyveSocketEvent event = gson.fromJson(content, OrbitBhyveSocketEvent.class);
|
||||||
|
if (event != null) {
|
||||||
|
processEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processEvent(OrbitBhyveSocketEvent event) {
|
||||||
|
switch (event.getEvent()) {
|
||||||
|
case "watering_in_progress_notification":
|
||||||
|
disableZones(event.getDeviceId());
|
||||||
|
Channel channel = getThingChannel(event.getDeviceId(), event.getStation());
|
||||||
|
if (channel != null) {
|
||||||
|
logger.debug("Watering zone: {}", event.getStation());
|
||||||
|
updateState(channel.getUID(), OnOffType.ON);
|
||||||
|
String program = event.getProgram().getAsString();
|
||||||
|
if (!program.isEmpty() && !"manual".equals(program)) {
|
||||||
|
channel = getThingChannel(event.getDeviceId(), "program_" + program);
|
||||||
|
if (channel != null) {
|
||||||
|
updateState(channel.getUID(), OnOffType.ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "watering_complete":
|
||||||
|
logger.debug("Watering complete");
|
||||||
|
disableZones(event.getDeviceId());
|
||||||
|
disablePrograms(event.getDeviceId());
|
||||||
|
updateDeviceStatus(event.getDeviceId());
|
||||||
|
break;
|
||||||
|
case "change_mode":
|
||||||
|
logger.debug("Updating mode to: {}", event.getMode());
|
||||||
|
Channel ch = getThingChannel(event.getDeviceId(), CHANNEL_MODE);
|
||||||
|
if (ch != null) {
|
||||||
|
updateState(ch.getUID(), new StringType(event.getMode()));
|
||||||
|
}
|
||||||
|
ch = getThingChannel(event.getDeviceId(), CHANNEL_CONTROL);
|
||||||
|
if (ch != null) {
|
||||||
|
updateState(ch.getUID(), "off".equals(event.getMode()) ? OnOffType.OFF : OnOffType.ON);
|
||||||
|
}
|
||||||
|
updateDeviceStatus(event.getDeviceId());
|
||||||
|
break;
|
||||||
|
case "rain_delay":
|
||||||
|
updateDeviceStatus(event.getDeviceId());
|
||||||
|
break;
|
||||||
|
case "skip_active_station":
|
||||||
|
disableZones(event.getDeviceId());
|
||||||
|
break;
|
||||||
|
case "program_changed":
|
||||||
|
OrbitBhyveProgram program = gson.fromJson(event.getProgram(), OrbitBhyveProgram.class);
|
||||||
|
if (program != null) {
|
||||||
|
updateDeviceProgramStatus(program);
|
||||||
|
updateDeviceStatus(program.getDeviceId());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.debug("Received event: {}", event.getEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAllStatuses() {
|
||||||
|
List<OrbitBhyveDevice> devices = getDevices();
|
||||||
|
for (Thing th : getThing().getThings()) {
|
||||||
|
String deviceId = th.getUID().getId();
|
||||||
|
OrbitBhyveSprinklerHandler handler = (OrbitBhyveSprinklerHandler) th.getHandler();
|
||||||
|
for (OrbitBhyveDevice device : devices) {
|
||||||
|
if (deviceId.equals(th.getUID().getId())) {
|
||||||
|
updateDeviceStatus(device, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDeviceStatus(@Nullable OrbitBhyveDevice device, @Nullable OrbitBhyveSprinklerHandler handler) {
|
||||||
|
if (device != null && handler != null) {
|
||||||
|
handler.setDeviceOnline(device.isConnected());
|
||||||
|
handler.updateDeviceStatus(device.getStatus());
|
||||||
|
handler.updateSmartWatering(device.getWaterSenseMode());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDeviceStatus(String deviceId) {
|
||||||
|
for (Thing th : getThing().getThings()) {
|
||||||
|
if (deviceId.equals(th.getUID().getId())) {
|
||||||
|
OrbitBhyveSprinklerHandler handler = (OrbitBhyveSprinklerHandler) th.getHandler();
|
||||||
|
OrbitBhyveDevice device = getDevice(deviceId);
|
||||||
|
updateDeviceStatus(device, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDeviceProgramStatus(OrbitBhyveProgram program) {
|
||||||
|
for (Thing th : getThing().getThings()) {
|
||||||
|
if (program.getDeviceId().equals(th.getUID().getId())) {
|
||||||
|
OrbitBhyveSprinklerHandler handler = (OrbitBhyveSprinklerHandler) th.getHandler();
|
||||||
|
if (handler != null) {
|
||||||
|
handler.updateProgram(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableZones(String deviceId) {
|
||||||
|
disableChannel(deviceId, "zone_");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disablePrograms(String deviceId) {
|
||||||
|
disableChannel(deviceId, "program_");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableChannel(String deviceId, String name) {
|
||||||
|
for (Thing th : getThing().getThings()) {
|
||||||
|
if (deviceId.equals(th.getUID().getId())) {
|
||||||
|
for (Channel ch : th.getChannels()) {
|
||||||
|
if (ch.getUID().getId().startsWith(name)) {
|
||||||
|
updateState(ch.getUID(), OnOffType.OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Channel getThingChannel(String deviceId, int station) {
|
||||||
|
for (Thing th : getThing().getThings()) {
|
||||||
|
if (deviceId.equals(th.getUID().getId())) {
|
||||||
|
return th.getChannel("zone_" + station);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug("Cannot find zone: {} for device: {}", station, deviceId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Channel getThingChannel(String deviceId, String name) {
|
||||||
|
for (Thing th : getThing().getThings()) {
|
||||||
|
if (deviceId.equals(th.getUID().getId())) {
|
||||||
|
return th.getChannel(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug("Cannot find channel: {} for device: {}", name, deviceId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Session createSession() {
|
||||||
|
String url = BHYVE_WS_URL;
|
||||||
|
URI uri = URI.create(url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// The socket that receives events
|
||||||
|
OrbitBhyveSocket socket = new OrbitBhyveSocket(this);
|
||||||
|
// Attempt Connect
|
||||||
|
Future<Session> fut = webSocketClient.connect(socket, uri);
|
||||||
|
// Wait for Connect
|
||||||
|
return fut.get();
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect websocket client");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void initializeWebSocketSession() {
|
||||||
|
logger.debug("Initializing WebSocket session");
|
||||||
|
closeSession();
|
||||||
|
session = createSession();
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession != null) {
|
||||||
|
logger.debug("WebSocket connected!");
|
||||||
|
try {
|
||||||
|
String msg = "{\"event\":\"app_connection\",\"orbit_session_token\":\"" + sessionToken + "\"}";
|
||||||
|
logger.trace("sending message:\n {}", msg);
|
||||||
|
localSession.getRemote().sendString(msg);
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Cannot send hello string to web socket!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeSession() {
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession != null && localSession.isOpen()) {
|
||||||
|
localSession.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runZone(String deviceId, String zone, int time) {
|
||||||
|
String dateTime = format.format(new Date());
|
||||||
|
try {
|
||||||
|
ping();
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
|
||||||
|
localSession.getRemote()
|
||||||
|
.sendString("{\"event\":\"change_mode\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\""
|
||||||
|
+ dateTime + "\",\"mode\":\"manual\",\"stations\":[{\"station\":" + zone
|
||||||
|
+ ",\"run_time\":" + time + "}]}");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Error during zone watering execution");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runProgram(String deviceId, String program) {
|
||||||
|
String dateTime = format.format(new Date());
|
||||||
|
try {
|
||||||
|
ping();
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
|
||||||
|
localSession.getRemote().sendString("{\"event\":\"change_mode\",\"mode\":\"manual\",\"program\":\""
|
||||||
|
+ program + "\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\"" + dateTime + "\"}");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Error during program watering execution");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableProgram(OrbitBhyveProgram program, boolean enable) {
|
||||||
|
try {
|
||||||
|
String payLoad = "{\"sprinkler_timer_program\":{\"id\":\"" + program.getId() + "\",\"device_id\":\""
|
||||||
|
+ program.getDeviceId() + "\",\"program\":\"" + program.getProgram() + "\",\"enabled\":" + enable
|
||||||
|
+ "}}";
|
||||||
|
logger.debug("updating program {} with data {}", program.getProgram(), payLoad);
|
||||||
|
ContentResponse response = sendRequestBuilder(BHYVE_PROGRAMS + "/" + program.getId(), HttpMethod.PUT)
|
||||||
|
.content(new StringContentProvider(payLoad), "application/json; charset=utf-8").send();
|
||||||
|
if (response.getStatus() == 200) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Enable programs response: {}", response.getContentAsString());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
logger.debug("Returned status: {}", response.getStatus());
|
||||||
|
updateStatus(ThingStatus.OFFLINE);
|
||||||
|
}
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating programs");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating programs");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRainDelay(String deviceId, int delay) {
|
||||||
|
String dateTime = format.format(new Date());
|
||||||
|
try {
|
||||||
|
ping();
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
|
||||||
|
localSession.getRemote().sendString("{\"event\":\"rain_delay\",\"device_id\":\"" + deviceId
|
||||||
|
+ "\",\"delay\":" + delay + ",\"timestamp\":\"" + dateTime + "\"}");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during rain delay setting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopWatering(String deviceId) {
|
||||||
|
String dateTime = format.format(new Date());
|
||||||
|
try {
|
||||||
|
ping();
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
|
||||||
|
localSession.getRemote().sendString("{\"event\":\"change_mode\",\"device_id\":\"" + deviceId
|
||||||
|
+ "\",\"timestamp\":\"" + dateTime + "\",\"mode\":\"manual\",\"stations\":[]}");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during watering stopping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OrbitBhyveProgram> getPrograms() {
|
||||||
|
try {
|
||||||
|
ContentResponse response = sendRequestBuilder(BHYVE_PROGRAMS, HttpMethod.GET).send();
|
||||||
|
if (response.getStatus() == 200) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Programs response: {}", response.getContentAsString());
|
||||||
|
}
|
||||||
|
OrbitBhyveProgram[] devices = gson.fromJson(response.getContentAsString(), OrbitBhyveProgram[].class);
|
||||||
|
return Arrays.asList(devices);
|
||||||
|
} else {
|
||||||
|
logger.debug("Returned status: {}", response.getStatus());
|
||||||
|
updateStatus(ThingStatus.OFFLINE);
|
||||||
|
}
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting programs");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting programs");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeRunMode(String deviceId, String mode) {
|
||||||
|
String dateTime = format.format(new Date());
|
||||||
|
try {
|
||||||
|
ping();
|
||||||
|
Session localSession = session;
|
||||||
|
if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
|
||||||
|
localSession.getRemote().sendString("{\"event\":\"change_mode\",\"mode\":\"" + mode
|
||||||
|
+ "\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\"" + dateTime + "\"}");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during setting run mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmartWatering(String deviceId, boolean enable) {
|
||||||
|
OrbitBhyveDevice device = getDevice(deviceId);
|
||||||
|
if (device != null && device.getId().equals(deviceId)) {
|
||||||
|
device.setWaterSenseMode(enable ? "auto" : "off");
|
||||||
|
updateDevice(deviceId, gson.toJson(device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDevice(String deviceId, String deviceString) {
|
||||||
|
String payload = "{\"device\":" + deviceString + "}";
|
||||||
|
logger.trace("New String: {}", payload);
|
||||||
|
try {
|
||||||
|
ContentResponse response = sendRequestBuilder(BHYVE_DEVICES + "/" + deviceId, HttpMethod.PUT)
|
||||||
|
.content(new StringContentProvider(payload), "application/json;charset=UTF-8").send();
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Device update response: {}", response.getContentAsString());
|
||||||
|
}
|
||||||
|
if (response.getStatus() != 200) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Update device response status: " + response.getStatus());
|
||||||
|
}
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating device");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating device");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.handler;
|
||||||
|
|
||||||
|
import static org.openhab.binding.orbitbhyve.internal.OrbitBhyveBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveDevice;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveDeviceStatus;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveProgram;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveZone;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
|
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.library.types.StringType;
|
||||||
|
import org.openhab.core.library.unit.Units;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.Channel;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.ThingStatusInfo;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveSprinklerHandler} is responsible for handling commands, which are
|
||||||
|
* sent to one of the channels.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveSprinklerHandler extends BaseThingHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(OrbitBhyveSprinklerHandler.class);
|
||||||
|
|
||||||
|
public OrbitBhyveSprinklerHandler(Thing thing) {
|
||||||
|
super(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int wateringTime = 5;
|
||||||
|
private HashMap<String, OrbitBhyveProgram> programs = new HashMap<>();
|
||||||
|
private String deviceId = "";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
OrbitBhyveBridgeHandler handler = getBridgeHandler();
|
||||||
|
if (handler != null) {
|
||||||
|
if (CHANNEL_CONTROL.equals(channelUID.getId()) && command instanceof OnOffType) {
|
||||||
|
String mode = OnOffType.ON.equals(command) ? "auto" : "off";
|
||||||
|
handler.changeRunMode(deviceId, mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (CHANNEL_SMART_WATERING.equals(channelUID.getId()) && command instanceof OnOffType) {
|
||||||
|
boolean enable = OnOffType.ON.equals(command);
|
||||||
|
handler.setSmartWatering(deviceId, enable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!channelUID.getId().startsWith("enable_program") && OnOffType.OFF.equals(command)) {
|
||||||
|
handler.stopWatering(deviceId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (CHANNEL_WATERING_TIME.equals(channelUID.getId()) && command instanceof QuantityType) {
|
||||||
|
final QuantityType<?> value = ((QuantityType<?>) command).toUnit(Units.MINUTE);
|
||||||
|
if (value != null) {
|
||||||
|
wateringTime = value.intValue();
|
||||||
|
updateState(CHANNEL_WATERING_TIME, new DecimalType(wateringTime));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channelUID.getId().startsWith("zone")) {
|
||||||
|
if (OnOffType.ON.equals(command)) {
|
||||||
|
handler.runZone(deviceId, channelUID.getId().replace("zone_", ""), wateringTime);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channelUID.getId().startsWith("program")) {
|
||||||
|
if (OnOffType.ON.equals(command)) {
|
||||||
|
handler.runProgram(deviceId, channelUID.getId().replace("program_", ""));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channelUID.getId().startsWith("enable_program") && command instanceof OnOffType) {
|
||||||
|
String id = channelUID.getId().replace("enable_program_", "");
|
||||||
|
OrbitBhyveProgram prog = programs.get(id);
|
||||||
|
if (prog != null) {
|
||||||
|
handler.enableProgram(prog, OnOffType.ON.equals(command));
|
||||||
|
} else {
|
||||||
|
logger.debug("Cannot get program id: {}", id);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (CHANNEL_RAIN_DELAY.equals(channelUID.getId()) && command instanceof DecimalType) {
|
||||||
|
final QuantityType<?> value = ((QuantityType<?>) command).toUnit(Units.HOUR);
|
||||||
|
if (value != null) {
|
||||||
|
handler.setRainDelay(deviceId, value.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSprinklerId() {
|
||||||
|
return getThing().getConfiguration().get("id") != null ? getThing().getConfiguration().get("id").toString()
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable OrbitBhyveBridgeHandler getBridgeHandler() {
|
||||||
|
Bridge bridge = getBridge();
|
||||||
|
if (bridge != null) {
|
||||||
|
return (OrbitBhyveBridgeHandler) bridge.getHandler();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
Bridge bridge = getBridge();
|
||||||
|
if (bridge != null) {
|
||||||
|
logger.debug("Initializing, bridge is {}", bridge.getStatus());
|
||||||
|
if (ThingStatus.ONLINE == bridge.getStatus()) {
|
||||||
|
doInit();
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void doInit() {
|
||||||
|
OrbitBhyveBridgeHandler handler = getBridgeHandler();
|
||||||
|
if (handler != null) {
|
||||||
|
deviceId = getSprinklerId();
|
||||||
|
if ("".equals(deviceId)) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sprinkler id is missing!");
|
||||||
|
} else {
|
||||||
|
OrbitBhyveDevice device = handler.getDevice(deviceId);
|
||||||
|
if (device != null) {
|
||||||
|
setDeviceOnline(device.isConnected());
|
||||||
|
createChannels(device.getZones());
|
||||||
|
updateDeviceStatus(device.getStatus());
|
||||||
|
}
|
||||||
|
List<OrbitBhyveProgram> programs = handler.getPrograms();
|
||||||
|
for (OrbitBhyveProgram program : programs) {
|
||||||
|
if (deviceId.equals(program.getDeviceId())) {
|
||||||
|
cacheProgram(program);
|
||||||
|
createProgram(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState(CHANNEL_WATERING_TIME, new DecimalType(wateringTime));
|
||||||
|
logger.debug("Finished initializing of sprinkler!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||||
|
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||||
|
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||||
|
doInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void cacheProgram(OrbitBhyveProgram program) {
|
||||||
|
if (!programs.containsKey(program.getProgram())) {
|
||||||
|
programs.put(program.getProgram(), program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateDeviceStatus(OrbitBhyveDeviceStatus status) {
|
||||||
|
if (!status.getMode().isEmpty()) {
|
||||||
|
updateState(CHANNEL_MODE, new StringType(status.getMode()));
|
||||||
|
updateState(CHANNEL_CONTROL, "off".equals(status.getMode()) ? OnOffType.OFF : OnOffType.ON);
|
||||||
|
}
|
||||||
|
if (!status.getNextStartTime().isEmpty()) {
|
||||||
|
DateTimeType dt = new DateTimeType(status.getNextStartTime());
|
||||||
|
updateState(CHANNEL_NEXT_START, dt);
|
||||||
|
logger.debug("Next start time: {}", status.getNextStartTime());
|
||||||
|
}
|
||||||
|
updateState(CHANNEL_RAIN_DELAY, new DecimalType(status.getDelay()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createProgram(OrbitBhyveProgram program) {
|
||||||
|
String channelName = "program_" + program.getProgram();
|
||||||
|
if (thing.getChannel(channelName) == null) {
|
||||||
|
logger.debug("Creating channel for program: {} with name: {}", program.getProgram(), program.getName());
|
||||||
|
createProgramChannel(channelName, "Switch", "Program " + program.getName());
|
||||||
|
}
|
||||||
|
String enableChannelName = "enable_" + channelName;
|
||||||
|
if (thing.getChannel(enableChannelName) == null) {
|
||||||
|
logger.debug("Creating enable channel for program: {} with name: {}", program.getProgram(),
|
||||||
|
program.getName());
|
||||||
|
createProgramChannel(enableChannelName, "Switch", "Enable program " + program.getName());
|
||||||
|
}
|
||||||
|
Channel ch = thing.getChannel(enableChannelName);
|
||||||
|
if (ch != null) {
|
||||||
|
updateState(ch.getUID(), program.isEnabled() ? OnOffType.ON : OnOffType.OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createProgramChannel(String name, String type, String label) {
|
||||||
|
ChannelTypeUID program = new ChannelTypeUID(BINDING_ID, "program");
|
||||||
|
createChannel(name, type, label, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createChannels(List<OrbitBhyveZone> zones) {
|
||||||
|
for (OrbitBhyveZone zone : zones) {
|
||||||
|
String channelName = "zone_" + zone.getStation();
|
||||||
|
if (thing.getChannel(channelName) == null) {
|
||||||
|
logger.debug("Creating channel for zone: {} with name: {}", zone.getStation(), zone.getName());
|
||||||
|
createZoneChannel(channelName, "Switch", "Zone " + zone.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createZoneChannel(String name, String type, String label) {
|
||||||
|
ChannelTypeUID zone = new ChannelTypeUID(BINDING_ID, "zone");
|
||||||
|
createChannel(name, type, label, zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createChannel(String name, String type, String label, ChannelTypeUID typeUID) {
|
||||||
|
ThingBuilder thingBuilder = editThing();
|
||||||
|
Channel channel = ChannelBuilder.create(new ChannelUID(thing.getUID(), name), type).withLabel(label)
|
||||||
|
.withType(typeUID).build();
|
||||||
|
thingBuilder.withChannel(channel);
|
||||||
|
updateThing(thingBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceOnline(boolean connected) {
|
||||||
|
if (!connected) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
"Not connected to Orbit BHyve Cloud");
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateProgram(OrbitBhyveProgram program) {
|
||||||
|
String enableChannelName = "enable_program_" + program.getProgram();
|
||||||
|
Channel ch = thing.getChannel(enableChannelName);
|
||||||
|
if (ch != null) {
|
||||||
|
updateState(ch.getUID(), program.isEnabled() ? OnOffType.ON : OnOffType.OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSmartWatering(String senseMode) {
|
||||||
|
updateState(CHANNEL_SMART_WATERING, ("auto".equals(senseMode)) ? OnOffType.ON : OnOffType.OFF);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveDevice} holds information about a B-Hyve
|
||||||
|
* device.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveDevice {
|
||||||
|
String name = "";
|
||||||
|
String type = "";
|
||||||
|
String id = "";
|
||||||
|
List<OrbitBhyveZone> zones = new ArrayList<>();
|
||||||
|
OrbitBhyveDeviceStatus status = new OrbitBhyveDeviceStatus();
|
||||||
|
|
||||||
|
@SerializedName("is_connected")
|
||||||
|
boolean isConnected = false;
|
||||||
|
|
||||||
|
@SerializedName("hardware_version")
|
||||||
|
String hwVersion = "";
|
||||||
|
|
||||||
|
@SerializedName("firmware_version")
|
||||||
|
String fwVersion = "";
|
||||||
|
|
||||||
|
@SerializedName("mac_address")
|
||||||
|
String macAddress = "";
|
||||||
|
|
||||||
|
@SerializedName("num_stations")
|
||||||
|
int numStations = 0;
|
||||||
|
|
||||||
|
@SerializedName("last_connected_at")
|
||||||
|
String lastConnectedAt = "";
|
||||||
|
|
||||||
|
JsonObject location = new JsonObject();
|
||||||
|
|
||||||
|
@SerializedName("restricted_frequency")
|
||||||
|
JsonObject restrictedFrequency = new JsonObject();
|
||||||
|
|
||||||
|
@SerializedName("suggested_start_time")
|
||||||
|
String suggestedStartTime = "";
|
||||||
|
|
||||||
|
JsonObject timezone = new JsonObject();
|
||||||
|
|
||||||
|
@SerializedName("water_sense_mode")
|
||||||
|
String waterSenseMode = "";
|
||||||
|
|
||||||
|
@SerializedName("wifi_version")
|
||||||
|
int wifiVersion = 0;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHwVersion() {
|
||||||
|
return hwVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFwVersion() {
|
||||||
|
return fwVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMacAddress() {
|
||||||
|
return macAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumStations() {
|
||||||
|
return numStations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OrbitBhyveZone> getZones() {
|
||||||
|
return zones;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrbitBhyveDeviceStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWaterSenseMode() {
|
||||||
|
return waterSenseMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWaterSenseMode(String waterSenseMode) {
|
||||||
|
this.waterSenseMode = waterSenseMode;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.model;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveDeviceStatus} holds information about a B-Hyve
|
||||||
|
* device status.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveDeviceStatus {
|
||||||
|
@SerializedName("run_mode")
|
||||||
|
String mode = "";
|
||||||
|
|
||||||
|
@SerializedName("next_start_time")
|
||||||
|
String nextStartTime = "";
|
||||||
|
|
||||||
|
@SerializedName("rain_delay")
|
||||||
|
int delay = 0;
|
||||||
|
|
||||||
|
@SerializedName("rain_delay_started_at")
|
||||||
|
String rainDelayStartedAt = "";
|
||||||
|
|
||||||
|
public String getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNextStartTime() {
|
||||||
|
return nextStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDelay() {
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.model;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveProgram} holds information about a B-Hyve
|
||||||
|
* device programs.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveProgram {
|
||||||
|
@SerializedName("device_id")
|
||||||
|
String deviceId = "";
|
||||||
|
|
||||||
|
String program = "";
|
||||||
|
String name = "";
|
||||||
|
String id = "";
|
||||||
|
boolean enabled = false;
|
||||||
|
|
||||||
|
public String getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgram() {
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.model;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveSessionResponse} holds information about a B-Hyve
|
||||||
|
* session response.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveSessionResponse {
|
||||||
|
@SerializedName("orbit_session_token")
|
||||||
|
String orbitSessionToken = "";
|
||||||
|
|
||||||
|
public String getOrbitSessionToken() {
|
||||||
|
return orbitSessionToken;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.model;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveSocketEvent} holds information about a B-Hyve
|
||||||
|
* event received on web socket.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveSocketEvent {
|
||||||
|
String event = "";
|
||||||
|
String mode = "";
|
||||||
|
JsonElement program = new JsonObject();
|
||||||
|
int delay = 0;
|
||||||
|
|
||||||
|
@SerializedName("device_id")
|
||||||
|
String deviceId = "";
|
||||||
|
|
||||||
|
@SerializedName("current_station")
|
||||||
|
int station = 0;
|
||||||
|
|
||||||
|
public String getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStation() {
|
||||||
|
return station;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement getProgram() {
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDelay() {
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.model;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveZone} holds information about a B-Hyve
|
||||||
|
* zone.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveZone {
|
||||||
|
String name = "";
|
||||||
|
int station = 0;
|
||||||
|
|
||||||
|
@SerializedName("catch_cup_run_time")
|
||||||
|
int catchCupRunTime = 0;
|
||||||
|
|
||||||
|
@SerializedName("catch_cup_volumes")
|
||||||
|
JsonArray catchCupVolumes = new JsonArray();
|
||||||
|
|
||||||
|
@SerializedName("num_sprinklers")
|
||||||
|
int numSprinklers = 0;
|
||||||
|
|
||||||
|
@SerializedName("landscape_type")
|
||||||
|
@Nullable
|
||||||
|
String landscapeType;
|
||||||
|
|
||||||
|
@SerializedName("soil_type")
|
||||||
|
@Nullable
|
||||||
|
String soilType;
|
||||||
|
|
||||||
|
@SerializedName("sprinkler_type")
|
||||||
|
@Nullable
|
||||||
|
String sprinklerType;
|
||||||
|
|
||||||
|
@SerializedName("sun_shade")
|
||||||
|
@Nullable
|
||||||
|
String sunShade;
|
||||||
|
|
||||||
|
@SerializedName("slope_grade")
|
||||||
|
int slopeGrade = 0;
|
||||||
|
|
||||||
|
@SerializedName("image_url")
|
||||||
|
String imageUrl = "";
|
||||||
|
|
||||||
|
@SerializedName("smart_watering_enabled")
|
||||||
|
boolean smartWateringEnabled = false;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStation() {
|
||||||
|
return station;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSmartWateringEnabled() {
|
||||||
|
return smartWateringEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmartWateringEnabled(boolean smartWateringEnabled) {
|
||||||
|
this.smartWateringEnabled = smartWateringEnabled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.orbitbhyve.internal.net;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||||
|
import org.openhab.binding.orbitbhyve.internal.handler.OrbitBhyveBridgeHandler;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link OrbitBhyveSocket} class defines websocket used for connection with
|
||||||
|
* the Orbit B-Hyve cloud.
|
||||||
|
*
|
||||||
|
* @author Ondrej Pecta - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class OrbitBhyveSocket extends WebSocketAdapter {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(OrbitBhyveSocket.class);
|
||||||
|
private OrbitBhyveBridgeHandler handler;
|
||||||
|
|
||||||
|
public OrbitBhyveSocket(OrbitBhyveBridgeHandler handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketText(@Nullable String message) {
|
||||||
|
super.onWebSocketText(message);
|
||||||
|
if (message != null) {
|
||||||
|
logger.trace("Got message: {}", message);
|
||||||
|
handler.processStatusResponse(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<binding:binding id="orbitbhyve" 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>Orbit B-hyve Binding</name>
|
||||||
|
<description>This is the binding for Orbit B-hyve Wi-Fi irrigation systems.</description>
|
||||||
|
|
||||||
|
</binding:binding>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<config-description:config-descriptions
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<config-description uri="bridge-type:orbitbhyve:bridge">
|
||||||
|
<parameter name="email" type="text" required="true">
|
||||||
|
<label>Email</label>
|
||||||
|
<context>email</context>
|
||||||
|
<description>This is a login to your B-hyve account.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="password" type="text" required="true">
|
||||||
|
<label>Password</label>
|
||||||
|
<context>password</context>
|
||||||
|
<description>This is a password to your B-hyve account.</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="refresh" type="integer" required="false" min="10">
|
||||||
|
<label>Refresh</label>
|
||||||
|
<description>Specifies the refresh time in seconds for polling data from Orbit cloud</description>
|
||||||
|
<default>30</default>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
|
||||||
|
<config-description uri="thing-type:orbitbhyve:sprinkler">
|
||||||
|
<parameter name="id" type="text" required="true">
|
||||||
|
<label>Sprinkler Device ID</label>
|
||||||
|
<description>The identifier of the Orbit sprinkler device</description>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</config-description:config-descriptions>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="orbitbhyve"
|
||||||
|
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 -->
|
||||||
|
<bridge-type id="bridge">
|
||||||
|
<label>Bridge</label>
|
||||||
|
<description>Bridge for Orbit B-hyve Binding</description>
|
||||||
|
<config-description-ref uri="bridge-type:orbitbhyve:bridge"/>
|
||||||
|
</bridge-type>
|
||||||
|
|
||||||
|
</thing:thing-descriptions>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="orbitbhyve"
|
||||||
|
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">
|
||||||
|
|
||||||
|
<!-- Mode Channel Type -->
|
||||||
|
<channel-type id="mode">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Irrigation Mode</label>
|
||||||
|
<description>Channel representing mode of Orbit B-hyve Device (auto/manual)</description>
|
||||||
|
<state readOnly="true">
|
||||||
|
<options>
|
||||||
|
<option value="auto">Auto</option>
|
||||||
|
<option value="manual">Manual</option>
|
||||||
|
</options>
|
||||||
|
</state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="next_start">
|
||||||
|
<item-type>DateTime</item-type>
|
||||||
|
<label>Next Watering Time</label>
|
||||||
|
<description>Channel representing start time of the next watering</description>
|
||||||
|
<state readOnly="true"></state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="rain_delay">
|
||||||
|
<item-type>Number:Time</item-type>
|
||||||
|
<label>Rain Delay</label>
|
||||||
|
<description>Channel representing rain delay in hours</description>
|
||||||
|
<state pattern="%d h"></state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="watering_time">
|
||||||
|
<item-type>Number:Time</item-type>
|
||||||
|
<label>Zone Watering Time</label>
|
||||||
|
<description>Channel representing the manual zone watering time in minutes</description>
|
||||||
|
<state max="240" min="0" pattern="%d min"></state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="control">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Sprinkler State Control</label>
|
||||||
|
<description>Channel for enabling/disabling the sprinkler (ON/OFF)</description>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="smart_watering">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Smart Watering Control</label>
|
||||||
|
<description>Channel for enabling/disabling the smart watering mode</description>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="program">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Program Channel</label>
|
||||||
|
<description>Dynamic channel representing a program</description>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="zone">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Zone Channel</label>
|
||||||
|
<description>Dynamic channel representing a zone</description>
|
||||||
|
</channel-type>
|
||||||
|
</thing:thing-descriptions>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="orbitbhyve"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<thing-type id="sprinkler">
|
||||||
|
<supported-bridge-type-refs>
|
||||||
|
<bridge-type-ref id="bridge"/>
|
||||||
|
</supported-bridge-type-refs>
|
||||||
|
<label>Sprinkler</label>
|
||||||
|
<description>Orbit B-hyve Sprinkler</description>
|
||||||
|
<channels>
|
||||||
|
<channel id="control" typeId="control"/>
|
||||||
|
<channel id="mode" typeId="mode"/>
|
||||||
|
<channel id="smart_watering" typeId="smart_watering"/>
|
||||||
|
<channel id="next_start" typeId="next_start"/>
|
||||||
|
<channel id="rain_delay" typeId="rain_delay"/>
|
||||||
|
<channel id="watering_time" typeId="watering_time"/>
|
||||||
|
</channels>
|
||||||
|
<config-description-ref uri="thing-type:orbitbhyve:sprinkler"/>
|
||||||
|
</thing-type>
|
||||||
|
|
||||||
|
</thing:thing-descriptions>
|
|
@ -254,6 +254,7 @@
|
||||||
<module>org.openhab.binding.openweathermap</module>
|
<module>org.openhab.binding.openweathermap</module>
|
||||||
<module>org.openhab.binding.openwebnet</module>
|
<module>org.openhab.binding.openwebnet</module>
|
||||||
<module>org.openhab.binding.oppo</module>
|
<module>org.openhab.binding.oppo</module>
|
||||||
|
<module>org.openhab.binding.orbitbhyve</module>
|
||||||
<module>org.openhab.binding.orvibo</module>
|
<module>org.openhab.binding.orvibo</module>
|
||||||
<module>org.openhab.binding.paradoxalarm</module>
|
<module>org.openhab.binding.paradoxalarm</module>
|
||||||
<module>org.openhab.binding.pentair</module>
|
<module>org.openhab.binding.pentair</module>
|
||||||
|
|
Loading…
Reference in New Issue