mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[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.openwebnet/ @mvalla
|
||||
/bundles/org.openhab.binding.oppo/ @mlobstein
|
||||
/bundles/org.openhab.binding.orbitbhyve/ @octa22
|
||||
/bundles/org.openhab.binding.orvibo/ @tavalin
|
||||
/bundles/org.openhab.binding.paradoxalarm/ @theater
|
||||
/bundles/org.openhab.binding.pentair/ @jsjames
|
||||
|
@ -1091,6 +1091,11 @@
|
||||
<artifactId>org.openhab.binding.oppo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.orbitbhyve</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.orvibo</artifactId>
|
||||
|
13
bundles/org.openhab.binding.orbitbhyve/NOTICE
Normal file
13
bundles/org.openhab.binding.orbitbhyve/NOTICE
Normal file
@ -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
|
84
bundles/org.openhab.binding.orbitbhyve/README.md
Normal file
84
bundles/org.openhab.binding.orbitbhyve/README.md
Normal file
@ -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]
|
||||
```
|
17
bundles/org.openhab.binding.orbitbhyve/pom.xml
Normal file
17
bundles/org.openhab.binding.orbitbhyve/pom.xml
Normal file
@ -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.openwebnet</module>
|
||||
<module>org.openhab.binding.oppo</module>
|
||||
<module>org.openhab.binding.orbitbhyve</module>
|
||||
<module>org.openhab.binding.orvibo</module>
|
||||
<module>org.openhab.binding.paradoxalarm</module>
|
||||
<module>org.openhab.binding.pentair</module>
|
||||
|
Loading…
Reference in New Issue
Block a user