added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
54
bundles/org.openhab.binding.iaqualink/.classpath
Normal file
54
bundles/org.openhab.binding.iaqualink/.classpath
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path=".apt_generated">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path=".apt_generated_tests">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="target/generated-sources/annotations">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.iaqualink/.project
Normal file
23
bundles/org.openhab.binding.iaqualink/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.iaqualink</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.iaqualink/NOTICE
Normal file
13
bundles/org.openhab.binding.iaqualink/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
|
||||
121
bundles/org.openhab.binding.iaqualink/README.md
Normal file
121
bundles/org.openhab.binding.iaqualink/README.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# iAquaLink Binding
|
||||
|
||||
This binding supports:
|
||||
|
||||
* Any iAquaLink based pool system
|
||||
* Reading auxiliary, temperature, pump, chemistry and system values
|
||||
* Controlling system, auxiliary, lighting, and temperature settings
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
The binding requires the iAquaLink user name and password.
|
||||
If you have more then one pool system registered to an account, you may optionally specify the pool serial ID/Number to use, otherwise the first pool controller will be used.
|
||||
|
||||
|
||||
## Manual Thing Configuration
|
||||
|
||||
```
|
||||
Thing iaqualink:controller:pool [ userName="user@domain.com", password="somepassword"]
|
||||
```
|
||||
|
||||
## Channels
|
||||
|
||||
|
||||
The following is a list of supported channels.
|
||||
Auxiliary and OneTouch channels will be dynamically added depending on what a system reports as being supported.
|
||||
|
||||
Auxiliary channels that are of a number type represent lighting modes (typically 0-15).
|
||||
Auxiliary channels that are dimmer types can set the light value in increments of 25 (0,25,50,750,100).
|
||||
The Auxiliary channel type will be dynamically assigned based on the controller configuration.
|
||||
|
||||
Heater status can be OFF (0), Enabled/ON (3), or Heating (1).
|
||||
|
||||
| Channel Type ID | Item Type |
|
||||
|---------------------|----------------------------|
|
||||
| status | String |
|
||||
| system_type | Number |
|
||||
| temp_scale | String |
|
||||
| spa_temp | Number:Temperature |
|
||||
| pool_temp | Number:Temperature |
|
||||
| air_temp | Number:Temperature |
|
||||
| spa_set_point | Number:Temperature |
|
||||
| pool_set_point | Number:Temperature |
|
||||
| cover_pool | Switch |
|
||||
| freeze_protection | Switch |
|
||||
| spa_pump | Switch |
|
||||
| pool_pump | Switch |
|
||||
| spa_heater | Switch |
|
||||
| pool_heater | Switch |
|
||||
| solar_heater | Switch |
|
||||
| spa_heater_status | Number |
|
||||
| pool_heater_status | Number |
|
||||
| solar_heater_status | Number |
|
||||
| spa_salinity | Number |
|
||||
| pool_salinity | Number |
|
||||
| orp | Number |
|
||||
| ph | Number |
|
||||
| onetouch_1 | Switch |
|
||||
| onetouch_n+1 | Switch |
|
||||
| aux_1 | Switch or String or Dimmer |
|
||||
| aux_n+1 | Switch or String or Dimmer |
|
||||
|
||||
### Color/Mood Auxiliary Channels
|
||||
|
||||
String auxiliary channels can control a variety of lighting moods/colors depending on what type of lighting system is installed.
|
||||
The following is a table of aux_n channel values (String) to lighting set descriptions values.
|
||||
The binding will automatically detect which color system is enabled and add the appropriate channel type with the following option labels.
|
||||
Colors can be set, but only On or Off is reported back as the current state of the channel.
|
||||
|
||||
| String Value | jandy Color | Jandy LED Water Colors | Pentair SAm/SAL | Hayward Universal | Pentair intelliBrite |
|
||||
|--------------|----------------|------------------------|-----------------|-------------------|----------------------|
|
||||
| "off" | Off | Off | Off | Off | Off |
|
||||
| "on" | On | On | On | On | On |
|
||||
| "1" | Alpine White | Alpine White | White | Voodoo Lounge | SAM |
|
||||
| "2" | Sky Blue | Sky Blue | Light Green | Deep Blue Sea | Party |
|
||||
| "3" | Cobalt Blue | Cobalt Blue | Green | Afternoon Skies | Romance |
|
||||
| "4" | Caribbean Blue | Caribbean Blue | Cyan | Emerald | Caribbean |
|
||||
| "5" | Spring Green | Spring Green | Blue | Sangria | American |
|
||||
| "6" | Emerald Green | Emerald Green | Lavender | Cloud White | Cal Sunset |
|
||||
| "7" | Emerald Rose | Emerald Rose | Magenta | Twilight | Royal |
|
||||
| "8" | Magenta | Magenta | Light Magenta | Tranquility | Blue |
|
||||
| "9" | Garnet Red | Violet | Color Splash | Gemstone | Green |
|
||||
| "10" | Violet | Slow Splash | | USA! | Red |
|
||||
| "11" | Color Splash | Fast Splash | | Mardi Gras | White |
|
||||
| "12" | | USA!!! | | Cool Caberet | Magenta |
|
||||
| "13" | | Fat Tuesday | | | Hold |
|
||||
| "14" | | Disco Tech | | | Recall |
|
||||
|
||||
|
||||
|
||||
|
||||
## Sample Items
|
||||
|
||||
```
|
||||
Group Group_AquaLink
|
||||
String AquaLinkStatus "Status [%s]" (Group_AquaLink) {channel="iaqualink:controller:pool:status"}
|
||||
Switch AquaLinkBoosterPump "Booster Pump" (Group_AquaLink) {channel="iaqualink:controller:pool:aux_1"}
|
||||
Switch AquaLinkPoolLight "Pool Light" (Group_AquaLink) {channel="iaqualink:controller:pool:aux_2"}
|
||||
Switch AquaLinkSpaLight "Spa Light" (Group_AquaLink) {channel="iaqualink:controller:pool:aux_3"}
|
||||
Switch AquaLinkVanishingEdge "Vanishing Edge" (Group_AquaLink) {channel="iaqualink:controller:pool:aux_4"}
|
||||
|
||||
Switch AquaLinkAllOffOneTouch "All Off" (Group_AquaLink) {channel="iaqualink:controller:pool:onetouch_1"}
|
||||
Switch AquaLinkSpaOneTouch "Spa Mode" (Group_AquaLink) {channel="iaqualink:controller:pool:onetouch_2"}
|
||||
Switch AquaLinkCleanOneTouch "Clean Mode" (Group_AquaLink) {channel="iaqualink:controller:pool:onetouch_3"}
|
||||
Switch AquaLinkPoolOneTouch "Pool Mode" (Group_AquaLink) {channel="iaqualink:controller:pool:onetouch_4"}
|
||||
|
||||
Number:Temperature AquaLinkSpaTemp "Spa Temperature [%d]" (Group_AquaLink) {channel="iaqualink:controller:pool:spa_temp"}
|
||||
Number:Temperature AquaLinkPoolTemp "Pool Temperature [%d]" (Group_AquaLink) {channel="iaqualink:controller:pool:pool_temp"}
|
||||
Number:Temperature AquaLinkAirTemp "Air Temperature [%d]" (Group_AquaLink) {channel="iaqualink:controller:pool:air_temp"}
|
||||
|
||||
Number:Temperature AquaLinkSpaSetpoint "Spa Setpoint [%d]" (Group_AquaLink) {channel="iaqualink:controller:pool:spa_set_point"}
|
||||
Number:Temperature AquaLinkPoolSetpoint "Pool Setpoint [%d]" (Group_AquaLink) {channel="iaqualink:controller:pool:pool_set_point"}
|
||||
|
||||
Switch AquaLinkSpaPump "Spa Pump" (Group_AquaLink) {channel="iaqualink:controller:pool:spa_pump"}
|
||||
Switch AquaLinkPoolPump"Pool Pump" (Group_AquaLink) {channel="iaqualink:controller:pool:pool_pump"}
|
||||
|
||||
Number AquaLinkSpaHeaterStatus "Spa Heater [%s]" (Group_AquaLink) {channel="iaqualink:controller:pool:spa_heater_status"}
|
||||
Number AquaLinkPoolHeaterStatus "Pool Heater [%s]" (Group_AquaLink) {channel="iaqualink:controller:pool:pool_heater_status"}
|
||||
|
||||
Switch AquaLinkSpaHeater "Spa Heater" (Group_AquaLink) {channel="iaqualink:controller:pool:spa_heater"}
|
||||
Switch AquaLinkPoolHeater "Pool Heater" (Group_AquaLink) {channel="iaqualink:controller:pool:pool_heater"}
|
||||
```
|
||||
17
bundles/org.openhab.binding.iaqualink/pom.xml
Normal file
17
bundles/org.openhab.binding.iaqualink/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.iaqualink</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: iAquaLink Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.iaqualink-${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-iaqualink" description="iAqualink Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.iaqualink/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link IAqualinkBindingConstants} class defines common constants, which are used
|
||||
* across the whole binding.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class IAqualinkBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "iaqualink";
|
||||
public static final String CHANNEL_TYPE_AUX_SWITCH = "aux-switch";
|
||||
public static final String CHANNEL_TYPE_AUX_DIMMER = "aux-dimmer";
|
||||
public static final String CHANNEL_TYPE_AUX_JANDYCOLOR = "aux-jandycolor";
|
||||
public static final String CHANNEL_TYPE_AUX_PENTAIRSAM = "aux-pentairsam";
|
||||
public static final String CHANNEL_TYPE_AUX_JANDYLED = "aux-jandyled";
|
||||
public static final String CHANNEL_TYPE_AUX_PENTAIRIB = "aux-pentairib";
|
||||
public static final String CHANNEL_TYPE_AUX_HAYWARD = "aux-hayward";
|
||||
|
||||
public static final String CHANNEL_TYPE_ONETOUCH = "onetouch";
|
||||
|
||||
public static final ThingTypeUID IAQUALINK_DEVICE_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "controller");
|
||||
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_ONETOUCH = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_ONETOUCH);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_AUX_SWITCH = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_AUX_SWITCH);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_AUX_DIMMER = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_AUX_DIMMER);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_AUX_JANDYCOLOR = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_AUX_JANDYCOLOR);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_AUX_PENTAIRSAM = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_AUX_PENTAIRSAM);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_AUX_JANDYLED = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_AUX_JANDYLED);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_AUX_PENTAIRIB = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_AUX_PENTAIRIB);
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_UID_AUX_HAYWARD = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_TYPE_AUX_HAYWARD);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal;
|
||||
|
||||
import static org.openhab.binding.iaqualink.internal.IAqualinkBindingConstants.IAQUALINK_DEVICE_THING_TYPE_UID;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.iaqualink.internal.handler.IAqualinkHandler;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link IAqualinkHandlerFactory} is responsible for creating things and
|
||||
* thing handlers.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.iaqualink")
|
||||
public class IAqualinkHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
@Activate
|
||||
public IAqualinkHandlerFactory(@Reference final HttpClientFactory httpClientFactory) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return IAQUALINK_DEVICE_THING_TYPE_UID.equals(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (IAQUALINK_DEVICE_THING_TYPE_UID.equals(thingTypeUID)) {
|
||||
return new IAqualinkHandler(thing, httpClient);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
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.util.StringContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.AccountInfo;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.Auxiliary;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.Device;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.Home;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.OneTouch;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.SignIn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
/**
|
||||
* IAqualink HTTP Client
|
||||
*
|
||||
* The {@link IAqualinkClient} provides basic HTTP commands to control and monitor a iAquaLink
|
||||
* based system.
|
||||
*
|
||||
* GSON is used to provide custom deserialization on the JSON results. These results
|
||||
* unfortunately are not returned as normalized JSON objects and require complex deserialization
|
||||
* handlers.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class IAqualinkClient {
|
||||
private final Logger logger = LoggerFactory.getLogger(IAqualinkClient.class);
|
||||
|
||||
private static final String HEADER_AGENT = "iAqualink/98 CFNetwork/978.0.7 Darwin/18.6.0";
|
||||
private static final String HEADER_ACCEPT = "*/*";
|
||||
private static final String HEADER_ACCEPT_LANGUAGE = "en-us";
|
||||
private static final String HEADER_ACCEPT_ENCODING = "br, gzip, deflate";
|
||||
|
||||
private static final String SUPPORT_URL = "https://support.iaqualink.com";
|
||||
private static final String IAQUALINK_BASE_URL = "https://p-api.iaqualink.net/v1/mobile/session.json";
|
||||
|
||||
private Gson gson = new GsonBuilder().registerTypeAdapter(Home.class, new HomeDeserializer())
|
||||
.registerTypeAdapter(OneTouch[].class, new OneTouchDeserializer())
|
||||
.registerTypeAdapter(Auxiliary[].class, new AuxDeserializer())
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||
private Gson gsonInternal = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.create();
|
||||
private HttpClient httpClient;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class NotAuthorizedException extends Exception {
|
||||
public NotAuthorizedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param httpClient
|
||||
*/
|
||||
public IAqualinkClient(HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial login to service
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
* @param apiKey
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public AccountInfo login(@Nullable String username, @Nullable String password, @Nullable String apiKey)
|
||||
throws IOException, NotAuthorizedException {
|
||||
String signIn = gson.toJson(new SignIn(apiKey, username, password)).toString();
|
||||
try {
|
||||
ContentResponse response = httpClient.newRequest(SUPPORT_URL + "/users/sign_in.json")
|
||||
.method(HttpMethod.POST).content(new StringContentProvider(signIn), "application/json").send();
|
||||
if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
|
||||
throw new NotAuthorizedException(response.getReason());
|
||||
}
|
||||
if (response.getStatus() != HttpStatus.OK_200) {
|
||||
throw new IOException(response.getReason());
|
||||
}
|
||||
return gson.fromJson(response.getContentAsString(), AccountInfo.class);
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all devices (pools) registered to an account
|
||||
*
|
||||
* @param apiKey
|
||||
* @param token
|
||||
* @param id
|
||||
* @return {@link Device[]}
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Device[] getDevices(@Nullable String apiKey, @Nullable String token, int id)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(UriBuilder.fromUri(SUPPORT_URL + "/devices.json"). //
|
||||
queryParam("api_key", apiKey). //
|
||||
queryParam("authentication_token", token). //
|
||||
queryParam("user_id", id).build(), Device[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the HomeScreen
|
||||
*
|
||||
* @param serialNumber
|
||||
* @param sessionId
|
||||
* @return {@link Home}
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Home getHome(@Nullable String serialNumber, @Nullable String sessionId)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return homeScreenCommand(serialNumber, sessionId, "get_home");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves {@link OneTouch[]} macros
|
||||
*
|
||||
* @param serialNumber
|
||||
* @param sessionId
|
||||
* @return {@link OneTouch[]}
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public OneTouch[] getOneTouch(@Nullable String serialNumber, @Nullable String sessionId)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return oneTouchCommand(serialNumber, sessionId, "get_onetouch");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves {@link Auxiliary[]} devices
|
||||
*
|
||||
* @param serialNumber
|
||||
* @param sessionId
|
||||
* @return {@link Auxiliary[]}
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Auxiliary[] getAux(@Nullable String serial, @Nullable String sessionID)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return auxCommand(serial, sessionID, "get_devices");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a HomeScreen Set command
|
||||
*
|
||||
* @param serial
|
||||
* @param sessionID
|
||||
* @param homeElementID
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Home homeScreenSetCommand(@Nullable String serial, @Nullable String sessionID, String homeElementID)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return homeScreenCommand(serial, sessionID, "set_" + homeElementID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an Auxiliary Set command
|
||||
*
|
||||
* @param serial
|
||||
* @param sessionID
|
||||
* @param auxID
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Auxiliary[] auxSetCommand(@Nullable String serial, @Nullable String sessionID, String auxID)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return auxCommand(serial, sessionID, "set_" + auxID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an Auxiliary light command
|
||||
*
|
||||
* @param serialNumber
|
||||
* @param sessionId
|
||||
* @param command
|
||||
* @param lightValue
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Auxiliary[] lightCommand(@Nullable String serial, @Nullable String sessionID, String auxID,
|
||||
String lightValue, String subType) throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(baseURI(). //
|
||||
queryParam("aux", auxID). //
|
||||
queryParam("command", "set_light"). //
|
||||
queryParam("light", lightValue). //
|
||||
queryParam("serial", serial). //
|
||||
queryParam("subtype", subType). //
|
||||
queryParam("sessionID", sessionID).build(), Auxiliary[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Auxiliary dimmer command
|
||||
*
|
||||
* @param serialNumber
|
||||
* @param sessionId
|
||||
* @param auxId
|
||||
* @param lightValue
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Auxiliary[] dimmerCommand(@Nullable String serial, @Nullable String sessionID, String auxID, String level)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(baseURI().queryParam("aux", auxID). //
|
||||
queryParam("command", "set_dimmer"). //
|
||||
queryParam("level", level). //
|
||||
queryParam("serial", serial). //
|
||||
queryParam("sessionID", sessionID).build(), Auxiliary[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Spa Temperature Setpoint
|
||||
*
|
||||
* @param serialNumber
|
||||
* @param sessionId
|
||||
* @param spaSetpoint
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Home setSpaTemp(@Nullable String serial, @Nullable String sessionID, float spaSetpoint)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(baseURI(). //
|
||||
queryParam("command", "set_temps"). //
|
||||
queryParam("temp1", spaSetpoint). //
|
||||
queryParam("serial", serial). //
|
||||
queryParam("sessionID", sessionID).build(), Home.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Pool Temperature Setpoint
|
||||
*
|
||||
* @param serialNumber
|
||||
* @param sessionId
|
||||
* @param poolSetpoint
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public Home setPoolTemp(@Nullable String serial, @Nullable String sessionID, float poolSetpoint)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(baseURI(). //
|
||||
queryParam("command", "set_temps"). //
|
||||
queryParam("temp2", poolSetpoint). //
|
||||
queryParam("serial", serial). //
|
||||
queryParam("sessionID", sessionID).build(), Home.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a OneTouch set command
|
||||
*
|
||||
* @param serial
|
||||
* @param sessionID
|
||||
* @param oneTouchID
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
public OneTouch[] oneTouchSetCommand(@Nullable String serial, @Nullable String sessionID, String oneTouchID)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return oneTouchCommand(serial, sessionID, "set_" + oneTouchID);
|
||||
}
|
||||
|
||||
private Home homeScreenCommand(@Nullable String serial, @Nullable String sessionID, String command)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(baseURI().queryParam("command", command). //
|
||||
queryParam("serial", serial). //
|
||||
queryParam("sessionID", sessionID).build(), Home.class);
|
||||
}
|
||||
|
||||
private Auxiliary[] auxCommand(@Nullable String serial, @Nullable String sessionID, String command)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(baseURI(). //
|
||||
queryParam("command", command). //
|
||||
queryParam("serial", serial). //
|
||||
queryParam("sessionID", sessionID).build(), Auxiliary[].class);
|
||||
}
|
||||
|
||||
private OneTouch[] oneTouchCommand(@Nullable String serial, @Nullable String sessionID, String command)
|
||||
throws IOException, NotAuthorizedException {
|
||||
return getAqualinkObject(baseURI().queryParam("command", command). //
|
||||
queryParam("serial", serial). //
|
||||
queryParam("sessionID", sessionID).build(), OneTouch[].class);
|
||||
}
|
||||
|
||||
private UriBuilder baseURI() {
|
||||
return UriBuilder.fromUri(IAQUALINK_BASE_URL).queryParam("actionID", "command");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T>
|
||||
* @param url
|
||||
* @param typeOfT
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
private <T> T getAqualinkObject(URI uri, Type typeOfT) throws IOException, NotAuthorizedException {
|
||||
return gson.fromJson(getRequest(uri), typeOfT);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws NotAuthorizedException
|
||||
*/
|
||||
private String getRequest(URI uri) throws IOException, NotAuthorizedException {
|
||||
try {
|
||||
logger.trace("Trying {}", uri);
|
||||
ContentResponse response = httpClient.newRequest(uri).method(HttpMethod.GET) //
|
||||
.agent(HEADER_AGENT) //
|
||||
.header(HttpHeader.ACCEPT_LANGUAGE, HEADER_ACCEPT_LANGUAGE) //
|
||||
.header(HttpHeader.ACCEPT_ENCODING, HEADER_ACCEPT_ENCODING) //
|
||||
.header(HttpHeader.ACCEPT, HEADER_ACCEPT) //
|
||||
.send();
|
||||
logger.trace("Response {}", response);
|
||||
if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
|
||||
throw new NotAuthorizedException(response.getReason());
|
||||
}
|
||||
if (response.getStatus() != HttpStatus.OK_200) {
|
||||
throw new IOException(response.getReason());
|
||||
}
|
||||
return response.getContentAsString();
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException | JsonParseException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////// .........Here be dragons...../////////////////////////
|
||||
|
||||
class HomeDeserializer implements JsonDeserializer<Home> {
|
||||
@Override
|
||||
public Home deserialize(@Nullable JsonElement json, @Nullable Type typeOfT,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json == null) {
|
||||
throw new JsonParseException("No JSON");
|
||||
}
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonArray homeScreen = jsonObject.getAsJsonArray("home_screen");
|
||||
JsonObject home = new JsonObject();
|
||||
JsonObject serializedMap = new JsonObject();
|
||||
if (homeScreen != null) {
|
||||
homeScreen.forEach(element -> {
|
||||
element.getAsJsonObject().entrySet().forEach(entry -> {
|
||||
home.add(entry.getKey(), entry.getValue());
|
||||
serializedMap.add(entry.getKey(), entry.getValue());
|
||||
});
|
||||
});
|
||||
home.add("serialized_map", serializedMap);
|
||||
return gsonInternal.fromJson(home, Home.class);
|
||||
}
|
||||
throw new JsonParseException("Invalid structure for Home class");
|
||||
}
|
||||
}
|
||||
|
||||
class OneTouchDeserializer implements JsonDeserializer<OneTouch[]> {
|
||||
@Override
|
||||
public OneTouch[] deserialize(@Nullable JsonElement json, @Nullable Type typeOfT,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json == null) {
|
||||
throw new JsonParseException("No JSON");
|
||||
}
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonArray oneTouchScreen = jsonObject.getAsJsonArray("onetouch_screen");
|
||||
List<OneTouch> list = new ArrayList<>();
|
||||
if (oneTouchScreen != null) {
|
||||
oneTouchScreen.forEach(oneTouchScreenElement -> {
|
||||
oneTouchScreenElement.getAsJsonObject().entrySet().forEach(oneTouchScreenEntry -> {
|
||||
if (oneTouchScreenEntry.getKey().startsWith("onetouch_")) {
|
||||
JsonArray oneTouchArray = oneTouchScreenEntry.getValue().getAsJsonArray();
|
||||
if (oneTouchArray != null) {
|
||||
JsonObject oneTouchJson = new JsonObject();
|
||||
oneTouchJson.add("name", new JsonPrimitive(oneTouchScreenEntry.getKey()));
|
||||
oneTouchArray.forEach(arrayElement -> {
|
||||
arrayElement.getAsJsonObject().entrySet().forEach(oneTouchEntry -> {
|
||||
oneTouchJson.add(oneTouchEntry.getKey(), oneTouchEntry.getValue());
|
||||
});
|
||||
});
|
||||
list.add(gsonInternal.fromJson(oneTouchJson, OneTouch.class));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return list.toArray(new OneTouch[list.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
class AuxDeserializer implements JsonDeserializer<Auxiliary[]> {
|
||||
@Override
|
||||
public Auxiliary[] deserialize(@Nullable JsonElement json, @Nullable Type typeOfT,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json == null) {
|
||||
throw new JsonParseException("No JSON");
|
||||
}
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonArray auxScreen = jsonObject.getAsJsonArray("devices_screen");
|
||||
List<Auxiliary> list = new ArrayList<>();
|
||||
if (auxScreen != null) {
|
||||
auxScreen.forEach(auxElement -> {
|
||||
auxElement.getAsJsonObject().entrySet().forEach(auxScreenEntry -> {
|
||||
if (auxScreenEntry.getKey().startsWith("aux_")) {
|
||||
JsonArray auxArray = auxScreenEntry.getValue().getAsJsonArray();
|
||||
if (auxArray != null) {
|
||||
JsonObject auxJson = new JsonObject();
|
||||
auxJson.add("name", new JsonPrimitive(auxScreenEntry.getKey()));
|
||||
auxArray.forEach(arrayElement -> {
|
||||
arrayElement.getAsJsonObject().entrySet().forEach(auxEntry -> {
|
||||
auxJson.add(auxEntry.getKey(), auxEntry.getValue());
|
||||
});
|
||||
});
|
||||
list.add(gsonInternal.fromJson(auxJson, Auxiliary.class));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return list.toArray(new Auxiliary[list.size()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.api.model;
|
||||
|
||||
/**
|
||||
* Account Info Object
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class AccountInfo {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String email;
|
||||
|
||||
private String createdAt;
|
||||
|
||||
private String updatedAt;
|
||||
|
||||
private Object timeZone;
|
||||
|
||||
private String firstName;
|
||||
|
||||
private String lastName;
|
||||
|
||||
private String address1;
|
||||
|
||||
private String address2;
|
||||
|
||||
private String city;
|
||||
|
||||
private String state;
|
||||
|
||||
private String postalCode;
|
||||
|
||||
private String country;
|
||||
|
||||
private String phone;
|
||||
|
||||
private Boolean optIn1;
|
||||
|
||||
private Boolean optIn2;
|
||||
|
||||
private String authenticationToken;
|
||||
|
||||
private String role;
|
||||
|
||||
private String sessionId;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(String createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public String getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(String updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Object getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
public void setTimeZone(Object timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getAddress1() {
|
||||
return address1;
|
||||
}
|
||||
|
||||
public void setAddress1(String address1) {
|
||||
this.address1 = address1;
|
||||
}
|
||||
|
||||
public String getAddress2() {
|
||||
return address2;
|
||||
}
|
||||
|
||||
public void setAddress2(String address2) {
|
||||
this.address2 = address2;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getPostalCode() {
|
||||
return postalCode;
|
||||
}
|
||||
|
||||
public void setPostalCode(String postalCode) {
|
||||
this.postalCode = postalCode;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public Boolean getOptIn1() {
|
||||
return optIn1;
|
||||
}
|
||||
|
||||
public void setOptIn1(Boolean optIn1) {
|
||||
this.optIn1 = optIn1;
|
||||
}
|
||||
|
||||
public Boolean getOptIn2() {
|
||||
return optIn2;
|
||||
}
|
||||
|
||||
public void setOptIn2(Boolean optIn2) {
|
||||
this.optIn2 = optIn2;
|
||||
}
|
||||
|
||||
public String getAuthenticationToken() {
|
||||
return authenticationToken;
|
||||
}
|
||||
|
||||
public void setAuthenticationToken(String authenticationToken) {
|
||||
this.authenticationToken = authenticationToken;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.api.model;
|
||||
|
||||
/**
|
||||
* Auxiliary devices.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class Auxiliary {
|
||||
|
||||
private String state;
|
||||
|
||||
private String label;
|
||||
|
||||
private String icon;
|
||||
|
||||
private String type;
|
||||
|
||||
private String subtype;
|
||||
|
||||
private String name;
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.api.model;
|
||||
|
||||
/**
|
||||
* Device refers to a iAqualink Pool Controller.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class Device {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String serialNumber;
|
||||
|
||||
private String createdAt;
|
||||
|
||||
private String updatedAt;
|
||||
|
||||
private String name;
|
||||
|
||||
private String deviceType;
|
||||
|
||||
private Object ownerId;
|
||||
|
||||
private Boolean updating;
|
||||
|
||||
private Object firmwareVersion;
|
||||
|
||||
private Object targetFirmwareVersion;
|
||||
|
||||
private Object updateFirmwareStartAt;
|
||||
|
||||
private Object lastActivityAt;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSerialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
public void setSerialNumber(String serialNumber) {
|
||||
this.serialNumber = serialNumber;
|
||||
}
|
||||
|
||||
public String getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(String createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public String getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(String updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public Object getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setOwnerId(Object ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
public Boolean getUpdating() {
|
||||
return updating;
|
||||
}
|
||||
|
||||
public void setUpdating(Boolean updating) {
|
||||
this.updating = updating;
|
||||
}
|
||||
|
||||
public Object getFirmwareVersion() {
|
||||
return firmwareVersion;
|
||||
}
|
||||
|
||||
public void setFirmwareVersion(Object firmwareVersion) {
|
||||
this.firmwareVersion = firmwareVersion;
|
||||
}
|
||||
|
||||
public Object getTargetFirmwareVersion() {
|
||||
return targetFirmwareVersion;
|
||||
}
|
||||
|
||||
public void setTargetFirmwareVersion(Object targetFirmwareVersion) {
|
||||
this.targetFirmwareVersion = targetFirmwareVersion;
|
||||
}
|
||||
|
||||
public Object getUpdateFirmwareStartAt() {
|
||||
return updateFirmwareStartAt;
|
||||
}
|
||||
|
||||
public void setUpdateFirmwareStartAt(Object updateFirmwareStartAt) {
|
||||
this.updateFirmwareStartAt = updateFirmwareStartAt;
|
||||
}
|
||||
|
||||
public Object getLastActivityAt() {
|
||||
return lastActivityAt;
|
||||
}
|
||||
|
||||
public void setLastActivityAt(Object lastActivityAt) {
|
||||
this.lastActivityAt = lastActivityAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.api.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link Home} refers to the "Home" screen of a pool controller.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class Home {
|
||||
|
||||
private String status;
|
||||
|
||||
private String response;
|
||||
|
||||
private String systemType;
|
||||
|
||||
private String tempScale;
|
||||
|
||||
private String spaTemp;
|
||||
|
||||
private String poolTemp;
|
||||
|
||||
private String airTemp;
|
||||
|
||||
private String spaSetPoint;
|
||||
|
||||
private String poolSetPoint;
|
||||
|
||||
private String coverPool;
|
||||
|
||||
private String freezeProtection;
|
||||
|
||||
private String spaPump;
|
||||
|
||||
private String poolPump;
|
||||
|
||||
private String spaHeater;
|
||||
|
||||
private String poolHeater;
|
||||
|
||||
private String solarHeater;
|
||||
|
||||
private String spaSalinity;
|
||||
|
||||
private String poolSalinity;
|
||||
|
||||
private String orp;
|
||||
|
||||
private String ph;
|
||||
|
||||
private Map<String, String> serializedMap;
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(String response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public String getSystemType() {
|
||||
return systemType;
|
||||
}
|
||||
|
||||
public void setSystemType(String systemType) {
|
||||
this.systemType = systemType;
|
||||
}
|
||||
|
||||
public String getTempScale() {
|
||||
return tempScale;
|
||||
}
|
||||
|
||||
public void setTempScale(String tempScale) {
|
||||
this.tempScale = tempScale;
|
||||
}
|
||||
|
||||
public String getSpaTemp() {
|
||||
return spaTemp;
|
||||
}
|
||||
|
||||
public void setSpaTemp(String spaTemp) {
|
||||
this.spaTemp = spaTemp;
|
||||
}
|
||||
|
||||
public String getPoolTemp() {
|
||||
return poolTemp;
|
||||
}
|
||||
|
||||
public void setPoolTemp(String poolTemp) {
|
||||
this.poolTemp = poolTemp;
|
||||
}
|
||||
|
||||
public String getAirTemp() {
|
||||
return airTemp;
|
||||
}
|
||||
|
||||
public void setAirTemp(String airTemp) {
|
||||
this.airTemp = airTemp;
|
||||
}
|
||||
|
||||
public String getSpaSetPoint() {
|
||||
return spaSetPoint;
|
||||
}
|
||||
|
||||
public void setSpaSetPoint(String spaSetPoint) {
|
||||
this.spaSetPoint = spaSetPoint;
|
||||
}
|
||||
|
||||
public String getPoolSetPoint() {
|
||||
return poolSetPoint;
|
||||
}
|
||||
|
||||
public void setPoolSetPoint(String poolSetPoint) {
|
||||
this.poolSetPoint = poolSetPoint;
|
||||
}
|
||||
|
||||
public String getCoverPool() {
|
||||
return coverPool;
|
||||
}
|
||||
|
||||
public void setCoverPool(String coverPool) {
|
||||
this.coverPool = coverPool;
|
||||
}
|
||||
|
||||
public String getFreezeProtection() {
|
||||
return freezeProtection;
|
||||
}
|
||||
|
||||
public void setFreezeProtection(String freezeProtection) {
|
||||
this.freezeProtection = freezeProtection;
|
||||
}
|
||||
|
||||
public String getSpaPump() {
|
||||
return spaPump;
|
||||
}
|
||||
|
||||
public void setSpaPump(String spaPump) {
|
||||
this.spaPump = spaPump;
|
||||
}
|
||||
|
||||
public String getPoolPump() {
|
||||
return poolPump;
|
||||
}
|
||||
|
||||
public void setPoolPump(String poolPump) {
|
||||
this.poolPump = poolPump;
|
||||
}
|
||||
|
||||
public String getSpaHeater() {
|
||||
return spaHeater;
|
||||
}
|
||||
|
||||
public void setSpaHeater(String spaHeater) {
|
||||
this.spaHeater = spaHeater;
|
||||
}
|
||||
|
||||
public String getPoolHeater() {
|
||||
return poolHeater;
|
||||
}
|
||||
|
||||
public void setPoolHeater(String poolHeater) {
|
||||
this.poolHeater = poolHeater;
|
||||
}
|
||||
|
||||
public String getSolarHeater() {
|
||||
return solarHeater;
|
||||
}
|
||||
|
||||
public void setSolarHeater(String solarHeater) {
|
||||
this.solarHeater = solarHeater;
|
||||
}
|
||||
|
||||
public String getSpaSalinity() {
|
||||
return spaSalinity;
|
||||
}
|
||||
|
||||
public void setSpaSalinity(String spaSalinity) {
|
||||
this.spaSalinity = spaSalinity;
|
||||
}
|
||||
|
||||
public String getPoolSalinity() {
|
||||
return poolSalinity;
|
||||
}
|
||||
|
||||
public void setPoolSalinity(String poolSalinity) {
|
||||
this.poolSalinity = poolSalinity;
|
||||
}
|
||||
|
||||
public String getOrp() {
|
||||
return orp;
|
||||
}
|
||||
|
||||
public void setOrp(String orp) {
|
||||
this.orp = orp;
|
||||
}
|
||||
|
||||
public String getPh() {
|
||||
return ph;
|
||||
}
|
||||
|
||||
public void setPh(String ph) {
|
||||
this.ph = ph;
|
||||
}
|
||||
|
||||
public Map<String, String> getSerializedMap() {
|
||||
return serializedMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.api.model;
|
||||
|
||||
/**
|
||||
* OneTouch Macros.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class OneTouch {
|
||||
|
||||
private String status;
|
||||
|
||||
private String state;
|
||||
|
||||
private String label;
|
||||
|
||||
private String name;
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.api.model;
|
||||
|
||||
/**
|
||||
* Object used to login to service.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class SignIn {
|
||||
|
||||
private String apiKey;
|
||||
|
||||
private String email;
|
||||
|
||||
private String password;
|
||||
|
||||
public SignIn(String apiKey, String email, String password) {
|
||||
super();
|
||||
this.apiKey = apiKey;
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.config;
|
||||
|
||||
/**
|
||||
* Configuration properties for connecting to a iAqualink Account
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class IAqualinkConfiguration {
|
||||
|
||||
/**
|
||||
* user to us when connecting to the account
|
||||
*/
|
||||
public String userName;
|
||||
|
||||
/**
|
||||
* password to us when connecting to the account
|
||||
*/
|
||||
public String password;
|
||||
|
||||
/**
|
||||
* Option serialId of the pool controller to connect to, only useful if you have more then one controller
|
||||
*/
|
||||
public String serialId;
|
||||
|
||||
/**
|
||||
* fixed API key provided by iAqualink clients (Android, IOS) , unknown if this will change in the future.
|
||||
*/
|
||||
public String apiKey;
|
||||
|
||||
/**
|
||||
* Rate we poll for new data
|
||||
*/
|
||||
public int refresh;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.iaqualink.internal.IAqualinkBindingConstants;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
|
||||
/**
|
||||
* AuxiliaryType maps iAquaLink Auxiliary Devices to binding channel types
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum AuxiliaryType {
|
||||
SWITCH("0", "0", IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_SWITCH),
|
||||
DIMMER("1", "NA", IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_DIMMER),
|
||||
JANDYCOLOR("2", "1", IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_JANDYCOLOR),
|
||||
PENTAIRSAM("2", "2", IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_PENTAIRSAM),
|
||||
JANDYLED("2", "4", IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_JANDYLED),
|
||||
PENTAIRIB("2", "5", IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_PENTAIRIB),
|
||||
HAYWARD("2", "6", IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_HAYWARD);
|
||||
|
||||
private String type;
|
||||
private String subType;
|
||||
private ChannelTypeUID channelTypeUID;
|
||||
|
||||
AuxiliaryType(String type, String subType, ChannelTypeUID channelTypeUID) {
|
||||
this.type = type;
|
||||
this.subType = subType;
|
||||
this.channelTypeUID = channelTypeUID;
|
||||
}
|
||||
|
||||
public String getSubType() {
|
||||
return subType;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public ChannelTypeUID getChannelTypeUID() {
|
||||
return channelTypeUID;
|
||||
}
|
||||
|
||||
public static AuxiliaryType fromSubType(String subType) {
|
||||
for (AuxiliaryType at : AuxiliaryType.values()) {
|
||||
if (at.subType.equals(subType)) {
|
||||
return at;
|
||||
}
|
||||
}
|
||||
return AuxiliaryType.SWITCH;
|
||||
}
|
||||
|
||||
public static AuxiliaryType fromChannelTypeUID(@Nullable ChannelTypeUID channelTypeUID) {
|
||||
for (AuxiliaryType at : AuxiliaryType.values()) {
|
||||
if (at.channelTypeUID.equals(channelTypeUID)) {
|
||||
return at;
|
||||
}
|
||||
}
|
||||
return AuxiliaryType.SWITCH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Heater States returned by iAquaLink API
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum HeaterState {
|
||||
OFF("0", "off"),
|
||||
HEATING("1", "on"),
|
||||
ENABLED("3", "enabled");
|
||||
|
||||
String value;
|
||||
String label;
|
||||
|
||||
HeaterState(String value, String label) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public static @Nullable HeaterState fromValue(String value) {
|
||||
for (HeaterState state : HeaterState.values()) {
|
||||
if (state.value.equals(value)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.iaqualink.internal.handler;
|
||||
|
||||
import static org.openhab.core.library.unit.ImperialUnits.FAHRENHEIT;
|
||||
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.iaqualink.internal.IAqualinkBindingConstants;
|
||||
import org.openhab.binding.iaqualink.internal.api.IAqualinkClient;
|
||||
import org.openhab.binding.iaqualink.internal.api.IAqualinkClient.NotAuthorizedException;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.AccountInfo;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.Auxiliary;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.Device;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.Home;
|
||||
import org.openhab.binding.iaqualink.internal.api.model.OneTouch;
|
||||
import org.openhab.binding.iaqualink.internal.config.IAqualinkConfiguration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.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.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* iAquaLink Control Binding
|
||||
*
|
||||
* iAquaLink controllers allow remote access to Jandy/Zodiac pool systems. This
|
||||
* binding allows openHAB to both monitor and control a pool system through
|
||||
* these controllers.
|
||||
*
|
||||
* The {@link IAqualinkHandler} is responsible for handling commands, which
|
||||
* are sent to one of the channels.
|
||||
*
|
||||
* @author Dan Cunningham - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class IAqualinkHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(IAqualinkHandler.class);
|
||||
|
||||
/**
|
||||
* Minimum amount of time we can poll for updates
|
||||
*/
|
||||
private static final int MIN_REFRESH_SECONDS = 5;
|
||||
|
||||
/**
|
||||
* Minimum amount of time we can poll after a command
|
||||
*/
|
||||
private static final int COMMAND_REFRESH_SECONDS = 5;
|
||||
|
||||
/**
|
||||
* Default iAqulink key used by existing clients in the marketplace
|
||||
*/
|
||||
private static final String DEFAULT_API_KEY = "EOOEMOW4YR6QNB07";
|
||||
|
||||
/**
|
||||
* Local cache of iAqualink states
|
||||
*/
|
||||
private Map<String, State> stateMap = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
/**
|
||||
* Our poll rate
|
||||
*/
|
||||
private int refresh;
|
||||
|
||||
/**
|
||||
* fixed API key provided by iAqualink clients (Android, IOS), unknown if this will change in the future.
|
||||
*/
|
||||
|
||||
private @Nullable String apiKey;
|
||||
|
||||
/**
|
||||
* Optional serial number of the pool controller to connect to, only useful if you have more then one controller
|
||||
*/
|
||||
private @Nullable String serialNumber;
|
||||
|
||||
/**
|
||||
* Server issued sessionId
|
||||
*/
|
||||
private @Nullable String sessionId;
|
||||
|
||||
/**
|
||||
* When we first connect we will dynamically create channels based on what the controller is configured for
|
||||
*/
|
||||
private boolean firstRun;
|
||||
|
||||
/**
|
||||
* Future to poll for updated
|
||||
*/
|
||||
private @Nullable ScheduledFuture<?> pollFuture;
|
||||
|
||||
/**
|
||||
* The client interface to the iAqualink Service
|
||||
*/
|
||||
private IAqualinkClient client;
|
||||
|
||||
/**
|
||||
* Temperature unit, will be set based on user setting
|
||||
*/
|
||||
private Unit<Temperature> temperatureUnit = CELSIUS;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link IAqualinkHandler}
|
||||
*
|
||||
* @param thing
|
||||
* @param httpClient
|
||||
*/
|
||||
public IAqualinkHandler(Thing thing, HttpClient httpClient) {
|
||||
super(thing);
|
||||
client = new IAqualinkClient(httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// don't hold up initialize
|
||||
scheduler.schedule(this::configure, 0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Handler disposed.");
|
||||
clearPolling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelLinked(ChannelUID channelUID) {
|
||||
// clear our cached value so the new channel gets updated on the next poll
|
||||
stateMap.remove(channelUID.getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand channel: {} command: {}", channelUID, command);
|
||||
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
logger.warn("Controller is not ONLINE and is not responding to commands");
|
||||
return;
|
||||
}
|
||||
|
||||
clearPolling();
|
||||
|
||||
String channelName = channelUID.getIdWithoutGroup();
|
||||
// remove the current state to ensure we send an update
|
||||
stateMap.remove(channelUID.getAsString());
|
||||
try {
|
||||
if (command instanceof RefreshType) {
|
||||
logger.debug("Channel {} state has been cleared", channelName);
|
||||
} else if (channelName.startsWith("aux_")) {
|
||||
// Auxiliary Commands
|
||||
String auxId = channelName.replaceFirst("aux_", "");
|
||||
if (command instanceof PercentType) {
|
||||
client.dimmerCommand(serialNumber, sessionId, auxId, command.toString());
|
||||
} else if (command instanceof StringType) {
|
||||
String cmd = "off".equals(command.toString()) ? "0"
|
||||
: "on".equals(command.toString()) ? "1" : command.toString();
|
||||
client.lightCommand(serialNumber, sessionId, auxId, cmd,
|
||||
AuxiliaryType.fromChannelTypeUID(getChannelTypeUID(channelUID)).getSubType());
|
||||
} else if (command instanceof OnOffType) {
|
||||
// these are toggle commands and require we have the current state to turn on/off
|
||||
Auxiliary[] auxs = client.getAux(serialNumber, sessionId);
|
||||
Optional<Auxiliary> optional = Arrays.stream(auxs).filter(o -> o.getName().equals(channelName))
|
||||
.findFirst();
|
||||
if (optional.isPresent()) {
|
||||
if (toState(channelName, "Switch", optional.get().getState()) != command) {
|
||||
client.auxSetCommand(serialNumber, sessionId, channelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (channelName.endsWith("_set_point")) {
|
||||
// Set Point Commands
|
||||
if ("spa_set_point".equals(channelName)) {
|
||||
BigDecimal value = commandToRoundedTemperature(command, temperatureUnit);
|
||||
if (value != null) {
|
||||
client.setSpaTemp(serialNumber, sessionId, value.floatValue());
|
||||
}
|
||||
} else if ("pool_set_point".equals(channelName)) {
|
||||
BigDecimal value = commandToRoundedTemperature(command, temperatureUnit);
|
||||
if (value != null) {
|
||||
client.setPoolTemp(serialNumber, sessionId, value.floatValue());
|
||||
}
|
||||
}
|
||||
} else if (command instanceof OnOffType) {
|
||||
// these are toggle commands and require we have the current state to turn on/off
|
||||
if (channelName.startsWith("onetouch_")) {
|
||||
OneTouch[] ota = client.getOneTouch(serialNumber, sessionId);
|
||||
Optional<OneTouch> optional = Arrays.stream(ota).filter(o -> o.getName().equals(channelName))
|
||||
.findFirst();
|
||||
if (optional.isPresent()) {
|
||||
if (toState(channelName, "Switch", optional.get().getState()) != command) {
|
||||
logger.debug("Sending command {} to {}", command, channelName);
|
||||
client.oneTouchSetCommand(serialNumber, sessionId, channelName);
|
||||
}
|
||||
}
|
||||
} else if (channelName.endsWith("heater") || channelName.endsWith("pump")) {
|
||||
String value = client.getHome(serialNumber, sessionId).getSerializedMap().get(channelName);
|
||||
if (toState(channelName, "Switch", value) != command) {
|
||||
logger.debug("Sending command {} to {}", command, channelName);
|
||||
client.homeScreenSetCommand(serialNumber, sessionId, channelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
initPolling(COMMAND_REFRESH_SECONDS);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Exception executing command", e);
|
||||
initPolling(COMMAND_REFRESH_SECONDS);
|
||||
} catch (NotAuthorizedException e) {
|
||||
logger.debug("Authorization Exception sending command", e);
|
||||
configure();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this thing
|
||||
*/
|
||||
private void configure() {
|
||||
clearPolling();
|
||||
firstRun = true;
|
||||
|
||||
IAqualinkConfiguration configuration = getConfig().as(IAqualinkConfiguration.class);
|
||||
String username = configuration.userName;
|
||||
String password = configuration.password;
|
||||
String confSerialId = configuration.serialId;
|
||||
String confApiKey = configuration.apiKey;
|
||||
|
||||
if (StringUtils.isNotBlank(confApiKey)) {
|
||||
this.apiKey = confApiKey;
|
||||
} else {
|
||||
this.apiKey = DEFAULT_API_KEY;
|
||||
}
|
||||
|
||||
this.refresh = Math.max(configuration.refresh, MIN_REFRESH_SECONDS);
|
||||
|
||||
try {
|
||||
AccountInfo accountInfo = client.login(username, password, apiKey);
|
||||
sessionId = accountInfo.getSessionId();
|
||||
if (sessionId == null) {
|
||||
throw new IOException("Response from controller not valid");
|
||||
}
|
||||
logger.debug("SessionID {}", sessionId);
|
||||
|
||||
Device[] devices = client.getDevices(apiKey, accountInfo.getAuthenticationToken(), accountInfo.getId());
|
||||
|
||||
if (devices.length == 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No registered devices found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(confSerialId)) {
|
||||
serialNumber = confSerialId.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
|
||||
if (!Arrays.stream(devices).anyMatch(device -> device.getSerialNumber().equals(serialNumber))) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"No Device for given serialId found");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
serialNumber = devices[0].getSerialNumber();
|
||||
}
|
||||
|
||||
initPolling(COMMAND_REFRESH_SECONDS);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Could not connect to service {}", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} catch (NotAuthorizedException e) {
|
||||
logger.debug("Credentials not valid");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Credentials not valid");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts/Restarts polling with an initial delay. This allows changes in the poll cycle for when commands are sent
|
||||
* and we need to poll sooner then the next refresh cycle.
|
||||
*/
|
||||
private synchronized void initPolling(int initialDelay) {
|
||||
clearPolling();
|
||||
pollFuture = scheduler.scheduleWithFixedDelay(this::pollController, initialDelay, refresh, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops/clears this thing's polling future
|
||||
*/
|
||||
private void clearPolling() {
|
||||
ScheduledFuture<?> localFuture = pollFuture;
|
||||
if (isFutureValid(localFuture)) {
|
||||
if (localFuture != null) {
|
||||
localFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFutureValid(@Nullable ScheduledFuture<?> future) {
|
||||
return future != null && !future.isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the controller for updates.
|
||||
*/
|
||||
private void pollController() {
|
||||
ScheduledFuture<?> localFuture = pollFuture;
|
||||
try {
|
||||
Home home = client.getHome(serialNumber, sessionId);
|
||||
|
||||
if ("Error".equals(home.getResponse())) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Service reports controller status as: " + home.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> map = home.getSerializedMap();
|
||||
if (map != null) {
|
||||
temperatureUnit = "F".equalsIgnoreCase(map.get("temp_scale")) ? FAHRENHEIT : CELSIUS;
|
||||
map.forEach((k, v) -> {
|
||||
updatedState(k, v);
|
||||
if (k.endsWith("_heater")) {
|
||||
HeaterState hs = HeaterState.fromValue(v);
|
||||
updatedState(k + "_status", hs == null ? null : hs.getLabel());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
OneTouch[] oneTouches = client.getOneTouch(serialNumber, sessionId);
|
||||
Auxiliary[] auxes = client.getAux(serialNumber, sessionId);
|
||||
|
||||
if (firstRun) {
|
||||
firstRun = false;
|
||||
updateChannels(auxes, oneTouches);
|
||||
}
|
||||
|
||||
for (OneTouch ot : oneTouches) {
|
||||
updatedState(ot.getName(), ot.getState());
|
||||
}
|
||||
|
||||
for (Auxiliary aux : auxes) {
|
||||
switch (aux.getType()) {
|
||||
// dimmer uses subType for value
|
||||
case "1":
|
||||
updatedState(aux.getName(), aux.getSubtype());
|
||||
break;
|
||||
// Color lights do not report the color value, only on/off
|
||||
case "2":
|
||||
updatedState(aux.getName(), "0".equals(aux.getState()) ? "off" : "on");
|
||||
break;
|
||||
// all else are switches
|
||||
default:
|
||||
updatedState(aux.getName(), aux.getState());
|
||||
}
|
||||
}
|
||||
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// poller will continue to run, set offline until next run
|
||||
logger.debug("Exception polling", e);
|
||||
if (isFutureValid(localFuture)) {
|
||||
// only valid futures should set state, otherwise this exception was do to being canceled.
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
} catch (NotAuthorizedException e) {
|
||||
// if are creds are not valid, we need to try re authorizing again
|
||||
logger.debug("Authorization Exception during polling", e);
|
||||
clearPolling();
|
||||
configure();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an channels state only if the value of the channel has changed since our last poll.
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
private void updatedState(String name, @Nullable String value) {
|
||||
logger.trace("updatedState {} : {}", name, value);
|
||||
Channel channel = getThing().getChannel(name);
|
||||
if (channel != null) {
|
||||
State state = toState(name, channel.getAcceptedItemType(), value);
|
||||
State oldState = stateMap.put(channel.getUID().getAsString(), state);
|
||||
if (!state.equals(oldState)) {
|
||||
logger.trace("updating channel {} with state {} (old state {})", channel.getUID(), state, oldState);
|
||||
updateState(channel.getUID(), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link String} value to a {@link State} for a given
|
||||
* {@link String} accepted type
|
||||
*
|
||||
* @param itemType
|
||||
* @param value
|
||||
* @return {@link State}
|
||||
*/
|
||||
private State toState(String name, @Nullable String type, @Nullable String value) {
|
||||
try {
|
||||
// @nullable checker does not recognize isBlank as checking null here, so must use == null to make happy
|
||||
if (value == null || StringUtils.isBlank(value)) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
return StringType.valueOf(value);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "Number:Temperature":
|
||||
return new QuantityType<>(Float.parseFloat(value), temperatureUnit);
|
||||
case "Number":
|
||||
return new DecimalType(value);
|
||||
case "Dimmer":
|
||||
return new PercentType(value);
|
||||
case "Switch":
|
||||
return Integer.parseInt(value) > 0 ? OnOffType.ON : OnOffType.OFF;
|
||||
default:
|
||||
return StringType.valueOf(value);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates channels based on what is supported by the controller.
|
||||
*/
|
||||
private void updateChannels(Auxiliary[] auxes, OneTouch[] oneTouches) {
|
||||
List<Channel> channels = new ArrayList<>(getThing().getChannels());
|
||||
for (Auxiliary aux : auxes) {
|
||||
ChannelUID channelUID = new ChannelUID(getThing().getUID(), aux.getName());
|
||||
logger.debug("Add channel Aux Name: {} Label: {} Type: {} Subtype: {}", aux.getName(), aux.getLabel(),
|
||||
aux.getType(), aux.getSubtype());
|
||||
switch (aux.getType()) {
|
||||
case "1":
|
||||
addNewChannelToList(channels, channelUID, "Dimmer",
|
||||
IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_DIMMER, aux.getLabel());
|
||||
break;
|
||||
case "2": {
|
||||
addNewChannelToList(channels, channelUID, "String",
|
||||
AuxiliaryType.fromSubType(aux.getSubtype()).getChannelTypeUID(), aux.getLabel());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
addNewChannelToList(channels, channelUID, "Switch",
|
||||
IAqualinkBindingConstants.CHANNEL_TYPE_UID_AUX_SWITCH, aux.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
for (OneTouch oneTouch : oneTouches) {
|
||||
if ("0".equals(oneTouch.getStatus())) {
|
||||
// OneTouch is not enabled
|
||||
continue;
|
||||
}
|
||||
|
||||
ChannelUID channelUID = new ChannelUID(getThing().getUID(), oneTouch.getName());
|
||||
addNewChannelToList(channels, channelUID, "Switch", IAqualinkBindingConstants.CHANNEL_TYPE_UID_ONETOUCH,
|
||||
oneTouch.getLabel());
|
||||
}
|
||||
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
thingBuilder.withChannels(channels);
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a channel to the list of channels if the channel does not exist or is of a different type
|
||||
*
|
||||
*/
|
||||
private void addNewChannelToList(List<Channel> list, ChannelUID channelUID, String itemType,
|
||||
ChannelTypeUID channelType, String label) {
|
||||
// if there is no entry, add it
|
||||
if (!list.stream().anyMatch(c -> c.getUID().equals(channelUID))) {
|
||||
list.add(ChannelBuilder.create(channelUID, itemType).withType(channelType).withLabel(label).build());
|
||||
} else if (list.removeIf(c -> c.getUID().equals(channelUID) && !channelType.equals(c.getChannelTypeUID()))) {
|
||||
// this channel uid exists, but has a different type so remove and add our new one
|
||||
list.add(ChannelBuilder.create(channelUID, itemType).withType(channelType).withLabel(label).build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* inspired by the openHAB Nest thermostat binding
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private @Nullable BigDecimal commandToRoundedTemperature(Command command, Unit<Temperature> unit)
|
||||
throws IllegalArgumentException {
|
||||
QuantityType<Temperature> quantity;
|
||||
if (command instanceof QuantityType) {
|
||||
quantity = (QuantityType<Temperature>) command;
|
||||
} else {
|
||||
quantity = new QuantityType<>(new BigDecimal(command.toString()), unit);
|
||||
}
|
||||
|
||||
QuantityType<Temperature> temparatureQuantity = quantity.toUnit(unit);
|
||||
if (temparatureQuantity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BigDecimal value = temparatureQuantity.toBigDecimal();
|
||||
BigDecimal increment = CELSIUS == unit ? new BigDecimal("0.5") : new BigDecimal("1");
|
||||
BigDecimal divisor = value.divide(increment, 0, RoundingMode.HALF_UP);
|
||||
return divisor.multiply(increment);
|
||||
}
|
||||
|
||||
private ChannelTypeUID getChannelTypeUID(ChannelUID channelUID) {
|
||||
Channel channel = getThing().getChannel(channelUID.getId());
|
||||
Objects.requireNonNull(channel);
|
||||
ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
|
||||
Objects.requireNonNull(channelTypeUID);
|
||||
return channelTypeUID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="iaqualink" 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>iAquaLink Binding</name>
|
||||
<description>This is the binding for a iAquaLink pool controller.</description>
|
||||
<author>Dan Cunningham</author>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,331 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="iaqualink"
|
||||
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="controller">
|
||||
<label>iAquaLink Pool Controller</label>
|
||||
<description>A iAquaLink pool control thing represents a iAquaLink pool controller for Jandy/Zodiac systems
|
||||
</description>
|
||||
<channels>
|
||||
<channel id="status" typeId="status"/>
|
||||
<channel id="system_type" typeId="system_type"/>
|
||||
<channel id="temp_scale" typeId="temperature_scale"/>
|
||||
<channel id="spa_temp" typeId="temperature">
|
||||
<label>Spa Temperature</label>
|
||||
<description>The current temperature of the spa</description>
|
||||
</channel>
|
||||
<channel id="pool_temp" typeId="temperature">
|
||||
<label>Pool Temperature</label>
|
||||
<description>The current temperature of the pool</description>
|
||||
</channel>
|
||||
<channel id="air_temp" typeId="temperature">
|
||||
<label>Air Temperature</label>
|
||||
<description>The current outside temperature</description>
|
||||
</channel>
|
||||
<channel id="spa_set_point" typeId="setpoint">
|
||||
<label>Spa Setpoint</label>
|
||||
<description>The spa setpoint</description>
|
||||
</channel>
|
||||
<channel id="pool_set_point" typeId="setpoint">
|
||||
<label>Pool Setpoint</label>
|
||||
<description>The pool setpoint</description>
|
||||
</channel>
|
||||
<channel id="cover_pool" typeId="equipment-switch">
|
||||
<label>Cover Pool</label>
|
||||
<description>Pool covering</description>
|
||||
</channel>
|
||||
<channel id="freeze_protection" typeId="equipment-switch">
|
||||
<label>Freeze Protection</label>
|
||||
<description>Freeze protection</description>
|
||||
</channel>
|
||||
<channel id="spa_pump" typeId="equipment-switch">
|
||||
<label>Spa Pump</label>
|
||||
<description>Spa pump</description>
|
||||
</channel>
|
||||
<channel id="pool_pump" typeId="equipment-switch">
|
||||
<label>Pool Pump</label>
|
||||
<description>Pool pump</description>
|
||||
</channel>
|
||||
<channel id="spa_heater_status" typeId="equipment-heater">
|
||||
<label>Spa Heater Status</label>
|
||||
<description>Spa heater status</description>
|
||||
</channel>
|
||||
<channel id="spa_heater" typeId="equipment-switch">
|
||||
<label>Spa Heater Switch</label>
|
||||
<description>Spa heater switch</description>
|
||||
</channel>
|
||||
<channel id="pool_heater_status" typeId="equipment-heater">
|
||||
<label>Pool Heater Status</label>
|
||||
<description>Pool heater status</description>
|
||||
</channel>
|
||||
<channel id="pool_heater" typeId="equipment-switch">
|
||||
<label>Pool Heater Switch</label>
|
||||
<description>Pool heater Switch</description>
|
||||
</channel>
|
||||
<channel id="solar_heater_status" typeId="equipment-heater">
|
||||
<label>Solar Heater Status</label>
|
||||
<description>Solar heater status</description>
|
||||
</channel>
|
||||
<channel id="solar_heater" typeId="equipment-switch">
|
||||
<label>Solar Heater Switch</label>
|
||||
<description>Solar heater switch</description>
|
||||
</channel>
|
||||
<channel id="spa_salinity" typeId="chemical">
|
||||
<label>Spa Salinity</label>
|
||||
<description>Spa Salinity</description>
|
||||
</channel>
|
||||
<channel id="pool_salinity" typeId="chemical">
|
||||
<label>Pool Salinity</label>
|
||||
<description>Pool Salinity</description>
|
||||
</channel>
|
||||
<channel id="orp" typeId="chemical">
|
||||
<label>Orp</label>
|
||||
<description>Orp</description>
|
||||
</channel>
|
||||
<channel id="ph" typeId="chemical">
|
||||
<label>PH</label>
|
||||
<description>PH</description>
|
||||
</channel>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="userName" type="text" required="true">
|
||||
<label>User Name</label>
|
||||
<description>The user name to use when connecting to a iAqualink Account</description>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>The password to use when connecting to a iAqualink Account</description>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" required="true">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the refresh interval in seconds</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="serialId" type="text" required="false">
|
||||
<label>Serial Number</label>
|
||||
<description>Optionally specify the serial number of the controller which can be found on the iAquaLink Owner's
|
||||
Center. This is only useful if you have more then one controller (pool) associated with your account. Leave blank
|
||||
to have the first controller used.
|
||||
</description>
|
||||
</parameter>
|
||||
<parameter name="apiKey" type="text" required="false">
|
||||
<label>API Key</label>
|
||||
<description>Optionally specify the API key used for access. This is only useful for debugging or if the API key is
|
||||
changed by the vendor
|
||||
</description>
|
||||
<default>EOOEMOW4YR6QNB07</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<!-- System Channels -->
|
||||
|
||||
<channel-type id="status">
|
||||
<item-type>String</item-type>
|
||||
<label>Status</label>
|
||||
<description>The status of the iAqualink connection</description>
|
||||
<state readOnly="true"/>
|
||||
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="system_type" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>System Type</label>
|
||||
<description> System Type</description>
|
||||
<state readOnly="true"/>
|
||||
|
||||
</channel-type>
|
||||
|
||||
<!-- Equipment and Aux Channels -->
|
||||
|
||||
<channel-type id="aux-switch">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Auxiliary Switch</label>
|
||||
<description>The current state of auxiliary channel</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="aux-dimmer">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Auxiliary Dimmer</label>
|
||||
<description>The current state of auxiliary channel</description>
|
||||
<state step="25"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="aux-jandycolor">
|
||||
<item-type>String</item-type>
|
||||
<label>Jandy Color Lighting</label>
|
||||
<description>Jandy Color Lighting Channel</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="off">Off</option>
|
||||
<option value="on">On</option>
|
||||
<option value="1">Alpine White</option>
|
||||
<option value="2">Sky Blue</option>
|
||||
<option value="3">Cobalt Blue</option>
|
||||
<option value="4">Caribbean Blue</option>
|
||||
<option value="5">Spring Green</option>
|
||||
<option value="6">Emerald Green</option>
|
||||
<option value="7">Emerald Rose</option>
|
||||
<option value="8">Magenta</option>
|
||||
<option value="9">Garnet Red</option>
|
||||
<option value="10">Violet</option>
|
||||
<option value="11">Color Splash</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="aux-pentairsam">
|
||||
<item-type>String</item-type>
|
||||
<label>Pentair SAm/SAL Lighting</label>
|
||||
<description>Pentair SAm/SAL Lighting Channel</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="off">Off</option>
|
||||
<option value="on">On</option>
|
||||
<option value="1">White</option>
|
||||
<option value="2">Light Green</option>
|
||||
<option value="3">Green</option>
|
||||
<option value="4">Cyan</option>
|
||||
<option value="5">Blue</option>
|
||||
<option value="6">Lavender</option>
|
||||
<option value="7">Magenta</option>
|
||||
<option value="8">Light Magenta</option>
|
||||
<option value="9">Color Splash</option>
|
||||
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="aux-jandyled">
|
||||
<item-type>String</item-type>
|
||||
<label>Jandy Led Water Colors Lighting</label>
|
||||
<description>Jandy Led Water Colors Lighting Channel</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="off">Off</option>
|
||||
<option value="on">On</option>
|
||||
<option value="1">Alpine White</option>
|
||||
<option value="2">Sky Blue</option>
|
||||
<option value="3">Cobalt Blue</option>
|
||||
<option value="4">Caribbean Blue</option>
|
||||
<option value="5">Spring Green</option>
|
||||
<option value="6">Emerald Green</option>
|
||||
<option value="7">Emerald Rose</option>
|
||||
<option value="8">Magenta</option>
|
||||
<option value="9">Violet</option>
|
||||
<option value="10">Slow Splash</option>
|
||||
<option value="11">Fast Splash</option>
|
||||
<option value="12">USA!!!</option>
|
||||
<option value="13">Fat Tuesday</option>
|
||||
<option value="14">Disco Tech</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="aux-pentairib">
|
||||
<item-type>String</item-type>
|
||||
<label>Pentair intelliBrite Lighting</label>
|
||||
<description>Pentair intelliBrite Lighting Channel</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="off">Off</option>
|
||||
<option value="on">On</option>
|
||||
<option value="1">SAM</option>
|
||||
<option value="2">Party</option>
|
||||
<option value="3">Romance</option>
|
||||
<option value="4">Caribbean</option>
|
||||
<option value="5">American</option>
|
||||
<option value="6">Cal Sunset</option>
|
||||
<option value="7">Royal</option>
|
||||
<option value="8">Blue</option>
|
||||
<option value="9">Green</option>
|
||||
<option value="10">Red</option>
|
||||
<option value="11">White</option>
|
||||
<option value="12">Magenta</option>
|
||||
<option value="13">Hold</option>
|
||||
<option value="14">Recall</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="aux-hayward">
|
||||
<item-type>String</item-type>
|
||||
<label>Hayward Universal Lighting</label>
|
||||
<description>Hayward Universal Lighting Channel</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="off">Off</option>
|
||||
<option value="on">On</option>
|
||||
<option value="1">Voodoo Lounge</option>
|
||||
<option value="2">Deep Blue Sea</option>
|
||||
<option value="3">Afternoon Skies</option>
|
||||
<option value="4">Emerald</option>
|
||||
<option value="5">Sangria</option>
|
||||
<option value="6">Cloud White</option>
|
||||
<option value="7">Twilight</option>
|
||||
<option value="8">Tranquility</option>
|
||||
<option value="9">Gemstone</option>
|
||||
<option value="10">USA!</option>
|
||||
<option value="11">Mardi Gras</option>
|
||||
<option value="12">Cool Caberet</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="onetouch">
|
||||
<item-type>Switch</item-type>
|
||||
<label>OneTouch</label>
|
||||
<description>OneTouch commands</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="equipment-switch">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Equipment Switch</label>
|
||||
<description>The current state of a equipment switch</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="equipment-heater">
|
||||
<item-type>String</item-type>
|
||||
<label>Heater</label>
|
||||
<description>The current state of the heater</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="off">Off</option>
|
||||
<option value="heating">Heating</option>
|
||||
<option value="enabled">Enabled</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="setpoint">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Setpoint</label>
|
||||
<category>Temperature</category>
|
||||
<state pattern="%.1f %unit%"/>
|
||||
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<category>Temperature</category>
|
||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature_scale" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Temperature Units</label>
|
||||
<description>The selected units for temperature (C or F)</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="chemical">
|
||||
<item-type>Number</item-type>
|
||||
<label>Chemical</label>
|
||||
<category>Other</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user