]> git.basschouten.com Git - openhab-addons.git/commitdiff
[haywardomnilogic] Replacement for Hayward Omnilogic Pool Automation Binding (#8685)
authorMatt <mmyers75@icloud.com>
Fri, 22 Jan 2021 22:32:52 +0000 (17:32 -0500)
committerGitHub <noreply@github.com>
Fri, 22 Jan 2021 22:32:52 +0000 (14:32 -0800)
* Initial Contribution

Signed-off-by: Matt Myers <mmyers75@icloud.com>
37 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.haywardomnilogic/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/README.md [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardAccount.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardException.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardThingHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardThingProperties.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardTypeToRequest.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/config/HaywardConfig.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/discovery/HaywardDiscoveryService.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBackyardHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBowHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBridgeHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardChlorinatorHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardColorLogicHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardFilterHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardHeaterHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardRelayHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardSensorHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardVirtualHeaterHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/binding/binding.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/backyard.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/bow.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/bridge.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/chlorinator.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/colorlogic.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/filter.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/heater.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/pump.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/relay.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/sensor.xml [new file with mode: 0644]
bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/virtualHeater.xml [new file with mode: 0644]
bundles/pom.xml

index 1f5619a95051fc0022261a1dc71d066439c9cb38..adc1b1474d378a59b464b949af02eda92bc9637a 100644 (file)
@@ -91,6 +91,7 @@
 /bundles/org.openhab.binding.gree/ @markus7017
 /bundles/org.openhab.binding.groheondus/ @FlorianSW
 /bundles/org.openhab.binding.harmonyhub/ @digitaldan
+/bundles/org.openhab.binding.haywardomnilogic/ @matchews
 /bundles/org.openhab.binding.hdanywhere/ @kgoderis
 /bundles/org.openhab.binding.hdpowerview/ @beowulfe
 /bundles/org.openhab.binding.helios/ @kgoderis
index b8219428eec776d648638a20c2d861cfc7a77be3..d418a1638e82c03a0d03ddd0694ef8088061b5be 100644 (file)
       <artifactId>org.openhab.binding.harmonyhub</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.binding.haywardomnilogic</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.hdanywhere</artifactId>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/NOTICE b/bundles/org.openhab.binding.haywardomnilogic/NOTICE
new file mode 100644 (file)
index 0000000..c5ec8e3
--- /dev/null
@@ -0,0 +1,14 @@
+  
+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
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.haywardomnilogic/README.md b/bundles/org.openhab.binding.haywardomnilogic/README.md
new file mode 100644 (file)
index 0000000..0d7fa6d
--- /dev/null
@@ -0,0 +1,171 @@
+# Hayward Omnilogic Binding
+
+The Hayward Omnilogic binding integrates the Omnilogic pool controller using the Hayward API.
+
+The Hayward Omnilogic API interacts with Hayward's cloud server requiring a connection with the Internet for sending and receiving information.
+
+## Supported Things
+
+The table below lists the Hayward OmniLogic binding thing types:
+
+| Things                       | Description                                                                     | Thing Type    |
+|------------------------------|---------------------------------------------------------------------------------|---------------|
+| Hayward OmniLogix Connection | Connection to Hayward's Server                                                  | bridge        |
+| Backyard                     | Backyard                                                                        | backyard      |
+| Body of Water                | Body of Water                                                                   | bow           |
+| Chlorinator                  | Chlorinator                                                                     | chlorinator   |
+| Colorlogic Light             | Colorlogic Light                                                                | colorlogic    |
+| Filter                       | Filter control                                                                  | filter        |
+| Heater Equipment             | Actual heater (i.e. gas, solar, electric)                                       | heater        |
+| Pump                         | Auxillary pump control (i.e. spillover)                                         | pump          |
+| Relay                        | Accessory relay control (deck jet sprinklers, lights, etc.)                     | relay         |
+| Virtaul Heater               | A Virtual Heater that can control all of the heater equipment based on priority | virtualHeater |
+
+## Discovery
+
+The binding will automatically discover the Omnilogic pool things from the cloud server using your Hayward Omnilogic credentials.
+
+## Thing Configuration
+
+Hayward OmniLogic Connection Parameters:
+
+| Property             | Default                                                        | Required | Description                                  |
+|----------------------|----------------------------------------------------------------|----------|----------------------------------------------|
+| Host Name            | https://app1.haywardomnilogic.com/HAAPI/HomeAutomation/API.ash | Yes      | Host name of the Hayward API server          |
+| User Name            | None                                                           | Yes      | Your Hayward User Name (not email address)   |
+| Password             | None                                                           | Yes      | Your Hayward User Password                   |
+| Telemetry Poll Delay | 12                                                             | Yes      | Telemetry Poll Delay (10-60 seconds)         |
+| Alarm Poll Delay     | 60                                                             | Yes      | Alarm Poll Delay (0-120 seconds, 0 disabled) |
+
+## Channels
+
+### Backyard Channels
+
+| backyardAirTemp | Number:Temperature | Backyard air temp sensor reading | R |
+|-----------------|--------------------|----------------------------------|:-:|
+| backyardStatus  | String             | Backyard status                  | R |
+| backyardState   | String             | Backyard state                   | R |
+| backyardAlarm1  | String             | Backyard alarm #1                | R |
+| backyardAlarm2  | String             | Backyard alarm #2                | R |
+| backyardAlarm3  | String             | Backyard alarm #3                | R |
+| backyardAlarm4  | String             | Backyard alarm #4                | R |
+| backyardAlarm5  | String             | Backyard alarm #5                | R |
+
+### Body of Water Channels
+
+| Channel Type ID | Item Type          | Description                        | Read Write |
+|-----------------|--------------------|------------------------------------|:----------:|
+| bowFlow         | Switch             | Body of Water flow sensor feedback |      R     |
+| bowWaterTemp    | Number:Temperature | Body of Water temperature          |      R     |
+
+### Chlorinator Channels
+
+| Channel Type ID       | Item Type            | Description                                              | Read Write |
+|-----------------------|----------------------|----------------------------------------------------------|:----------:|
+| chlorEnable           | Switch               | Chlorinator enable                                       |     R/W    |
+| chlorOperatingMode    | String               | Chlorinator operating mode                               |      R     |
+| chlorTimedPercent     | Number:Dimensionless | Chlorinator timed percent                                |     R/W    |
+| chlorOperatingState   | Number               | Chlorinator operating state                              |      R     |
+| chlorScMode           | String               | Chlorinator super chlorinate mode                        |      R     |
+| chlorError            | Number               | Chlorinator error                                        |      R     |
+| chlorAlert            | String               | Chlorinator alert                                        |      R     |
+| chlorAvgSaltLevel     | Number:Dimensionless | Chlorinator average salt level in Part per Million (ppm) |      R     |
+| chlorInstantSaltLevel | Number:Dimensionless | Chlorinator instant salt level in Part per Million (ppm) |      R     |
+| chlorStatus           | Number               | Chlorinator K1/K2 relay status                           |      R     |
+
+### Colorlogic Light Channels
+
+| Channel Type ID            | Item Type | Description                   | Read Write |
+|----------------------------|-----------|-------------------------------|:----------:|
+| colorLogicLightEnable      | Switch    | Colorlogic Light enable       |     R/W    |
+| colorLogicLightState       | String    | Colorlogic Light state        |      R     |
+| colorLogicLightCurrentShow | String    | Colorlogic Light current show |     R/W    |
+
+### Filter Channels
+
+| Channel Type ID     | Item Type            | Description            | Read Write |
+|---------------------|----------------------|------------------------|:----------:|
+| filterEnable        | Switch               | Filter enable          |     R/W    |
+| filterValvePosition | String               | Filter valve position  |      R     |
+| filterSpeed         | Number:Dimensionless | Filter speed in %      |     R/W    |
+| filterState         | String               | Filter state           |      R     |
+| filterLastSpeed     | Number:Dimensionless | Filter last speed in % |      R     |
+
+### Heater Channels
+
+| Channel Type ID | Item Type | Description   | Read Write |
+|-----------------|-----------|---------------|:----------:|
+| heaterState     | Number    | Heater state  |      R     |
+| heaterEnable    | Switch    | Heater enable |      R     |
+
+### Pump Channels
+
+| Channel Type ID | Item Type            | Description     | Read Write |
+|-----------------|----------------------|-----------------|:----------:|
+| pumpEnable      | Switch               | Pump enable     |      R     |
+| pumpSpeed       | Number:Dimensionless | Pump speed in % |      R     |
+
+### Relay Channels
+
+| Channel Type ID | Item Type | Description | Read Write |
+|-----------------|-----------|-------------|:----------:|
+| relayState      | Switch    | Relay state |     R/W    |
+
+### Virtual Heater Channels
+
+| Channel Type ID       | Item Type          | Description             | Read Write |
+|-----------------------|--------------------|-------------------------|:----------:|
+| heaterEnable          | Number             | Heater enable           |      R     |
+| heaterCurrentSetpoint | Number:Temperature | Heater Current Setpoint |     R/W    |
+
+## Full Example
+
+After installing the binding, you will need to manually add the Hayward Connection thing and enter your credentials.
+All pool items can be autmatically discovered by scanning the bridge
+Goto the inbox and add the things.
+
+### demo.items:
+
+```text
+Group gPool "Pool" ["Location"]
+
+Group gHaywardChlorinator "Hayward Chlorinator" (gPool) ["Equipment"] 
+Switch               HaywardChlorinator_Power            "Power"                (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorEnable" }           
+String               HaywardChlorinator_OperatingMode    "Operating Mode"       (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorOperatingMode" }    
+Number:Dimensionless HaywardChlorinator_SaltOutput       "Salt Output (%)"      (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorTimedPercent" }     
+String               HaywardChlorinator_scMode           "scMode"               (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorScMode" }           
+Number               HaywardChlorinator_ChlorinatorError "Chlorinator Error"    (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorError" }            
+String               HaywardChlorinator_ChlorinatorAlert "Chlorinator Alert"    (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorAlert" }            
+Number:Dimensionless HaywardChlorinator_AverageSaltLevel "Average Salt Level"   (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorAvgSaltLevel" }     
+Number:Dimensionless HaywardChlorinator_InstantSaltLevel "Instant Salt Level"   (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorInstantSaltLevel" } 
+Number               HaywardChlorinator_Status           "Status"               (gHaywardChlorinator) ["Point"]  { channel="haywardomnilogic:chlorinator:3766402f00:34:chlorStatus" }           
+
+
+Group gHaywardBackyard "Hayward Backyard" (gPool) ["Equipment"]
+Number:Temperature  HaywardBackyard_AirTemp        "Air Temp"                   (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardAirTemp" } 
+String              HaywardBackyard_Status         "Status"                     (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardStatus" }  
+String              HaywardBackyard_State          "State"                      (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardState" }   
+String              HaywardBackyard_BackyardAlarm1 "Alarm"                      (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardAlarm1" }  
+String              HaywardBackyard_BackyardAlarm2 "Alarm"                      (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardAlarm2" }  
+String              HaywardBackyard_BackyardAlarm3 "Alarm"                      (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardAlarm3" }  
+String              HaywardBackyard_BackyardAlarm4 "Alarm"                      (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardAlarm4" }  
+String              HaywardBackyard_BackyardAlarm5 "Alarm"                      (gHaywardBackyard) ["Point"]  { channel="haywardomnilogic:backyard:3766402f00:35940:backyardAlarm5" }  
+
+Group gHaywardGas "Hayward Gas" (gPool) ["Equipment"]
+Number              HaywardGas_HeaterState  "Heater State"                      (gHaywardGas) ["Point"]  { channel="haywardomnilogic:heater:3766402f00:33:heaterState" }  
+Switch              HaywardGas_HeaterEnable "Heater Enable"                     (gHaywardGas) ["Point"]  { channel="haywardomnilogic:heater:3766402f00:33:heaterEnable" } 
+
+Group gHaywardJets "Hayward Jets" (gPool) ["Equipment"]
+Switch              HaywardJets_Power "Power"                                   (gHaywardJets) ["Point"]  { channel="haywardomnilogic:relay:3766402f00:37:relayState" } 
+
+Group gHaywardPool "Hayward Pool" (gPool) ["Equipment"]
+Switch              HaywardPool_FlowSensor "Flow Sensor"                        (gHaywardPool) ["Point"]  { channel="haywardomnilogic:bow:3766402f00:30:bowFlow" }      
+Number:Temperature  HaywardPool_WaterTemp  "Water Temp"                         (gHaywardPool) ["Point"]  { channel="haywardomnilogic:bow:3766402f00:30:bowWaterTemp" } 
+
+Group gHaywardPoolLight "Hayward Pool Light" (gPool) ["Equipment"]
+Switch              HaywardPoolLight_Power       "Power"                        (gHaywardPoolLight) ["Point"]  { channel="haywardomnilogic:colorlogic:3766402f00:38:colorLogicLightEnable" }      
+String              HaywardPoolLight_LightState  "Light State"                  (gHaywardPoolLight) ["Point"]  { channel="haywardomnilogic:colorlogic:3766402f00:38:colorLogicLightState" }       
+String              HaywardPoolLight_CurrentShow "Current Show"                 (gHaywardPoolLight) ["Point"]  { channel="haywardomnilogic:colorlogic:3766402f00:38:colorLogicLightCurrentShow" } 
+
+```
+
diff --git a/bundles/org.openhab.binding.haywardomnilogic/pom.xml b/bundles/org.openhab.binding.haywardomnilogic/pom.xml
new file mode 100644 (file)
index 0000000..68bb5c0
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.openhab.addons.bundles</groupId>
+    <artifactId>org.openhab.addons.reactor.bundles</artifactId>
+    <version>3.1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.openhab.binding.haywardomnilogic</artifactId>
+
+  <name>openHAB Add-ons :: Bundles :: Hayward OmniLogic Binding</name>
+
+</project>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardAccount.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardAccount.java
new file mode 100644 (file)
index 0000000..6b60270
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HaywardAccount} class contains fields mapping thing configuration parameters.
+ *
+ * @author Matt Myers - Initial contribution
+ */
+
+@NonNullByDefault
+public class HaywardAccount {
+    public String token = "";
+    public String mspSystemID = "";
+    public String userID = "";
+    public String backyardName = "";
+    public String address = "";
+    public String firstName = "";
+    public String lastName = "";
+    public String roleType = "";
+    public String units = "";
+    public String vspSpeedFormat = "";
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardBindingConstants.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardBindingConstants.java
new file mode 100644 (file)
index 0000000..2346547
--- /dev/null
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.openhab.binding.haywardomnilogic.internal;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link HaywardBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardBindingConstants {
+
+    private static final String BINDING_ID = "haywardomnilogic";
+
+    // List of all Thing Type UIDs
+    public static final ThingTypeUID THING_TYPE_BACKYARD = new ThingTypeUID(BINDING_ID, "backyard");
+    public static final ThingTypeUID THING_TYPE_BOW = new ThingTypeUID(BINDING_ID, "bow");
+    public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
+    public static final ThingTypeUID THING_TYPE_CHLORINATOR = new ThingTypeUID(BINDING_ID, "chlorinator");
+    public static final ThingTypeUID THING_TYPE_COLORLOGIC = new ThingTypeUID(BINDING_ID, "colorlogic");
+    public static final ThingTypeUID THING_TYPE_FILTER = new ThingTypeUID(BINDING_ID, "filter");
+    public static final ThingTypeUID THING_TYPE_HEATER = new ThingTypeUID(BINDING_ID, "heater");
+    public static final ThingTypeUID THING_TYPE_PUMP = new ThingTypeUID(BINDING_ID, "pump");
+    public static final ThingTypeUID THING_TYPE_RELAY = new ThingTypeUID(BINDING_ID, "relay");
+    public static final ThingTypeUID THING_TYPE_SENSOR = new ThingTypeUID(BINDING_ID, "sensor");
+    public static final ThingTypeUID THING_TYPE_VIRTUALHEATER = new ThingTypeUID(BINDING_ID, "virtualHeater");
+
+    public static final Set<ThingTypeUID> BRIDGE_THING_TYPES_UIDS = Set.of(THING_TYPE_BRIDGE);
+
+    public static final Set<ThingTypeUID> THING_TYPES_UIDS = Set.of(HaywardBindingConstants.THING_TYPE_BACKYARD,
+            HaywardBindingConstants.THING_TYPE_BOW, HaywardBindingConstants.THING_TYPE_BRIDGE,
+            HaywardBindingConstants.THING_TYPE_CHLORINATOR, HaywardBindingConstants.THING_TYPE_COLORLOGIC,
+            HaywardBindingConstants.THING_TYPE_FILTER, HaywardBindingConstants.THING_TYPE_HEATER,
+            HaywardBindingConstants.THING_TYPE_PUMP, HaywardBindingConstants.THING_TYPE_RELAY,
+            HaywardBindingConstants.THING_TYPE_SENSOR, HaywardBindingConstants.THING_TYPE_VIRTUALHEATER);
+
+    // List of all Channel ids (bridge)
+    // No Channels
+
+    // List of all Channel ids (backyard)
+    public static final String CHANNEL_BACKYARD_AIRTEMP = "backyardAirTemp";
+    public static final String CHANNEL_BACKYARD_STATUS = "backyardStatus";
+    public static final String CHANNEL_BACKYARD_STATE = "backyardState";
+
+    // List of all Channel ids (bow)
+    public static final String CHANNEL_BOW_WATERTEMP = "bowWaterTemp";
+    public static final String CHANNEL_BOW_FLOW = "bowFlow";
+
+    // List of all Channel ids (chlorinator)
+    public static final String CHANNEL_CHLORINATOR_ENABLE = "chlorEnable";
+    public static final String CHANNEL_CHLORINATOR_OPERATINGMODE = "chlorOperatingMode";
+    public static final String CHANNEL_CHLORINATOR_TIMEDPERCENT = "chlorTimedPercent";
+    public static final String CHANNEL_CHLORINATOR_SCMODE = "chlorScMode";
+    public static final String CHANNEL_CHLORINATOR_ERROR = "chlorError";
+    public static final String CHANNEL_CHLORINATOR_ALERT = "chlorAlert";
+    public static final String CHANNEL_CHLORINATOR_AVGSALTLEVEL = "chlorAvgSaltLevel";
+    public static final String CHANNEL_CHLORINATOR_INSTANTSALTLEVEL = "chlorInstantSaltLevel";
+    public static final String CHANNEL_CHLORINATOR_STATUS = "chlorStatus";
+
+    // List of all Channel ids (colorlogic)
+    public static final String CHANNEL_COLORLOGIC_ENABLE = "colorLogicLightEnable";
+    public static final String CHANNEL_COLORLOGIC_LIGHTSTATE = "colorLogicLightState";
+    public static final String CHANNEL_COLORLOGIC_CURRENTSHOW = "colorLogicLightCurrentShow";
+
+    // List of all Channel ids (filter)
+    public static final String CHANNEL_FILTER_ENABLE = "filterEnable";
+    public static final String CHANNEL_FILTER_VALVEPOSITION = "filterValvePosition";
+    public static final String CHANNEL_FILTER_SPEED = "filterSpeed";
+    public static final String CHANNEL_FILTER_STATE = "filterState";
+    public static final String CHANNEL_FILTER_LASTSPEED = "filterLastSpeed";
+
+    public static final String PROPERTY_FILTER_MINPUMPSPEED = "Min Pump Percent";
+    public static final String PROPERTY_FILTER_MAXPUMPSPEED = "Max Pump Percent";
+    public static final String PROPERTY_FILTER_MINPUMPRPM = "Min Pump RPM";
+    public static final String PROPERTY_FILTER_MAXPUMPRPM = "Max Pump RPM";
+
+    // List of all Channel ids (heater)
+    public static final String CHANNEL_HEATER_STATE = "heaterState";
+    public static final String CHANNEL_HEATER_TEMP = "heaterTemp";
+    public static final String CHANNEL_HEATER_ENABLE = "heaterEnable";
+
+    // List of all Channel ids (pump)
+    public static final String CHANNEL_PUMP_ENABLE = "pumpEnable";
+    public static final String CHANNEL_PUMP_SPEED = "pumpSpeed";
+
+    public static final String PROPERTY_PUMP_MINPUMPSPEED = "Min Pump Speed";
+    public static final String PROPERTY_PUMP_MAXPUMPSPEED = "Min Pump Speed";
+    public static final String PROPERTY_PUMP_MINPUMPRPM = "Min Pump RPM";
+    public static final String PROPERTY_PUMP_MAXPUMPRPM = "Max Pump RPM";
+
+    // List of all Channel ids (relay)
+    public static final String CHANNEL_RELAY_STATE = "relayState";
+
+    // List of all Channel ids (sensor)
+    public static final String CHANNEL_SENSOR_DATA = "sensorData";
+
+    // List of all Channel ids (virtualHeater)
+    public static final String CHANNEL_VIRTUALHEATER_CURRENTSETPOINT = "virtualHeaterCurrentSetpoint";
+    public static final String CHANNEL_VIRTUALHEATER_ENABLE = "virtualHeaterEnable";
+
+    // The properties associated with all things
+    public static final String PROPERTY_SYSTEM_ID = "Property system ID";
+    public static final String PROPERTY_TYPE = "propertyType";
+    public static final String PROPERTY_BOWNAME = "BOW Name";
+    public static final String PROPERTY_BOWID = "BOW ID";
+
+    // Hayward Command html
+    public static final String COMMAND_PARAMETERS = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Request>";
+
+    public static final String COMMAND_SCHEDULE = "<Parameter name=\"IsCountDownTimer\" dataType=\"bool\">false</Parameter>"
+            + "<Parameter name=\"StartTimeHours\" dataType=\"int\">0</Parameter>"
+            + "<Parameter name=\"StartTimeMinutes\" dataType=\"int\">0</Parameter>"
+            + "<Parameter name=\"EndTimeHours\" dataType=\"int\">0</Parameter>"
+            + "<Parameter name=\"EndTimeMinutes\" dataType=\"int\">0</Parameter>"
+            + "<Parameter name=\"DaysActive\" dataType=\"int\">0</Parameter>"
+            + "<Parameter name=\"Recurring\" dataType=\"bool\">false</Parameter>";
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardException.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardException.java
new file mode 100644 (file)
index 0000000..4553a4c
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HaywardException} is thrown during the getMspConfig, mspConfigDiscovery, getTelemetry,
+ * evaluateXPath and httpXmlResponse methods
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardException extends Exception {
+
+    /**
+     * The {@link HaywardException} is thrown by getMspConfig() and mspConfigDiscovery()
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Constructor.
+     *
+     * @param message Hayward error message
+     */
+    public HaywardException(String message) {
+        super(message);
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardHandlerFactory.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardHandlerFactory.java
new file mode 100644 (file)
index 0000000..cba37b0
--- /dev/null
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal;
+
+import static org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants.*;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardBackyardHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardBowHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardBridgeHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardChlorinatorHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardColorLogicHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardFilterHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardHeaterHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardRelayHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardSensorHandler;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardVirtualHeaterHandler;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link HaywardHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Matt Myers - Initial contribution
+ */
+
+@Component(service = ThingHandlerFactory.class, configurationPid = "binding.haywardomnilogic")
+@NonNullByDefault
+public class HaywardHandlerFactory extends BaseThingHandlerFactory {
+
+    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
+            Stream.concat(BRIDGE_THING_TYPES_UIDS.stream(), THING_TYPES_UIDS.stream()).collect(Collectors.toSet()));
+    private final HttpClient httpClient;
+
+    @Override
+    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+        return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+    }
+
+    @Activate
+    public HaywardHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
+        this.httpClient = httpClientFactory.getCommonHttpClient();
+    }
+
+    /**
+     * Creates the specific handler for this thing.
+     */
+    @Override
+    protected @Nullable ThingHandler createHandler(Thing thing) {
+        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_BRIDGE)) {
+            return new HaywardBridgeHandler((Bridge) thing, httpClient);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_BACKYARD)) {
+            return new HaywardBackyardHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_BOW)) {
+            return new HaywardBowHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_CHLORINATOR)) {
+            return new HaywardChlorinatorHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_COLORLOGIC)) {
+            return new HaywardColorLogicHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_FILTER)) {
+            return new HaywardFilterHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_HEATER)) {
+            return new HaywardHeaterHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_RELAY)) {
+            return new HaywardRelayHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_SENSOR)) {
+            return new HaywardSensorHandler(thing);
+        }
+        if (thingTypeUID.equals(HaywardBindingConstants.THING_TYPE_VIRTUALHEATER)) {
+            return new HaywardVirtualHeaterHandler(thing);
+        }
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardThingHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardThingHandler.java
new file mode 100644 (file)
index 0000000..89f311f
--- /dev/null
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.openhab.binding.haywardomnilogic.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardBridgeHandler;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.ImperialUnits;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
+
+/**
+ * The {@link HaywarThingHandler} is a subclass of the BaseThingHandler and a Super
+ * Class to each Hayward Thing Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+
+@NonNullByDefault
+public abstract class HaywardThingHandler extends BaseThingHandler {
+
+    public HaywardThingHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void initialize() {
+        updateStatus(ThingStatus.ONLINE);
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+    }
+
+    public abstract void getTelemetry(String xmlResponse) throws HaywardException;
+
+    public State toState(String type, String channelID, String value) throws NumberFormatException {
+        switch (type) {
+            case "Number":
+                return new DecimalType(value);
+            case "Switch":
+            case "system.power":
+                return Integer.parseInt(value) > 0 ? OnOffType.ON : OnOffType.OFF;
+            case "Number:Dimensionless":
+                switch (channelID) {
+                    case "chlorTimedPercent":
+                    case "filterSpeed":
+                    case "pumpSpeed":
+                    case "filterLastSpeed":
+                        return new QuantityType<>(Integer.parseInt(value), Units.PERCENT);
+                    case "chlorAvgSaltLevel":
+                    case "chlorInstantSaltLevel":
+                        return new QuantityType<>(Integer.parseInt(value), Units.PARTS_PER_MILLION);
+                }
+                return StringType.valueOf(value);
+            case "Number:Temperature":
+                Bridge bridge = getBridge();
+                if (bridge != null) {
+                    HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+                    if (bridgehandler != null) {
+                        if (bridgehandler.account.units.equals("Standard")) {
+                            return new QuantityType<>(Integer.parseInt(value), ImperialUnits.FAHRENHEIT);
+                        } else {
+                            return new QuantityType<>(Integer.parseInt(value), SIUnits.CELSIUS);
+                        }
+                    }
+                }
+                // default to imperial if no bridge
+                return new QuantityType<>(Integer.parseInt(value), ImperialUnits.FAHRENHEIT);
+            default:
+                return StringType.valueOf(value);
+        }
+    }
+
+    public String cmdToString(Command command) {
+        if (command == OnOffType.OFF) {
+            return "0";
+        } else if (command == OnOffType.ON) {
+            return "1";
+        } else if (command instanceof DecimalType) {
+            return ((DecimalType) command).toString();
+        } else if (command instanceof QuantityType) {
+            return ((QuantityType<?>) command).format("%1.0f");
+        } else {
+            return command.toString();
+        }
+    }
+
+    public void updateData(String channelID, String data) {
+        Channel chan = getThing().getChannel(channelID);
+        if (chan != null) {
+            String acceptedItemType = chan.getAcceptedItemType();
+            if (acceptedItemType != null) {
+                State state = toState(acceptedItemType, channelID, data);
+                updateState(chan.getUID(), state);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardThingProperties.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardThingProperties.java
new file mode 100644 (file)
index 0000000..06692f9
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HaywardThingProperties} class contains fields mapping thing configuration parameters.
+ *
+ * @author Matt Myers - Initial contribution
+ */
+
+@NonNullByDefault
+public class HaywardThingProperties {
+    public String systemID = "";
+    public String poolID = "";
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardTypeToRequest.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/HaywardTypeToRequest.java
new file mode 100644 (file)
index 0000000..6eaff76
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The type to request.
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public enum HaywardTypeToRequest {
+    BACKYARD,
+    BOW,
+    CHLORINATOR,
+    COLORLOGIC,
+    CSAD,
+    FILTER,
+    HEATER,
+    PUMP,
+    RELAY,
+    SENSOR,
+    VIRTUALHEATER
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/config/HaywardConfig.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/config/HaywardConfig.java
new file mode 100644 (file)
index 0000000..8b8a749
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link HaywardConfig} class contains fields mapping thing configuration parameters.
+ *
+ * @author Matt Myers - Initial contribution
+ */
+
+@NonNullByDefault
+public class HaywardConfig {
+    public String endpointUrl = "";
+    public String username = "";
+    public String password = "";
+    public int alarmPollTime = 60;
+    public int telemetryPollTime = 10;
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/discovery/HaywardDiscoveryService.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/discovery/HaywardDiscoveryService.java
new file mode 100644 (file)
index 0000000..93fe883
--- /dev/null
@@ -0,0 +1,230 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.discovery;
+
+import static org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants.THING_TYPES_UIDS;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardTypeToRequest;
+import org.openhab.binding.haywardomnilogic.internal.handler.HaywardBridgeHandler;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Sets up the discovery results and details
+ *
+ * @author Matt Myers - Initial contribution
+ */
+
+@NonNullByDefault
+public class HaywardDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService {
+    private final Logger logger = LoggerFactory.getLogger(HaywardDiscoveryService.class);
+    private @Nullable HaywardBridgeHandler discoveryBridgehandler;
+
+    public HaywardDiscoveryService() {
+        super(THING_TYPES_UIDS, 0, false);
+    }
+
+    @Override
+    public void activate() {
+        super.activate(null);
+    }
+
+    @Override
+    public void deactivate() {
+        super.deactivate();
+    }
+
+    @Override
+    protected void startScan() {
+        HaywardBridgeHandler bridgehandler = discoveryBridgehandler;
+        try {
+            if (bridgehandler != null) {
+                String xmlResults = bridgehandler.getMspConfig();
+                mspConfigDiscovery(xmlResults);
+            }
+        } catch (HaywardException e) {
+            logger.warn("Exception during discovery scan: {}", e.getMessage());
+        } catch (InterruptedException e) {
+            return;
+        }
+    }
+
+    public synchronized void mspConfigDiscovery(String xmlResponse) {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> names = new ArrayList<>();
+        Map<String, Object> backyardProperties = new HashMap<>();
+        Map<String, Object> bowProperties = new HashMap<>();
+        HaywardBridgeHandler bridgehandler = discoveryBridgehandler;
+
+        if (bridgehandler == null) {
+            return;
+        }
+
+        // Find Backyard
+        names = bridgehandler.evaluateXPath("//Backyard/Name/text()", xmlResponse);
+
+        for (int i = 0; i < names.size(); i++) {
+            backyardProperties.put(HaywardBindingConstants.PROPERTY_TYPE, HaywardTypeToRequest.BACKYARD);
+            backyardProperties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, bridgehandler.account.mspSystemID);
+
+            onDeviceDiscovered(HaywardBindingConstants.THING_TYPE_BACKYARD, names.get(i), backyardProperties);
+        }
+
+        // Find Bodies of Water
+        systemIDs = bridgehandler.evaluateXPath("//Body-of-water/System-Id/text()", xmlResponse);
+        names = bridgehandler.evaluateXPath("//Body-of-water/Name/text()", xmlResponse);
+
+        for (int i = 0; i < systemIDs.size(); i++) {
+            bowProperties.put(HaywardBindingConstants.PROPERTY_TYPE, HaywardTypeToRequest.BOW);
+            bowProperties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, systemIDs.get(i));
+
+            onDeviceDiscovered(HaywardBindingConstants.THING_TYPE_BOW, names.get(i), bowProperties);
+        }
+
+        // Find Chlorinators
+        discoverDevices(bridgehandler, xmlResponse, "Chlorinator", HaywardTypeToRequest.CHLORINATOR,
+                HaywardBindingConstants.THING_TYPE_CHLORINATOR, null);
+
+        // Find ColorLogic Lights
+        discoverDevices(bridgehandler, xmlResponse, "ColorLogic-Light", HaywardTypeToRequest.COLORLOGIC,
+                HaywardBindingConstants.THING_TYPE_COLORLOGIC, null);
+
+        // Find Filters
+        final List<String> filterProperty1 = bridgehandler.evaluateXPath("//Filter/Min-Pump-Speed/text()", xmlResponse);
+        final List<String> filterProperty2 = bridgehandler.evaluateXPath("//Filter/Max-Pump-Speed/text()", xmlResponse);
+        final List<String> filterProperty3 = bridgehandler.evaluateXPath("//Filter/Min-Pump-RPM/text()", xmlResponse);
+        final List<String> filterProperty4 = bridgehandler.evaluateXPath("//Filter/Max-Pump-RPM/text()", xmlResponse);
+
+        discoverDevices(bridgehandler, xmlResponse, "Filter", HaywardTypeToRequest.FILTER,
+                HaywardBindingConstants.THING_TYPE_FILTER, (props, i) -> {
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MINPUMPSPEED, filterProperty1.get(i));
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXPUMPSPEED, filterProperty2.get(i));
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MINPUMPRPM, filterProperty3.get(i));
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXPUMPRPM, filterProperty4.get(i));
+                });
+
+        // Find Heaters
+        discoverDevices(bridgehandler, xmlResponse, "Heater-Equipment", HaywardTypeToRequest.HEATER,
+                HaywardBindingConstants.THING_TYPE_HEATER, null);
+
+        // Find Pumps
+        final List<String> pumpProperty1 = bridgehandler.evaluateXPath("//Pump/Min-Pump-Speed/text()", xmlResponse);
+        final List<String> pumpProperty2 = bridgehandler.evaluateXPath("//Pump/Max-Pump-Speed/text()", xmlResponse);
+        final List<String> pumpProperty3 = bridgehandler.evaluateXPath("//Pump/Min-Pump-RPM/text()", xmlResponse);
+        final List<String> pumpProperty4 = bridgehandler.evaluateXPath("//Pump/Max-Pump-RPM/text()", xmlResponse);
+
+        discoverDevices(bridgehandler, xmlResponse, "Pump", HaywardTypeToRequest.PUMP,
+                HaywardBindingConstants.THING_TYPE_FILTER, (props, i) -> {
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MINPUMPSPEED, pumpProperty1.get(i));
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXPUMPSPEED, pumpProperty2.get(i));
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MINPUMPRPM, pumpProperty3.get(i));
+                    props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXPUMPRPM, pumpProperty4.get(i));
+                });
+
+        // Find Relays
+        discoverDevices(bridgehandler, xmlResponse, "Relay", HaywardTypeToRequest.RELAY,
+                HaywardBindingConstants.THING_TYPE_RELAY, null);
+
+        // Find Virtual Heaters
+        discoverDevices(bridgehandler, xmlResponse, "Heater", HaywardTypeToRequest.VIRTUALHEATER,
+                HaywardBindingConstants.THING_TYPE_VIRTUALHEATER, null);
+
+        // Find Sensors
+        discoverDevices(bridgehandler, xmlResponse, "Sensor", HaywardTypeToRequest.SENSOR,
+                HaywardBindingConstants.THING_TYPE_SENSOR, null);
+    }
+
+    private void discoverDevices(HaywardBridgeHandler bridgehandler, String xmlResponse, String xmlSearchTerm,
+            HaywardTypeToRequest type, ThingTypeUID thingType,
+            @Nullable BiConsumer<Map<String, Object>, Integer> additionalPropertyConsumer) {
+        List<String> systemIDs = bridgehandler.evaluateXPath("//" + xmlSearchTerm + "/System-Id/text()", xmlResponse);
+        List<String> names;
+
+        // Set Virtual Heater Name
+        if (thingType == HaywardBindingConstants.THING_TYPE_VIRTUALHEATER) {
+            names = new ArrayList<>(systemIDs);
+            Collections.fill(names, "Heater");
+        } else {
+            names = bridgehandler.evaluateXPath("//" + xmlSearchTerm + "/Name/text()", xmlResponse);
+        }
+
+        for (int i = 0; i < systemIDs.size(); i++) {
+            // get Body of Water for each item
+            List<String> bowID = bridgehandler.evaluateXPath(
+                    "//*[System-Id=" + systemIDs.get(i) + "]/ancestor::Body-of-water/System-Id/text()", xmlResponse);
+            List<String> bowName = bridgehandler.evaluateXPath(
+                    "//*[System-Id=" + systemIDs.get(i) + "]/ancestor::Body-of-water/Name/text()", xmlResponse);
+
+            // skip system sensors with no BOW
+            if (bowID.isEmpty()) {
+                continue;
+            }
+
+            Map<String, Object> properties = new HashMap<>();
+            properties.put(HaywardBindingConstants.PROPERTY_TYPE, type);
+            properties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, systemIDs.get(i));
+            properties.put(HaywardBindingConstants.PROPERTY_BOWID, bowID.get(0));
+            properties.put(HaywardBindingConstants.PROPERTY_BOWNAME, bowName.get(0));
+            if (additionalPropertyConsumer != null) {
+                additionalPropertyConsumer.accept(properties, i);
+            }
+            onDeviceDiscovered(thingType, names.get(i), properties);
+        }
+    }
+
+    public void onDeviceDiscovered(ThingTypeUID thingType, String label, Map<String, Object> properties) {
+        HaywardBridgeHandler bridgehandler = discoveryBridgehandler;
+        String systemID = (String) properties.get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
+        if (bridgehandler != null) {
+            if (systemID != null) {
+                ThingUID thingUID = new ThingUID(thingType, bridgehandler.getThing().getUID(), systemID);
+                DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
+                        .withBridge(bridgehandler.getThing().getUID())
+                        .withRepresentationProperty(HaywardBindingConstants.PROPERTY_SYSTEM_ID)
+                        .withLabel("Hayward " + label).withProperties(properties).build();
+                thingDiscovered(result);
+            }
+        }
+    }
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        if (handler instanceof HaywardBridgeHandler) {
+            this.discoveryBridgehandler = (HaywardBridgeHandler) handler;
+        }
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return discoveryBridgehandler;
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBackyardHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBackyardHandler.java
new file mode 100644 (file)
index 0000000..0419770
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Backyard Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardBackyardHandler extends HaywardThingHandler {
+    private final Logger logger = LoggerFactory.getLogger(HaywardBackyardHandler.class);
+
+    public HaywardBackyardHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> data = new ArrayList<>();
+        List<String> systemIDs = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//Backyard/@systemId", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        // Air temp
+                        data = bridgehandler.evaluateXPath("//Backyard/@airTemp", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_BACKYARD_AIRTEMP, data.get(0));
+
+                        // Status
+                        data = bridgehandler.evaluateXPath("//Backyard/@status", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_BACKYARD_STATUS, data.get(0));
+
+                        // State
+                        data = bridgehandler.evaluateXPath("//Backyard/@state", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_BACKYARD_STATE, data.get(0));
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+
+    public boolean getAlarmList(String systemID) throws HaywardException {
+        List<String> bowID = new ArrayList<>();
+        List<String> parameter1 = new ArrayList<>();
+        List<String> message = new ArrayList<>();
+        String alarmStr;
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                // *****Request Alarm List from Hayward server
+                String urlParameters = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Request><Name>GetAlarmList</Name><Parameters>"
+                        + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                        + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                        + bridgehandler.account.mspSystemID + "</Parameter>"
+                        + "<Parameter name=\"CultureInfoName\" dataType=\"String\">en-us</Parameter></Parameters></Request>";
+
+                try {
+                    String xmlResponse = bridgehandler.httpXmlResponse(urlParameters);
+
+                    if (xmlResponse.isEmpty()) {
+                        logger.debug("Hayward getAlarmList XML response was empty");
+                        return false;
+                    }
+
+                    String status = bridgehandler
+                            .evaluateXPath("/Response/Parameters//Parameter[@name='Status']/text()", xmlResponse)
+                            .get(0);
+
+                    if (!(status.equals("0"))) {
+                        logger.trace("Hayward getAlarm XML response: {}", xmlResponse);
+                        return false;
+                    }
+
+                    bowID = bridgehandler.evaluateXPath("//Property[@name='BowID']/text()", xmlResponse);
+                    parameter1 = bridgehandler.evaluateXPath("//Property[@name='Parameter1']/text()", xmlResponse);
+                    message = bridgehandler.evaluateXPath("//Property[@name='Message']/text()", xmlResponse);
+
+                    for (int i = 0; i < 5; i++) {
+                        if (i < bowID.size()) {
+                            alarmStr = parameter1.get(i) + ": " + message.get(i);
+                        } else {
+                            alarmStr = "";
+                        }
+                        updateData("backyardAlarm" + String.format("%01d", i + 1), alarmStr);
+                    }
+                    this.updateStatus(ThingStatus.ONLINE);
+                    return true;
+                } catch (InterruptedException e) {
+                    return false;
+                }
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+                return false;
+            }
+        } else {
+            this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            return false;
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBowHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBowHandler.java
new file mode 100644 (file)
index 0000000..1174117
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+
+/**
+ * The Body of Water Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardBowHandler extends HaywardThingHandler {
+
+    public HaywardBowHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//BodyOfWater/@systemId", xmlResponse);
+
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        // Flow
+                        data = bridgehandler.evaluateXPath("//BodyOfWater/@flow", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_BOW_FLOW, data.get(i));
+
+                        // Water Temp
+                        data = bridgehandler.evaluateXPath("//BodyOfWater/@waterTemp", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_BOW_WATERTEMP, data.get(i));
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBridgeHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardBridgeHandler.java
new file mode 100644 (file)
index 0000000..ebaba9f
--- /dev/null
@@ -0,0 +1,475 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpVersion;
+import org.openhab.binding.haywardomnilogic.internal.HaywardAccount;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.binding.haywardomnilogic.internal.HaywardTypeToRequest;
+import org.openhab.binding.haywardomnilogic.internal.config.HaywardConfig;
+import org.openhab.binding.haywardomnilogic.internal.discovery.HaywardDiscoveryService;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * The {@link HaywardBridgeHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Matt Myers - Initial contribution
+ */
+
+@NonNullByDefault
+public class HaywardBridgeHandler extends BaseBridgeHandler {
+    private final Logger logger = LoggerFactory.getLogger(HaywardBridgeHandler.class);
+    private final HttpClient httpClient;
+    private @Nullable ScheduledFuture<?> initializeFuture;
+    private @Nullable ScheduledFuture<?> pollTelemetryFuture;
+    private @Nullable ScheduledFuture<?> pollAlarmsFuture;
+    private int commFailureCount;
+    public HaywardConfig config = getConfig().as(HaywardConfig.class);
+    public HaywardAccount account = getConfig().as(HaywardAccount.class);
+
+    @Override
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return Collections.singleton(HaywardDiscoveryService.class);
+    }
+
+    public HaywardBridgeHandler(Bridge bridge, HttpClient httpClient) {
+        super(bridge);
+        this.httpClient = httpClient;
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+    }
+
+    @Override
+    public void dispose() {
+        clearPolling(initializeFuture);
+        clearPolling(pollTelemetryFuture);
+        clearPolling(pollAlarmsFuture);
+        logger.trace("Hayward polling cancelled");
+        super.dispose();
+    }
+
+    @Override
+    public void initialize() {
+        updateStatus(ThingStatus.UNKNOWN);
+        initializeFuture = scheduler.schedule(this::scheduledInitialize, 1, TimeUnit.SECONDS);
+        return;
+    }
+
+    public void scheduledInitialize() {
+        config = getConfigAs(HaywardConfig.class);
+
+        try {
+            clearPolling(pollTelemetryFuture);
+            clearPolling(pollAlarmsFuture);
+
+            if (!(login())) {
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                        "Unable to Login to Hayward's server");
+                clearPolling(pollTelemetryFuture);
+                clearPolling(pollAlarmsFuture);
+                commFailureCount = 50;
+                initPolling(60);
+                return;
+            }
+
+            if (!(getSiteList())) {
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                        "Unable to getMSP from Hayward's server");
+                clearPolling(pollTelemetryFuture);
+                clearPolling(pollAlarmsFuture);
+                commFailureCount = 50;
+                initPolling(60);
+                return;
+            }
+
+            if (!(mspConfigUnits())) {
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                        "Unable to getMSPConfigUnits from Hayward's server");
+                clearPolling(pollTelemetryFuture);
+                clearPolling(pollAlarmsFuture);
+                commFailureCount = 50;
+                initPolling(60);
+                return;
+            }
+
+            updateStatus(ThingStatus.ONLINE);
+            logger.debug("Succesfully opened connection to Hayward's server: {} Username:{}", config.endpointUrl,
+                    config.username);
+
+            initPolling(0);
+            logger.trace("Hayward Telemetry polling scheduled");
+
+            if (config.alarmPollTime > 0) {
+                initAlarmPolling(1);
+            }
+        } catch (HaywardException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
+                    "scheduledInitialize exception: " + e.getMessage());
+            clearPolling(pollTelemetryFuture);
+            clearPolling(pollAlarmsFuture);
+            commFailureCount = 50;
+            initPolling(60);
+            return;
+        } catch (InterruptedException e) {
+            return;
+        }
+    }
+
+    public synchronized boolean login() throws HaywardException, InterruptedException {
+        String xmlResponse;
+        String status;
+
+        // *****Login to Hayward server
+        String urlParameters = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Request>" + "<Name>Login</Name><Parameters>"
+                + "<Parameter name=\"UserName\" dataType=\"String\">" + config.username + "</Parameter>"
+                + "<Parameter name=\"Password\" dataType=\"String\">" + config.password + "</Parameter>"
+                + "</Parameters></Request>";
+
+        xmlResponse = httpXmlResponse(urlParameters);
+
+        if (xmlResponse.isEmpty()) {
+            return false;
+        }
+
+        status = evaluateXPath("/Response/Parameters//Parameter[@name='Status']/text()", xmlResponse).get(0);
+
+        if (!(status.equals("0"))) {
+            logger.debug("Hayward Connection thing: Login XML response: {}", xmlResponse);
+            return false;
+        }
+
+        account.token = evaluateXPath("/Response/Parameters//Parameter[@name='Token']/text()", xmlResponse).get(0);
+        account.userID = evaluateXPath("/Response/Parameters//Parameter[@name='UserID']/text()", xmlResponse).get(0);
+        return true;
+    }
+
+    public synchronized boolean getApiDef() throws HaywardException, InterruptedException {
+        String xmlResponse;
+
+        // *****getConfig from Hayward server
+        String urlParameters = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Request><Name>GetAPIDef</Name><Parameters>"
+                + "<Parameter name=\"Token\" dataType=\"String\">" + account.token + "</Parameter>"
+                + "<Parameter name=\"MspSystemID\" dataType=\"int\">" + account.mspSystemID + "</Parameter>;"
+                + "<Parameter name=\"Version\" dataType=\"string\">0.4</Parameter >\r\n"
+                + "<Parameter name=\"Language\" dataType=\"string\">en</Parameter >\r\n" + "</Parameters></Request>";
+
+        xmlResponse = httpXmlResponse(urlParameters);
+
+        if (xmlResponse.isEmpty()) {
+            logger.debug("Hayward Connection thing: Login XML response was null");
+            return false;
+        }
+        return true;
+    }
+
+    public synchronized boolean getSiteList() throws HaywardException, InterruptedException {
+        String xmlResponse;
+        String status;
+
+        // *****Get MSP
+        String urlParameters = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Request><Name>GetSiteList</Name><Parameters>"
+                + "<Parameter name=\"Token\" dataType=\"String\">" + account.token
+                + "</Parameter><Parameter name=\"UserID\" dataType=\"String\">" + account.userID
+                + "</Parameter></Parameters></Request>";
+
+        xmlResponse = httpXmlResponse(urlParameters);
+
+        if (xmlResponse.isEmpty()) {
+            logger.debug("Hayward Connection thing: getSiteList XML response was null");
+            return false;
+        }
+
+        status = evaluateXPath("/Response/Parameters//Parameter[@name='Status']/text()", xmlResponse).get(0);
+
+        if (!(status.equals("0"))) {
+            logger.debug("Hayward Connection thing: getSiteList XML response: {}", xmlResponse);
+            return false;
+        }
+
+        account.mspSystemID = evaluateXPath("/Response/Parameters/Parameter/Item//Property[@name='MspSystemID']/text()",
+                xmlResponse).get(0);
+        account.backyardName = evaluateXPath(
+                "/Response/Parameters/Parameter/Item//Property[@name='BackyardName']/text()", xmlResponse).get(0);
+        account.address = evaluateXPath("/Response/Parameters/Parameter/Item//Property[@name='Address']/text()",
+                xmlResponse).get(0);
+        return true;
+    }
+
+    public synchronized String getMspConfig() throws HaywardException, InterruptedException {
+        // *****getMspConfig from Hayward server
+        String urlParameters = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Request><Name>GetMspConfigFile</Name><Parameters>"
+                + "<Parameter name=\"Token\" dataType=\"String\">" + account.token + "</Parameter>"
+                + "<Parameter name=\"MspSystemID\" dataType=\"int\">" + account.mspSystemID
+                + "</Parameter><Parameter name=\"Version\" dataType=\"string\">0</Parameter>\r\n"
+                + "</Parameters></Request>";
+
+        String xmlResponse = httpXmlResponse(urlParameters);
+
+        // Debug: Inject xml file for testing
+        // String path =
+        // "C:/Users/Controls/openhab-2-5-x/git/openhab-addons/bundles/org.openhab.binding.haywardomnilogic/getConfig.xml";
+        // xmlResponse = new String(Files.readAllBytes(Paths.get(path)));
+
+        if (xmlResponse.isEmpty()) {
+            logger.debug("Hayward Connection thing: requestConfig XML response was null");
+            return "Fail";
+        }
+
+        if (evaluateXPath("//Backyard/Name/text()", xmlResponse).isEmpty()) {
+            logger.debug("Hayward Connection thing: requestConfiguration XML response: {}", xmlResponse);
+            return "Fail";
+        }
+        return xmlResponse;
+    }
+
+    public synchronized boolean mspConfigUnits() throws HaywardException, InterruptedException {
+        List<String> property1 = new ArrayList<>();
+        List<String> property2 = new ArrayList<>();
+
+        String xmlResponse = getMspConfig();
+
+        // Get Units (Standard, Metric)
+        property1 = evaluateXPath("//System/Units/text()", xmlResponse);
+        account.units = property1.get(0);
+
+        // Get Variable Speed Pump Units (percent, RPM)
+        property2 = evaluateXPath("//System/Msp-Vsp-Speed-Format/text()", xmlResponse);
+        account.vspSpeedFormat = property2.get(0);
+
+        return true;
+    }
+
+    public synchronized boolean getTelemetryData() throws HaywardException, InterruptedException {
+        // *****getTelemetry from Hayward server
+        String urlParameters = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Request><Name>GetTelemetryData</Name><Parameters>"
+                + "<Parameter name=\"Token\" dataType=\"String\">" + account.token + "</Parameter>"
+                + "<Parameter name=\"MspSystemID\" dataType=\"int\">" + account.mspSystemID
+                + "</Parameter></Parameters></Request>";
+
+        String xmlResponse = httpXmlResponse(urlParameters);
+
+        if (xmlResponse.isEmpty()) {
+            logger.debug("Hayward Connection thing: getTelemetry XML response was null");
+            return false;
+        }
+
+        if (!evaluateXPath("/Response/Parameters//Parameter[@name='StatusMessage']/text()", xmlResponse).isEmpty()) {
+            logger.debug("Hayward Connection thing: getTelemetry XML response: {}", xmlResponse);
+            return false;
+        }
+
+        for (Thing thing : getThing().getThings()) {
+            if (thing.getHandler() instanceof HaywardThingHandler) {
+                HaywardThingHandler handler = (HaywardThingHandler) thing.getHandler();
+                if (handler != null) {
+                    handler.getTelemetry(xmlResponse);
+                }
+            }
+        }
+        return true;
+    }
+
+    public synchronized boolean getAlarmList() throws HaywardException {
+        for (Thing thing : getThing().getThings()) {
+            Map<String, String> properties = thing.getProperties();
+            if ("BACKYARD".equals(properties.get(HaywardBindingConstants.PROPERTY_TYPE))) {
+                HaywardBackyardHandler handler = (HaywardBackyardHandler) thing.getHandler();
+                if (handler != null) {
+                    String systemID = properties.get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
+                    if (systemID != null) {
+                        return handler.getAlarmList(systemID);
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private synchronized void initPolling(int initalDelay) {
+        pollTelemetryFuture = scheduler.scheduleWithFixedDelay(() -> {
+            try {
+                if (commFailureCount >= 5) {
+                    commFailureCount = 0;
+                    clearPolling(pollTelemetryFuture);
+                    clearPolling(pollAlarmsFuture);
+                    initialize();
+                    return;
+                }
+                if (!(getTelemetryData())) {
+                    commFailureCount++;
+                    return;
+                }
+            } catch (HaywardException e) {
+                logger.debug("Hayward Connection thing: Exception during poll: {}", e.getMessage());
+            } catch (InterruptedException e) {
+                return;
+            }
+        }, initalDelay, config.telemetryPollTime, TimeUnit.SECONDS);
+        return;
+    }
+
+    private synchronized void initAlarmPolling(int initalDelay) {
+        pollAlarmsFuture = scheduler.scheduleWithFixedDelay(() -> {
+            try {
+                getAlarmList();
+            } catch (HaywardException e) {
+                logger.debug("Hayward Connection thing: Exception during poll: {}", e.getMessage());
+            }
+        }, initalDelay, config.alarmPollTime, TimeUnit.SECONDS);
+    }
+
+    private void clearPolling(@Nullable ScheduledFuture<?> pollJob) {
+        if (pollJob != null) {
+            pollJob.cancel(false);
+        }
+    }
+
+    @Nullable
+    Thing getThingForType(HaywardTypeToRequest type, int num) {
+        for (Thing thing : getThing().getThings()) {
+            Map<String, String> properties = thing.getProperties();
+            if (Integer.toString(num).equals(properties.get(HaywardBindingConstants.PROPERTY_SYSTEM_ID))) {
+                if (type.toString().equals(properties.get(HaywardBindingConstants.PROPERTY_TYPE))) {
+                    return thing;
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<String> evaluateXPath(String xpathExp, String xmlResponse) {
+        List<String> values = new ArrayList<>();
+        try {
+            InputSource inputXML = new InputSource(new StringReader(xmlResponse));
+            XPath xPath = XPathFactory.newInstance().newXPath();
+            NodeList nodes = (NodeList) xPath.evaluate(xpathExp, inputXML, XPathConstants.NODESET);
+
+            for (int i = 0; i < nodes.getLength(); i++) {
+                values.add(nodes.item(i).getNodeValue());
+            }
+        } catch (XPathExpressionException e) {
+            logger.warn("XPathExpression exception: {}", e.getMessage());
+        }
+        return values;
+    }
+
+    private Request sendRequestBuilder(String url, HttpMethod method) {
+        return this.httpClient.newRequest(url).agent("NextGenForIPhone/16565 CFNetwork/887 Darwin/17.0.0")
+                .method(method).header(HttpHeader.ACCEPT_LANGUAGE, "en-us").header(HttpHeader.ACCEPT, "*/*")
+                .header(HttpHeader.ACCEPT_ENCODING, "gzip, deflate").version(HttpVersion.HTTP_1_1)
+                .header(HttpHeader.CONNECTION, "keep-alive").header(HttpHeader.HOST, "www.haywardomnilogic.com:80")
+                .timeout(10, TimeUnit.SECONDS);
+    }
+
+    public synchronized String httpXmlResponse(String urlParameters) throws HaywardException, InterruptedException {
+        String urlParameterslength = Integer.toString(urlParameters.length());
+        String statusMessage;
+
+        try {
+            ContentResponse httpResponse = sendRequestBuilder(config.endpointUrl, HttpMethod.POST)
+                    .content(new StringContentProvider(urlParameters), "text/xml; charset=utf-8")
+                    .header(HttpHeader.CONTENT_LENGTH, urlParameterslength).send();
+
+            int status = httpResponse.getStatus();
+            String xmlResponse = httpResponse.getContentAsString();
+
+            List<String> statusMessages = evaluateXPath("/Response/Parameters//Parameter[@name='StatusMessage']/text()",
+                    xmlResponse);
+            if (!(statusMessages.isEmpty())) {
+                statusMessage = statusMessages.get(0);
+            } else {
+                statusMessage = httpResponse.getReason();
+            }
+
+            if (status == 200) {
+                if (logger.isTraceEnabled()) {
+                    logger.trace("Hayward Connection thing:  {} Hayward http command: {}", getCallingMethod(),
+                            urlParameters);
+                    logger.trace("Hayward Connection thing:  {} Hayward http response: {} {}", getCallingMethod(),
+                            statusMessage, xmlResponse);
+                }
+                return xmlResponse;
+            } else {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Hayward Connection thing:  {} Hayward http command: {}", getCallingMethod(),
+                            urlParameters);
+                    logger.debug("Hayward Connection thing:  {} Hayward http response: {}", getCallingMethod(), status);
+                }
+                return "";
+            }
+        } catch (ExecutionException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    "Unable to resolve host.  Check Hayward hostname and your internet connection. " + e);
+            return "";
+        } catch (TimeoutException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    "Connection Timeout.  Check Hayward hostname and your internet connection. " + e);
+            return "";
+        }
+    }
+
+    private String getCallingMethod() {
+        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
+        StackTraceElement e = stacktrace[3];
+        return e.getMethodName();
+    }
+
+    public int convertCommand(Command command) {
+        if (command == OnOffType.ON) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardChlorinatorHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardChlorinatorHandler.java
new file mode 100644 (file)
index 0000000..0d1e12f
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Chlorinator Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardChlorinatorHandler extends HaywardThingHandler {
+    private final Logger logger = LoggerFactory.getLogger(HaywardChlorinatorHandler.class);
+    public String chlorTimedPercent = "";
+    public String chlorState = "";
+
+    public HaywardChlorinatorHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//Chlorinator/@systemId", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        // Operating Mode
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@operatingMode", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_OPERATINGMODE, data.get(i));
+
+                        // Timed Percent
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@Timed-Percent", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_TIMEDPERCENT, data.get(i));
+                        this.chlorTimedPercent = data.get(0);
+
+                        // scMode
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@scMode", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_SCMODE, data.get(i));
+
+                        // Error
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@chlrError", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_ERROR, data.get(i));
+
+                        // Alert
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@chlrAlert", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_ALERT, data.get(i));
+
+                        // Average Salt Level
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@avgSaltLevel", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_AVGSALTLEVEL, data.get(i));
+
+                        // Instant Salt Level
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@instantSaltLevel", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_INSTANTSALTLEVEL, data.get(i));
+
+                        // Status
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@status", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_STATUS, data.get(i));
+
+                        if (data.get(i).equals("0")) {
+                            updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_ENABLE, "0");
+                            // chlorState is used to set the chlorinator cfgState in the timedPercent command
+                            this.chlorState = "2";
+                        } else {
+                            updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_ENABLE, "1");
+                            // chlorState is used to set the chlorinator cfgState in the timedPercent command
+                            this.chlorState = "3";
+                        }
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if ((command instanceof RefreshType)) {
+            return;
+        }
+
+        String chlorCfgState = null;
+        String chlorTimedPercent = "0";
+
+        String systemID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
+        String poolID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_BOWID);
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                String cmdString = this.cmdToString(command);
+                try {
+                    switch (channelUID.getId()) {
+                        case HaywardBindingConstants.CHANNEL_CHLORINATOR_ENABLE:
+                            if (cmdString.equals("1")) {
+                                chlorCfgState = "3";
+                                chlorTimedPercent = this.chlorTimedPercent;
+                            } else {
+                                chlorCfgState = "2";
+                                chlorTimedPercent = this.chlorTimedPercent;
+                            }
+                            break;
+                        case HaywardBindingConstants.CHANNEL_CHLORINATOR_TIMEDPERCENT:
+                            chlorCfgState = this.chlorState;
+                            chlorTimedPercent = cmdString;
+                            break;
+                        default:
+                            logger.warn("haywardCommand Unsupported type {}", channelUID);
+                            return;
+                    }
+
+                    String cmdURL = HaywardBindingConstants.COMMAND_PARAMETERS
+                            + "<Name>SetCHLORParams</Name><Parameters>"
+                            + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                            + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                            + bridgehandler.account.mspSystemID + "</Parameter>"
+                            + "<Parameter name=\"PoolID\" dataType=\"int\">" + poolID + "</Parameter>"
+                            + "<Parameter name=\"ChlorID\" dataType=\"int\" alias=\"EquipmentID\">" + systemID
+                            + "</Parameter>" + "<Parameter name=\"CfgState\" dataType=\"byte\" alias=\"Data1\">"
+                            + chlorCfgState + "</Parameter>"
+                            + "<Parameter name=\"OpMode\" dataType=\"byte\" alias=\"Data2\">1</Parameter>"
+                            + "<Parameter name=\"BOWType\" dataType=\"byte\" alias=\"Data3\">1</Parameter>"
+                            + "<Parameter name=\"CellType\" dataType=\"byte\" alias=\"Data4\">4</Parameter>"
+                            + "<Parameter name=\"TimedPercent\" dataType=\"byte\" alias=\"Data5\">" + chlorTimedPercent
+                            + "</Parameter>"
+                            + "<Parameter name=\"SCTimeout\" dataType=\"byte\" unit=\"hour\" alias=\"Data6\">24</Parameter>"
+                            + "<Parameter name=\"ORPTimout\" dataType=\"byte\" unit=\"hour\" alias=\"Data7\">24</Parameter>"
+                            + "</Parameters></Request>";
+
+                    // *****Send Command to Hayward server
+                    String xmlResponse = bridgehandler.httpXmlResponse(cmdURL);
+                    String status = bridgehandler.evaluateXPath("//Parameter[@name='Status']/text()", xmlResponse)
+                            .get(0);
+
+                    if (!(status.equals("0"))) {
+                        logger.debug("haywardCommand XML response: {}", xmlResponse);
+                        return;
+                    }
+                } catch (HaywardException e) {
+                    logger.debug("Unable to send command to Hayward's server {}:{}:{}",
+                            bridgehandler.config.endpointUrl, bridgehandler.config.username, e.getMessage());
+                } catch (InterruptedException e) {
+                    return;
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardColorLogicHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardColorLogicHandler.java
new file mode 100644 (file)
index 0000000..bb42b06
--- /dev/null
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The ColorLogic Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardColorLogicHandler extends HaywardThingHandler {
+    private final Logger logger = LoggerFactory.getLogger(HaywardColorLogicHandler.class);
+
+    public HaywardColorLogicHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//ColorLogic-Light/@systemId", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        // Light State
+                        data = bridgehandler.evaluateXPath("//ColorLogic-Light/@lightState", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_COLORLOGIC_LIGHTSTATE, data.get(i));
+
+                        if (data.get(i).equals("0")) {
+                            updateData(HaywardBindingConstants.CHANNEL_COLORLOGIC_ENABLE, "0");
+                        } else {
+                            updateData(HaywardBindingConstants.CHANNEL_COLORLOGIC_ENABLE, "1");
+                        }
+
+                        // Current Show
+                        data = bridgehandler.evaluateXPath("//ColorLogic-Light/@currentShow", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_COLORLOGIC_CURRENTSHOW, data.get(0));
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if ((command instanceof RefreshType)) {
+            return;
+        }
+
+        String systemID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
+        String poolID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_BOWID);
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                String cmdString = this.cmdToString(command);
+                String cmdURL = null;
+                try {
+                    switch (channelUID.getId()) {
+                        case HaywardBindingConstants.CHANNEL_COLORLOGIC_ENABLE:
+                            if (command == OnOffType.ON) {
+                                cmdString = "1";
+                            } else {
+                                cmdString = "0";
+                            }
+                            cmdURL = HaywardBindingConstants.COMMAND_PARAMETERS
+                                    + "<Name>SetUIEquipmentCmd</Name><Parameters>"
+                                    + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                                    + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                                    + bridgehandler.account.mspSystemID + "</Parameter>"
+                                    + "<Parameter name=\"PoolID\" dataType=\"int\">" + poolID + "</Parameter>"
+                                    + "<Parameter name=\"EquipmentID\" dataType=\"int\">" + systemID + "</Parameter>"
+                                    + "<Parameter name=\"IsOn\" dataType=\"int\">" + cmdString + "</Parameter>"
+                                    + HaywardBindingConstants.COMMAND_SCHEDULE + "</Parameters></Request>";
+                            break;
+                        case HaywardBindingConstants.CHANNEL_COLORLOGIC_CURRENTSHOW:
+                            cmdURL = HaywardBindingConstants.COMMAND_PARAMETERS
+                                    + "<Name>SetStandAloneLightShow</Name><Parameters>"
+                                    + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                                    + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                                    + bridgehandler.account.mspSystemID + "</Parameter>"
+                                    + "<Parameter name=\"PoolID\" dataType=\"int\">" + poolID + "</Parameter>"
+                                    + "<Parameter name=\"LightID\" dataType=\"int\">" + systemID + "</Parameter>"
+                                    + "<Parameter name=\"Show\" dataType=\"int\">" + cmdString + "</Parameter>"
+                                    + "<Parameter name=\"Speed\" dataType=\"byte\">4</Parameter>"
+                                    + "<Parameter name=\"Brightness\" dataType=\"byte\">4</Parameter>"
+                                    + "<Parameter name=\"Reserved\" dataType=\"byte\">0</Parameter>"
+                                    + HaywardBindingConstants.COMMAND_SCHEDULE + "</Parameters></Request>";
+                            break;
+                        default:
+                            logger.warn("haywardCommand Unsupported type {}", channelUID);
+                            return;
+                    }
+
+                    // *****Send Command to Hayward server
+                    String xmlResponse = bridgehandler.httpXmlResponse(cmdURL);
+                    String status = bridgehandler.evaluateXPath("//Parameter[@name='Status']/text()", xmlResponse)
+                            .get(0);
+
+                    if (!(status.equals("0"))) {
+                        logger.debug("haywardCommand XML response: {}", xmlResponse);
+                        return;
+                    }
+                } catch (HaywardException e) {
+                    logger.debug("Unable to send command to Hayward's server {}:{}:{}",
+                            bridgehandler.config.endpointUrl, bridgehandler.config.username, e.getMessage());
+                } catch (InterruptedException e) {
+                    return;
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardFilterHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardFilterHandler.java
new file mode 100644 (file)
index 0000000..b39740c
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Filter Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardFilterHandler extends HaywardThingHandler {
+    private final Logger logger = LoggerFactory.getLogger(HaywardFilterHandler.class);
+
+    public HaywardFilterHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void initialize() {
+        updateStatus(ThingStatus.ONLINE);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//Filter/@systemId", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        // Operating Mode
+                        data = bridgehandler.evaluateXPath("//Chlorinator/@operatingMode", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_CHLORINATOR_OPERATINGMODE, data.get(i));
+
+                        // Valve Position
+                        data = bridgehandler.evaluateXPath("//Filter/@valvePosition", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_FILTER_VALVEPOSITION, data.get(i));
+
+                        // Speed
+                        data = bridgehandler.evaluateXPath("//Filter/@filterSpeed", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_FILTER_SPEED, data.get(i));
+
+                        if (data.get(i).equals("0")) {
+                            updateData(HaywardBindingConstants.CHANNEL_FILTER_ENABLE, "0");
+                        } else {
+                            updateData(HaywardBindingConstants.CHANNEL_FILTER_ENABLE, "1");
+                        }
+
+                        // State
+                        data = bridgehandler.evaluateXPath("//Filter/@filterState", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_FILTER_STATE, data.get(i));
+
+                        // lastSpeed
+                        data = bridgehandler.evaluateXPath("//Filter/@lastSpeed", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_FILTER_LASTSPEED, data.get(i));
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if ((command instanceof RefreshType)) {
+            return;
+        }
+
+        String systemID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
+        String poolID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_BOWID);
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                String cmdString = this.cmdToString(command);
+                try {
+                    switch (channelUID.getId()) {
+                        case HaywardBindingConstants.CHANNEL_FILTER_ENABLE:
+                            if (command == OnOffType.ON) {
+                                cmdString = "100";
+                            } else {
+                                cmdString = "0";
+                            }
+                            break;
+                        case HaywardBindingConstants.CHANNEL_FILTER_SPEED:
+                            break;
+                        default:
+                            logger.warn("haywardCommand Unsupported type {}", channelUID);
+                            return;
+                    }
+
+                    String cmdURL = HaywardBindingConstants.COMMAND_PARAMETERS
+                            + "<Name>SetUIEquipmentCmd</Name><Parameters>"
+                            + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                            + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                            + bridgehandler.account.mspSystemID + "</Parameter>"
+                            + "<Parameter name=\"PoolID\" dataType=\"int\">" + poolID + "</Parameter>"
+                            + "<Parameter name=\"EquipmentID\" dataType=\"int\">" + systemID + "</Parameter>"
+                            + "<Parameter name=\"IsOn\" dataType=\"int\">" + cmdString + "</Parameter>"
+                            + HaywardBindingConstants.COMMAND_SCHEDULE + "</Parameters></Request>";
+
+                    // *****Send Command to Hayward server
+                    String xmlResponse = bridgehandler.httpXmlResponse(cmdURL);
+                    String status = bridgehandler.evaluateXPath("//Parameter[@name='Status']/text()", xmlResponse)
+                            .get(0);
+
+                    if (!(status.equals("0"))) {
+                        logger.debug("haywardCommand XML response: {}", xmlResponse);
+                        return;
+                    }
+                } catch (HaywardException e) {
+                    logger.debug("Unable to send command to Hayward's server {}:{}:{}",
+                            bridgehandler.config.endpointUrl, bridgehandler.config.username, e.getMessage());
+                } catch (InterruptedException e) {
+                    return;
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardHeaterHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardHeaterHandler.java
new file mode 100644 (file)
index 0000000..bf469f0
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.binding.haywardomnilogic.internal.config.HaywardConfig;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+
+/**
+ * The Heater Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardHeaterHandler extends HaywardThingHandler {
+
+    HaywardConfig config = getConfig().as(HaywardConfig.class);
+
+    public HaywardHeaterHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//Heater/@systemId", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        // State
+                        data = bridgehandler.evaluateXPath("//Heater/@heaterState", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_HEATER_STATE, data.get(i));
+
+                        // Enable
+                        data = bridgehandler.evaluateXPath("//Heater/@enable", xmlResponse);
+                        if (data.get(i).equals("0")) {
+                            updateData(HaywardBindingConstants.CHANNEL_HEATER_ENABLE, "0");
+                        } else {
+                            updateData(HaywardBindingConstants.CHANNEL_HEATER_ENABLE, "1");
+                        }
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardRelayHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardRelayHandler.java
new file mode 100644 (file)
index 0000000..f890cd8
--- /dev/null
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Relay Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardRelayHandler extends HaywardThingHandler {
+    private final Logger logger = LoggerFactory.getLogger(HaywardRelayHandler.class);
+
+    public HaywardRelayHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//Relay/@systemId", xmlResponse);
+                data = bridgehandler.evaluateXPath("//Relay/@relayState", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        updateData(HaywardBindingConstants.CHANNEL_RELAY_STATE, data.get(i));
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if ((command instanceof RefreshType)) {
+            return;
+        }
+
+        String systemID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
+        String poolID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_BOWID);
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                String cmdString = this.cmdToString(command);
+                String cmdURL = null;
+                try {
+                    switch (channelUID.getId()) {
+                        case HaywardBindingConstants.CHANNEL_RELAY_STATE:
+                            cmdURL = HaywardBindingConstants.COMMAND_PARAMETERS
+                                    + "<Name>SetUIEquipmentCmd</Name><Parameters>"
+                                    + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                                    + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                                    + bridgehandler.account.mspSystemID + "</Parameter>"
+                                    + "<Parameter name=\"PoolID\" dataType=\"int\">" + poolID + "</Parameter>"
+                                    + "<Parameter name=\"EquipmentID\" dataType=\"int\">" + systemID + "</Parameter>"
+                                    + "<Parameter name=\"IsOn\" dataType=\"int\">" + cmdString + "</Parameter>"
+                                    + HaywardBindingConstants.COMMAND_SCHEDULE + "</Parameters></Request>";
+                            break;
+                        default:
+                            logger.warn("haywardCommand Unsupported type {}", channelUID);
+                            return;
+                    }
+
+                    // *****Send Command to Hayward server
+                    String xmlResponse = bridgehandler.httpXmlResponse(cmdURL);
+                    String status = bridgehandler.evaluateXPath("//Parameter[@name='Status']/text()", xmlResponse)
+                            .get(0);
+
+                    if (!(status.equals("0"))) {
+                        logger.debug("haywardCommand XML response: {}", xmlResponse);
+                        return;
+                    }
+                } catch (HaywardException e) {
+                    logger.debug("Unable to send command to Hayward's server {}:{}:{}",
+                            bridgehandler.config.endpointUrl, bridgehandler.config.username, e.getMessage());
+                } catch (InterruptedException e) {
+                    return;
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardSensorHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardSensorHandler.java
new file mode 100644 (file)
index 0000000..a811e69
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+
+/**
+ * The Sensor Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardSensorHandler extends HaywardThingHandler {
+
+    public HaywardSensorHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//Sensor/@systemId", xmlResponse);
+                data = bridgehandler.evaluateXPath("//Sensor/@relayState", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        updateData(HaywardBindingConstants.CHANNEL_RELAY_STATE, data.get(i));
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardVirtualHeaterHandler.java b/bundles/org.openhab.binding.haywardomnilogic/src/main/java/org/openhab/binding/haywardomnilogic/internal/handler/HaywardVirtualHeaterHandler.java
new file mode 100644 (file)
index 0000000..6ee8246
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.haywardomnilogic.internal.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
+import org.openhab.binding.haywardomnilogic.internal.HaywardException;
+import org.openhab.binding.haywardomnilogic.internal.HaywardThingHandler;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Virtual Heater Handler
+ *
+ * @author Matt Myers - Initial contribution
+ */
+@NonNullByDefault
+public class HaywardVirtualHeaterHandler extends HaywardThingHandler {
+    private final Logger logger = LoggerFactory.getLogger(HaywardVirtualHeaterHandler.class);
+
+    public HaywardVirtualHeaterHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    public void getTelemetry(String xmlResponse) throws HaywardException {
+        List<String> systemIDs = new ArrayList<>();
+        List<String> data = new ArrayList<>();
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                systemIDs = bridgehandler.evaluateXPath("//VirtualHeater/@systemId", xmlResponse);
+                String thingSystemID = getThing().getUID().getId();
+                for (int i = 0; i < systemIDs.size(); i++) {
+                    if (systemIDs.get(i).equals(thingSystemID)) {
+                        data = bridgehandler.evaluateXPath("//VirtualHeater/@Current-Set-Point", xmlResponse);
+                        updateData(HaywardBindingConstants.CHANNEL_VIRTUALHEATER_CURRENTSETPOINT, data.get(i));
+
+                        data = bridgehandler.evaluateXPath("//VirtualHeater/@enable", xmlResponse);
+                        if (data.get(i).equals("yes")) {
+                            updateData(HaywardBindingConstants.CHANNEL_VIRTUALHEATER_ENABLE, "1");
+                        } else if (data.get(i).equals("no")) {
+                            updateData(HaywardBindingConstants.CHANNEL_VIRTUALHEATER_ENABLE, "0");
+                        }
+                    }
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        if ((command instanceof RefreshType)) {
+            return;
+        }
+
+        String systemID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
+        String poolID = getThing().getProperties().get(HaywardBindingConstants.PROPERTY_BOWID);
+
+        Bridge bridge = getBridge();
+        if (bridge != null) {
+            HaywardBridgeHandler bridgehandler = (HaywardBridgeHandler) bridge.getHandler();
+            if (bridgehandler != null) {
+                String cmdString = this.cmdToString(command);
+                String cmdURL = null;
+
+                if (command == OnOffType.ON) {
+                    cmdString = "True";
+                } else if (command == OnOffType.OFF) {
+                    cmdString = "False";
+                }
+
+                try {
+                    switch (channelUID.getId()) {
+                        case HaywardBindingConstants.CHANNEL_VIRTUALHEATER_ENABLE:
+                            cmdURL = HaywardBindingConstants.COMMAND_PARAMETERS
+                                    + "<Name>SetHeaterEnable</Name><Parameters>"
+                                    + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                                    + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                                    + bridgehandler.account.mspSystemID + "</Parameter>"
+                                    + "<Parameter name=\"PoolID\" dataType=\"int\">" + poolID + "</Parameter>"
+                                    + "<Parameter name=\"HeaterID\" dataType=\"int\">" + systemID + "</Parameter>"
+                                    + "<Parameter name=\"Enabled\" dataType=\"bool\">" + cmdString + "</Parameter>"
+                                    + "</Parameters></Request>";
+                            break;
+
+                        case HaywardBindingConstants.CHANNEL_VIRTUALHEATER_CURRENTSETPOINT:
+                            cmdURL = HaywardBindingConstants.COMMAND_PARAMETERS
+                                    + "<Name>SetUIHeaterCmd</Name><Parameters>"
+                                    + "<Parameter name=\"Token\" dataType=\"String\">" + bridgehandler.account.token
+                                    + "</Parameter>" + "<Parameter name=\"MspSystemID\" dataType=\"int\">"
+                                    + bridgehandler.account.mspSystemID + "</Parameter>"
+                                    + "<Parameter name=\"PoolID\" dataType=\"int\">" + poolID + "</Parameter>"
+                                    + "<Parameter name=\"HeaterID\" dataType=\"int\">" + systemID + "</Parameter>"
+                                    + "<Parameter name=\"Temp\" dataType=\"int\">" + cmdString + "</Parameter>"
+                                    + "</Parameters></Request>";
+                            break;
+                        default:
+                            logger.warn("haywardCommand Unsupported type {}", channelUID);
+                            return;
+                    }
+
+                    // *****Send Command to Hayward server
+                    String xmlResponse = bridgehandler.httpXmlResponse(cmdURL);
+                    String status = bridgehandler.evaluateXPath("//Parameter[@name='Status']/text()", xmlResponse)
+                            .get(0);
+
+                    if (!(status.equals("0"))) {
+                        logger.debug("haywardCommand XML response: {}", xmlResponse);
+                        return;
+                    }
+                } catch (HaywardException e) {
+                    logger.debug("Unable to send command to Hayward's server {}:{}:{}",
+                            bridgehandler.config.endpointUrl, bridgehandler.config.username, e.getMessage());
+                } catch (InterruptedException e) {
+                    return;
+                }
+                this.updateStatus(ThingStatus.ONLINE);
+            } else {
+                this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644 (file)
index 0000000..2c6eab0
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<binding:binding id="haywardomnilogic" 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>Hayward OmniLogix Binding</name>
+       <description>Binding for the Hayward OmniLogix swimming pool automation controller.</description>
+       <author>Matt Myers</author>
+</binding:binding>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/backyard.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/backyard.xml
new file mode 100644 (file)
index 0000000..c947bc3
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="backyard" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Backyard</label>
+               <description>The Hayward Backyard</description>
+               <channels>
+                       <channel id="backyardAirTemp" typeId="airTemp"/>
+                       <channel id="backyardStatus" typeId="backyardstatus"/>
+                       <channel id="backyardState" typeId="backyardstate"/>
+                       <channel id="backyardAlarm1" typeId="alarm"/>
+                       <channel id="backyardAlarm2" typeId="alarm"/>
+                       <channel id="backyardAlarm3" typeId="alarm"/>
+                       <channel id="backyardAlarm4" typeId="alarm"/>
+                       <channel id="backyardAlarm5" typeId="alarm"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+       <channel-type id="airTemp">
+               <item-type>Number:Temperature</item-type>
+               <label>Air Temp</label>
+               <description>Air Temp</description>
+               <state pattern="%1f %unit%" readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="backyardstatus">
+               <item-type>String</item-type>
+               <label>Status</label>
+               <description>Status</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="1">Normal</option>
+                               <option value="2">Alarm</option>
+                               <option value="3">Expired</option>
+                               <option value="4">Lost Link</option>
+                               <option value="5">Service Mode</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="backyardstate">
+               <item-type>String</item-type>
+               <label>State</label>
+               <description>State</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="0">Powered Off</option>
+                               <option value="1">Normal</option>
+                               <option value="2">Service Mode</option>
+                               <option value="3">Config Mode</option>
+                               <option value="4">Timed Service Mode</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="alarm">
+               <item-type>String</item-type>
+               <label>Alarm</label>
+               <description>Alarm</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/bow.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/bow.xml
new file mode 100644 (file)
index 0000000..aa59474
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="bow" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Body of Water</label>
+               <description>The Hayward Body of Water</description>
+               <channels>
+                       <channel id="bowFlow" typeId="waterFlow"/>
+                       <channel id="bowWaterTemp" typeId="waterTemp"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+       <channel-type id="waterFlow">
+               <item-type>system.power</item-type>
+               <label>Flow Sensor</label>
+               <description>Flow Sensor</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="waterTemp">
+               <item-type>Number:Temperature</item-type>
+               <label>Water Temp</label>
+               <description>Water Temp</description>
+               <state pattern="%1f %unit%" readOnly="true"/>
+       </channel-type>
+
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/bridge.xml
new file mode 100644 (file)
index 0000000..1b5c8f9
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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">
+
+       <!-- The bridge to communicate with Hayward's server -->
+       <bridge-type id="bridge">
+               <label>Hayward OmniLogix Connection</label>
+               <description>Connection to Hayward's Server</description>
+
+               <config-description>
+                       <parameter name="endpointUrl" type="text" required="true">
+                               <context>url</context>
+                               <label>Endpoint URL</label>
+                               <default>https://app1.haywardomnilogic.com/HAAPI/HomeAutomation/API.ashx</default>
+                               <description>The URL of the Hayward API Server</description>
+                       </parameter>
+                       <parameter name="username" type="text" required="true">
+                               <label>User Name</label>
+                               <description>The username to connect to the server.</description>
+                       </parameter>
+                       <parameter name="password" type="text" required="true">
+                               <context>password</context>
+                               <label>Password</label>
+                               <description>The password to connect to the server.</description>
+                       </parameter>
+                       <parameter name="telemetryPollTime" type="integer" min="10" max="60" unit="s" required="true">
+                               <label>Telemetry Poll Delay</label>
+                               <default>12</default>
+                               <description>How often to request telemetry data from Hayward Server</description>
+                       </parameter>
+                       <parameter name="alarmPollTime" type="integer" min="0" max="120" unit="s" required="true">
+                               <label>Alarm Poll Delay</label>
+                               <default>60</default>
+                               <description>How often to request alarm data from Hayward Server. Enter 0 to disable.</description>
+                       </parameter>
+               </config-description>
+       </bridge-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/chlorinator.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/chlorinator.xml
new file mode 100644 (file)
index 0000000..4b77e92
--- /dev/null
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="chlorinator" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Chlorinator</label>
+               <description>Chlorinator</description>
+               <channels>
+                       <channel id="chlorEnable" typeId="system.power"/>
+                       <channel id="chlorOperatingMode" typeId="chlorOperatingMode"/>
+                       <channel id="chlorTimedPercent" typeId="timedPercent"/>
+                       <channel id="chlorScMode" typeId="scMode"/>
+                       <channel id="chlorError" typeId="chlorError"/>
+                       <channel id="chlorAlert" typeId="chlorAlert"/>
+                       <channel id="chlorAvgSaltLevel" typeId="avgSaltLevel"/>
+                       <channel id="chlorInstantSaltLevel" typeId="instantSaltLevel"/>
+                       <channel id="chlorStatus" typeId="status"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+       <channel-type id="chlorOperatingMode">
+               <item-type>String</item-type>
+               <label>Operating Mode</label>
+               <description>Operating Mode</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="0">Off</option>
+                               <option value="1">Timed Percent</option>
+                               <option value="2">ORP Autosense</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="timedPercent">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Salt Output (%)</label>
+               <description>Current salt output setting for the chlorinator (%).</description>
+               <state min="0" max="100" step="1.0" pattern="%1f %unit%" readOnly="false"/>
+       </channel-type>
+
+       <channel-type id="scMode">
+               <item-type>String</item-type>
+               <label>scMode</label>
+               <description>scMode</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="0">Off</option>
+                               <option value="1">Super Chlorinating</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="chlorError">
+               <item-type>Number</item-type>
+               <label>Chlorinator Error</label>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="chlorAlert">
+               <item-type>String</item-type>
+               <label>Chlorinator Alert</label>
+               <description>Chlorinator Alert</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="0">None</option>
+                               <option value="16">Low T-Cell Temperature</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="avgSaltLevel">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Average Salt Level</label>
+               <description>Average Salt Level</description>
+               <state pattern="%1f %unit%" readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="instantSaltLevel">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Instant Salt Level</label>
+               <description>Instant Salt Level</description>
+               <state pattern="%1f %unit%" readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="status">
+               <item-type>Number</item-type>
+               <label>Status</label>
+               <description>Status</description>
+               <state pattern="%1f" readOnly="true"/>
+       </channel-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/colorlogic.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/colorlogic.xml
new file mode 100644 (file)
index 0000000..25d882e
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="colorlogic" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Color Logic Light</label>
+               <description>Color Logic Light</description>
+               <channels>
+                       <channel id="colorLogicLightEnable" typeId="system.power"/>
+                       <channel id="colorLogicLightState" typeId="lightState"/>
+                       <channel id="colorLogicLightCurrentShow" typeId="currentShow"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+       <channel-type id="lightState">
+               <item-type>String</item-type>
+               <label>Light State</label>
+               <description>Light State</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="0">Off</option>
+                               <option value="1">On</option>
+                               <option value="4">15 Sec White Light</option>
+                               <option value="7">Powering Off</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="currentShow">
+               <item-type>String</item-type>
+               <label>Current Show</label>
+               <description>Current Show</description>
+               <state readOnly="false">
+                       <options>
+                               <option value="0">Voodoo Lounge</option>
+                               <option value="1">Deep Blue Sea</option>
+                               <option value="2">Royal Blue</option>
+                               <option value="3">Afternoon Sky</option>
+                               <option value="4">Aqua Green</option>
+                               <option value="5">Emerald</option>
+                               <option value="6">Cloud White</option>
+                               <option value="7">Warm Red</option>
+                               <option value="8">Flamingo</option>
+                               <option value="9">Vivid Violet</option>
+                               <option value="10">Sangria</option>
+                               <option value="11">Twilight</option>
+                               <option value="12">Tranquility</option>
+                               <option value="13">Gemstone</option>
+                               <option value="14">USA</option>
+                               <option value="15">Mardi Gras</option>
+                               <option value="16">Cool Cabaret</option>
+                       </options>
+               </state>
+       </channel-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/filter.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/filter.xml
new file mode 100644 (file)
index 0000000..0052918
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="filter" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Filter</label>
+               <description>Filter Equipment</description>
+               <channels>
+                       <channel id="filterEnable" typeId="system.power"/>
+                       <channel id="filterValvePosition" typeId="valvePosition"/>
+                       <channel id="filterSpeed" typeId="filterSpeed"/>
+                       <channel id="filterState" typeId="filterState"/>
+                       <channel id="filterLastSpeed" typeId="filterLastSpeed"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+                       <property name="Min Pump Percent"></property>
+                       <property name="Max Pump Percent"></property>
+                       <property name="Min Pump RPM"></property>
+                       <property name="Max Pump RPM"></property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+       <channel-type id="valvePosition">
+               <item-type>String</item-type>
+               <label>Valve Position</label>
+               <description>Valve Position</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="0">Off</option>
+                               <option value="1">Pool Only</option>
+                               <option value="2">Spa Only</option>
+                               <option value="3">Spill Over</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="filterSpeed">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Filter Speed</label>
+               <description>Filter Speed in %</description>
+               <state min="0" max="100" step="1.0" pattern="%1f %unit%" readOnly="false"/>
+       </channel-type>
+
+       <channel-type id="filterState">
+               <item-type>String</item-type>
+               <label>Filter State</label>
+               <description>Filter State</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="0">Off</option>
+                               <option value="1">Running</option>
+                               <option value="2">Priming</option>
+                               <option value="3">Waiting to Turn Off</option>
+                               <option value="4">Waiting to Turn Off Manual</option>
+                               <option value="5">Heater Extend</option>
+                               <option value="6">Heater Cool Down</option>
+                               <option value="7">Suspended</option>
+                               <option value="8">CSAD Extend</option>
+                               <option value="9">Filter Superchlorinate</option>
+                               <option value="10">Filter Force Priming</option>
+                               <option value="11">Filter Waiting for Pump to Turn Off</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="filterLastSpeed">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Last Speed</label>
+               <description>Last Speed</description>
+               <state pattern="%1f %unit%" readOnly="true"/>
+       </channel-type>
+
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/heater.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/heater.xml
new file mode 100644 (file)
index 0000000..960d9a9
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="heater" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Heater</label>
+               <description>Heater</description>
+               <channels>
+                       <channel id="heaterState" typeId="state"/>
+                       <channel id="heaterEnable" typeId="enable"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+       </thing-type>
+
+       <channel-type id="state">
+               <item-type>Number</item-type>
+               <label>Heater State</label>
+               <description>Heater State</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="enable">
+               <item-type>system.power</item-type>
+               <label>Heater Enable</label>
+               <description>Heater Enable</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/pump.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/pump.xml
new file mode 100644 (file)
index 0000000..16f1a09
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="pump" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Pump</label>
+               <description>Pump</description>
+               <channels>
+                       <channel id="pumpEnable" typeId="system.power"/>
+                       <channel id="pumpSpeed" typeId="pumpSpeed"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+                       <property name="Min Pump Percent"></property>
+                       <property name="Max Pump Percent"></property>
+                       <property name="Min Pump RPM"></property>
+                       <property name="Max Pump RPM"></property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+       <channel-type id="pumpSpeed">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Pump Speed in %</label>
+               <description>Pump Speed</description>
+               <state min="0" max="100" step="1.0" pattern="%1f %unit%" readOnly="false"/>
+       </channel-type>
+
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/relay.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/relay.xml
new file mode 100644 (file)
index 0000000..5c2b70e
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="relay" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Relay</label>
+               <description>Relay</description>
+               <channels>
+                       <channel id="relayState" typeId="system.power"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/sensor.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/sensor.xml
new file mode 100644 (file)
index 0000000..d33fc56
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="sensor" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Sensor</label>
+               <description>Sensor</description>
+               <channels>
+                       <channel id="sensorData" typeId="data"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+
+       </thing-type>
+
+       <channel-type id="bow">
+               <item-type>Number</item-type>
+               <label>Body of Water</label>
+               <description>The Body of Water ID</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="data">
+               <item-type>Number</item-type>
+               <label>Data</label>
+               <description>Sensor Data</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/virtualHeater.xml b/bundles/org.openhab.binding.haywardomnilogic/src/main/resources/OH-INF/thing/virtualHeater.xml
new file mode 100644 (file)
index 0000000..89d7d41
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="haywardomnilogic"
+       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="virtualHeater" listed="false">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"/>
+               </supported-bridge-type-refs>
+
+               <label>Virtual Heater</label>
+               <description>Virtual Heater</description>
+
+               <channels>
+                       <channel id="virtualHeaterEnable" typeId="system.power"/>
+                       <channel id="virtualHeaterCurrentSetpoint" typeId="currentSetpoint"/>
+               </channels>
+
+               <properties>
+                       <property name="Vendor">Hayward</property>
+               </properties>
+               <representation-property>systemID</representation-property>
+       </thing-type>
+
+       <channel-type id="currentSetpoint">
+               <item-type>Number:Temperature</item-type>
+               <label>Current Setpoint</label>
+               <description>Current Setpoint</description>
+               <category>Temperature</category>
+               <state min="65" max="90" step="1.0" pattern="%1f %unit%" readOnly="false"/>
+       </channel-type>
+
+</thing:thing-descriptions>
index 087005fe3c82d4792cdae6665ebc7a8300264863..13f4e56e17d3f543eee127f5fbc60d8965c8972e 100644 (file)
     <module>org.openhab.binding.gree</module>
     <module>org.openhab.binding.groheondus</module>
     <module>org.openhab.binding.harmonyhub</module>
+    <module>org.openhab.binding.haywardomnilogic</module>
     <module>org.openhab.binding.hdanywhere</module>
     <module>org.openhab.binding.hdpowerview</module>
     <module>org.openhab.binding.helios</module>