From 7d9e277a1507a511a43b83c0b17d405c5152d553 Mon Sep 17 00:00:00 2001
From: Ondrej Pecta <opecta@gmail.com>
Date: Thu, 8 Sep 2022 21:18:42 +0200
Subject: [PATCH] [jablotron] Added thermometers support for JA100F alarms
 (#13361)

Signed-off-by: Ondrej Pecta <opecta@gmail.com>
---
 .../org.openhab.binding.jablotron/README.md   |  4 +-
 .../handler/JablotronBridgeHandler.java       | 26 +++++++++--
 .../handler/JablotronJa100FHandler.java       | 42 ++++++++++++++++++
 .../ja100f/JablotronGetThermoDevicesData.java | 43 +++++++++++++++++++
 .../JablotronGetThermoDevicesResponse.java    | 31 +++++++++++++
 .../internal/model/ja100f/JablotronState.java | 12 ++++++
 .../model/ja100f/JablotronThermoDevice.java   | 40 +++++++++++++++++
 7 files changed, 192 insertions(+), 6 deletions(-)
 create mode 100644 bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesData.java
 create mode 100644 bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesResponse.java
 create mode 100644 bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronThermoDevice.java

diff --git a/bundles/org.openhab.binding.jablotron/README.md b/bundles/org.openhab.binding.jablotron/README.md
index 977e1de4c..26f699a59 100644
--- a/bundles/org.openhab.binding.jablotron/README.md
+++ b/bundles/org.openhab.binding.jablotron/README.md
@@ -56,11 +56,11 @@ Binding itself doesn't require specific configuration.
 | JA-100               | thermostat_%nr%  | Number:Temperature | the thermostat %nr% value                                 |
 | JA-100F              | sec-%nr%         | String             | the section %nr% status/control                           |
 | JA-100F              | pg-%nr%          | Switch             | the PG switch %nr% status/control                         |
+| JA-100F              | thm-%nr%         | Number:Temperature | the thermometer %nr% value                                |
 
 The state, pgm, thermometer, thermostat, sec and pg channels for the JA-100/JA-100F alarms are dynamically created according to your configuration.
 
-* The sections are represented by String channels (with possible values "set", "unset", "partialSet" for JA-100 and 
-possible values "ARM", "PARTIAL_ARM" and "DISARM" for JA100-F)
+* The sections are represented by String channels (with possible values "set", "unset", "partialSet" for JA-100 and possible values "ARM", "PARTIAL_ARM" and "DISARM" for JA100-F)
 
 ## Full Example
 
diff --git a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronBridgeHandler.java b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronBridgeHandler.java
index 9facea506..07c524aee 100644
--- a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronBridgeHandler.java
+++ b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronBridgeHandler.java
@@ -41,6 +41,7 @@ import org.openhab.binding.jablotron.internal.model.JablotronHistoryDataEvent;
 import org.openhab.binding.jablotron.internal.model.JablotronLoginResponse;
 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetPGResponse;
 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetSectionsResponse;
+import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetThermoDevicesResponse;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
@@ -334,12 +335,30 @@ public class JablotronBridgeHandler extends BaseBridgeHandler {
             return null;
         }
 
-        String urlParameters = "{\"connect-device\":false,\"list-type\":\"FULL\",\"service-id\":"
-                + handler.thingConfig.getServiceId() + ",\"service-states\":true}";
+        String urlParameters = getCommonUrlParameters(handler.thingConfig.getServiceId());
 
         return sendJsonMessage(url, urlParameters, JablotronGetPGResponse.class);
     }
 
+    private String getCommonUrlParameters(String serviceId) {
+        return "{\"connect-device\":false,\"list-type\":\"FULL\",\"service-id\":" + serviceId
+                + ",\"service-states\":true}";
+    }
+
+    protected @Nullable JablotronGetThermoDevicesResponse sendGetThermometers(Thing th, String alarm) {
+        String url = JABLOTRON_API_URL + alarm + "/thermoDevicesGet.json";
+        JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
+
+        if (handler == null) {
+            logger.debug("Thing handler is null");
+            return null;
+        }
+
+        String urlParameters = getCommonUrlParameters(handler.thingConfig.getServiceId());
+
+        return sendJsonMessage(url, urlParameters, JablotronGetThermoDevicesResponse.class);
+    }
+
     protected @Nullable JablotronGetSectionsResponse sendGetSections(Thing th, String alarm) {
         String url = JABLOTRON_API_URL + alarm + "/sectionsGet.json";
         JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
@@ -349,8 +368,7 @@ public class JablotronBridgeHandler extends BaseBridgeHandler {
             return null;
         }
 
-        String urlParameters = "{\"connect-device\":false,\"list-type\":\"FULL\",\"service-id\":"
-                + handler.thingConfig.getServiceId() + ",\"service-states\":true}";
+        String urlParameters = getCommonUrlParameters(handler.thingConfig.getServiceId());
 
         return sendJsonMessage(url, urlParameters, JablotronGetSectionsResponse.class);
     }
diff --git a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronJa100FHandler.java b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronJa100FHandler.java
index 5b520e646..f16b138cb 100644
--- a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronJa100FHandler.java
+++ b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/handler/JablotronJa100FHandler.java
@@ -22,11 +22,15 @@ import org.openhab.binding.jablotron.internal.model.JablotronHistoryDataEvent;
 import org.openhab.binding.jablotron.internal.model.JablotronServiceDetailSegment;
 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetPGResponse;
 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetSectionsResponse;
+import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetThermoDevicesResponse;
 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronSection;
 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronState;
+import org.openhab.binding.jablotron.internal.model.ja100f.JablotronThermoDevice;
 import org.openhab.core.cache.ExpiringCache;
 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.SIUnits;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
@@ -141,6 +145,15 @@ public class JablotronJa100FHandler extends JablotronAlarmHandler {
         updateThing(thingBuilder.build());
     }
 
+    private void createThermoChannel(String name, String label) {
+        ChannelTypeUID alarmStatus = new ChannelTypeUID(BINDING_ID, "temperature");
+        ThingBuilder thingBuilder = editThing();
+        Channel channel = ChannelBuilder.create(new ChannelUID(thing.getUID(), name), "Number").withLabel(label)
+                .withType(alarmStatus).build();
+        thingBuilder.withChannel(channel);
+        updateThing(thingBuilder.build());
+    }
+
     private @Nullable JablotronGetSectionsResponse sendGetSections() {
         JablotronBridgeHandler handler = getBridgeHandler();
         if (handler != null) {
@@ -177,6 +190,13 @@ public class JablotronJa100FHandler extends JablotronAlarmHandler {
                 updateSectionState(resp.getData().getStates());
             }
 
+            // thermo devices
+            JablotronGetThermoDevicesResponse respThermo = handler.sendGetThermometers(getThing(), alarmName);
+            if (respThermo != null) {
+                createThermoDeviceChannels(respThermo.getData().getThermoDevices());
+                updateThermoState(respThermo.getData().getStates());
+            }
+
             // update events
             List<JablotronHistoryDataEvent> events = sendGetEventHistory();
             if (events != null && !events.isEmpty()) {
@@ -213,6 +233,18 @@ public class JablotronJa100FHandler extends JablotronAlarmHandler {
         }
     }
 
+    private void createThermoDeviceChannels(List<JablotronThermoDevice> thermoDevices) {
+        for (JablotronThermoDevice device : thermoDevices) {
+            String id = device.getObjectDeviceId().toLowerCase();
+            logger.trace("object device id: {} with name: {}", id, device.getName());
+            Channel channel = getThing().getChannel(id);
+            if (channel == null) {
+                logger.debug("Creating a new channel: {}", id);
+                createThermoChannel(id, device.getName());
+            }
+        }
+    }
+
     private void updateSectionState(String section, List<JablotronState> states) {
         for (JablotronState state : states) {
             String id = state.getCloudComponentId();
@@ -230,6 +262,14 @@ public class JablotronJa100FHandler extends JablotronAlarmHandler {
         }
     }
 
+    private void updateThermoState(List<JablotronState> states) {
+        for (JablotronState state : states) {
+            logger.debug("updating thermo state: {}", state.getObjectDeviceId());
+            String id = state.getObjectDeviceId();
+            updateSection(id, state);
+        }
+    }
+
     private void updateSection(String id, JablotronState state) {
         logger.debug("component id: {} with state: {}", id, state.getState());
         State newState;
@@ -238,6 +278,8 @@ public class JablotronJa100FHandler extends JablotronAlarmHandler {
             newState = new StringType(state.getState());
         } else if (id.startsWith("PG-")) {
             newState = "ON".equals(state.getState()) ? OnOffType.ON : OnOffType.OFF;
+        } else if (id.startsWith("THM-")) {
+            newState = new QuantityType<>(state.getTemperature(), SIUnits.CELSIUS);
         } else {
             logger.debug("unknown component id: {}", id);
             return;
diff --git a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesData.java b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesData.java
new file mode 100644
index 000000000..18c436d23
--- /dev/null
+++ b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesData.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2022 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.jablotron.internal.model.ja100f;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link JablotronGetThermoDevicesData} class defines the data object for the
+ * getThermoDevices response
+ *
+ * @author Ondrej Pecta - Initial contribution
+ */
+@NonNullByDefault
+public class JablotronGetThermoDevicesData {
+
+    List<JablotronState> states = new ArrayList<>();
+
+    @SerializedName(value = "thermo-devices")
+    List<JablotronThermoDevice> thermoDevices = new ArrayList<>();
+
+    public List<JablotronState> getStates() {
+        return states;
+    }
+
+    public List<JablotronThermoDevice> getThermoDevices() {
+        return thermoDevices;
+    }
+}
diff --git a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesResponse.java b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesResponse.java
new file mode 100644
index 000000000..ff08d0cdb
--- /dev/null
+++ b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronGetThermoDevicesResponse.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2022 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.jablotron.internal.model.ja100f;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link JablotronGetThermoDevicesResponse} class defines the response object for the
+ * getThermometers call
+ *
+ * @author Ondrej Pecta - Initial contribution
+ */
+@NonNullByDefault
+public class JablotronGetThermoDevicesResponse {
+
+    JablotronGetThermoDevicesData data = new JablotronGetThermoDevicesData();
+
+    public JablotronGetThermoDevicesData getData() {
+        return data;
+    }
+}
diff --git a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronState.java b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronState.java
index 6972d6426..f9f76a62d 100644
--- a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronState.java
+++ b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronState.java
@@ -28,7 +28,11 @@ public class JablotronState {
     @SerializedName(value = "cloud-component-id", alternate = "component-id")
     String cloudComponentId = "";
 
+    @SerializedName(value = "object-device-id")
+    String objectDeviceId = "";
+
     String state = "";
+    float temperature = 0;
 
     public String getCloudComponentId() {
         return cloudComponentId;
@@ -37,4 +41,12 @@ public class JablotronState {
     public String getState() {
         return state;
     }
+
+    public float getTemperature() {
+        return temperature;
+    }
+
+    public String getObjectDeviceId() {
+        return objectDeviceId;
+    }
 }
diff --git a/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronThermoDevice.java b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronThermoDevice.java
new file mode 100644
index 000000000..17af16229
--- /dev/null
+++ b/bundles/org.openhab.binding.jablotron/src/main/java/org/openhab/binding/jablotron/internal/model/ja100f/JablotronThermoDevice.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2022 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.jablotron.internal.model.ja100f;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link JablotronThermoDevice} class defines the thermal device object
+ * for the getThermoDevices response
+ *
+ * @author Ondrej Pecta - Initial contribution
+ */
+@NonNullByDefault
+public class JablotronThermoDevice {
+
+    @SerializedName(value = "object-device-id")
+    String objectDeviceId = "";
+
+    String name = "";
+
+    public String getObjectDeviceId() {
+        return objectDeviceId;
+    }
+
+    public String getName() {
+        return name;
+    }
+}