]> git.basschouten.com Git - openhab-addons.git/commitdiff
[opensprinkler] Fix Program names and add new features for firmware 2.2.0 (#15410)
authorMatthew Skinner <matt@pcmus.com>
Sun, 10 Dec 2023 09:25:57 +0000 (20:25 +1100)
committerGitHub <noreply@github.com>
Sun, 10 Dec 2023 09:25:57 +0000 (10:25 +0100)
* Fix Program names are not parsed correctly in firmware 2.2.0

---------

Signed-off-by: Matthew Skinner <matt@pcmus.com>
13 files changed:
bundles/org.openhab.binding.opensprinkler/README.md
bundles/org.openhab.binding.opensprinkler/pom.xml
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/OpenSprinklerBindingConstants.java
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/OpenSprinklerState.java
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/api/OpenSprinklerApi.java
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/api/OpenSprinklerApiFactory.java
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/api/OpenSprinklerHttpApiV100.java
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/api/OpenSprinklerHttpApiV219.java
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/api/OpenSprinklerHttpApiV220.java [new file with mode: 0644]
bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/handler/OpenSprinklerDeviceHandler.java
bundles/org.openhab.binding.opensprinkler/src/main/resources/OH-INF/i18n/opensprinkler.properties
bundles/org.openhab.binding.opensprinkler/src/main/resources/OH-INF/thing/thing-types.xml
bundles/org.openhab.binding.opensprinkler/src/main/resources/OH-INF/update/instructions.xml [new file with mode: 0644]

index d8502898916ed155fe2e19ba5e40b032721ae219..03ca4c97af2442b5f412cd08d2b7ed417ca7ce99 100644 (file)
@@ -61,22 +61,25 @@ NOTE: Some channels will only show up if the hardware has the required sensor an
 
 | Channel Type ID | Item Type              |    | Description                                                                        |
 |-----------------|------------------------|----|------------------------------------------------------------------------------------|
-| rainsensor      | Switch                 | RO | This channel indicates whether rain is detected by the device or not.              |
-| sensor2         | Switch                 | RO | This channel is for the second sensor (if your hardware supports it).              |
+| cloudConnected  | Switch                 | RO | If the device is fully connected to the OpenSprinkler cloud this will show as 'ON'.|
 | currentDraw     | Number:ElectricCurrent | RO | Shows the current draw of the device.                                              |
-| waterlevel      | Number:Dimensionless   | RO | This channel shows the current water level in percent (0-250%). The water level is |
-|                 |                        |    | calculated based on the weather and influences the duration of the water programs. |
-| signalStrength  | Number                 | RO | Shows how strong the WiFi Signal is.     |
+| enablePrograms  | Switch                 | RW | Allow programs to auto run. When OFF, manually started stations will still work.   |
 | flowSensorCount | Number:Dimensionless   | RO | Shows the number of pulses the optional water flow sensor has reported.            |
+| nextDuration    | Number:Time            | RW | The time the station will open for when any stations are selected from the         |
+|                 |                        |    | `stations` channel. Defaults to 30 minutes if not set.                           |
+| pausePrograms   | Number:Time            | RW | Sets/Shows the amount of time that programs will be paused for.                    |
 | programs        | String                 | RW | Displays a list of the programs that are setup in your OpenSprinkler and when      |
 |                 |                        |    | selected will start that program for you.                                          |
+| rainDelay       | Number:Time            | RW | Sets/Shows the amount of time (hours) that rain has caused programs to be delayed. |
+| rainsensor      | Switch                 | RO | This channel indicates whether rain is detected by the device or not.              |
+| resetStations   | Switch                 | RW | The ON command will stop all stations immediately, including those waiting to run. |
+| sensor2         | Switch                 | RO | This channel is for the second sensor (if your hardware supports it).              |
+| signalStrength  | Number                 | RO | Shows how strong the WiFi Signal is.     |
 | stations        | String                 | RW | Display a list of stations that can be run when selected to the length of time set |
 |                 |                        |    | in the `nextDuration` channel.                                                  |
-| nextDuration    | Number:Time            | RW | The time the station will open for when any stations are selected from the         |
-|                 |                        |    | `stations` channel. Defaults to 30 minutes if not set.                           |
-| resetStations   | Switch                 | RW | The ON command will stop all stations immediately, including those waiting to run. |
-| enablePrograms  | Switch                 | RW | Allow programs to auto run. When OFF, manually started stations will still work.   |
-| rainDelay       | Number:Time            | RW | Sets/Shows the amount of time (hours) that rain has caused programs to be delayed. |
+| waterlevel      | Number:Dimensionless   | RO | This channel shows the current water level in percent (0-250%). The water level is |
+|                 |                        |    | calculated based on the weather and influences the duration of the water programs. |
+| queuedZones     | Number                 | RO | A count of how many zones are running and also waiting to run in the queue.        |
 
 ## Textual Example
 
index b77b0c4c24e819edfbcd172cd99cf1b3749a02f3..cc96b16715cf22cad92c7bf66fbe9522bd39e618 100644 (file)
@@ -13,5 +13,4 @@
   <artifactId>org.openhab.binding.opensprinkler</artifactId>
 
   <name>openHAB Add-ons :: Bundles :: OpenSprinkler Binding</name>
-
 </project>
index c698e4ec218872ab591a32bc6a17369910a2ab62..d16fab814533e59d1546fc11b4212333c21e47e5 100644 (file)
@@ -82,4 +82,7 @@ public class OpenSprinklerBindingConstants {
     public static final String NEXT_DURATION = "nextDuration";
     public static final String CHANNEL_IGNORE_RAIN = "ignoreRain";
     public static final String CHANNEL_RAIN_DELAY = "rainDelay";
+    public static final String CHANNEL_QUEUED_ZONES = "queuedZones";
+    public static final String CHANNEL_CLOUD_CONNECTED = "cloudConnected";
+    public static final String CHANNEL_PAUSE_PROGRAMS = "pausePrograms";
 }
index 7deed6cd6655c89666224096c4eb794098996d8f..f0804c83120dfe85458e377a14f3fd5f59993fa2 100644 (file)
@@ -63,6 +63,9 @@ public class OpenSprinklerState {
         public int rssi = 1;
         public int flcrt = -1;
         public int curr = -1;
+        public int pt = -1;
+        public int nq = -1;
+        public int otcs = -1;
     }
 
     public static class JnResponse {
index 42df1e5b687e6423894ee37d2a8c059d5b12ce7d..483275ac2cc32e071ddb6fc0d2c0a3c125157b1a 100644 (file)
@@ -253,4 +253,34 @@ public interface OpenSprinklerApi {
      * @return {@code QuantityType<Time>}
      */
     QuantityType<Time> getRainDelay();
+
+    /**
+     * Returns the Number of zones in the queue as an int.
+     *
+     * @return Number of zones in the queue as an int.
+     */
+    int getQueuedZones();
+
+    /**
+     * Returns the connection status of the OpenSprinkler Cloud.
+     *
+     * @return Connection state 0: not enabled, 1: connecting, 2: disconnected, 3: connected
+     */
+    int getCloudConnected();
+
+    /**
+     * Returns the paused status of the OpenSprinkler.
+     *
+     * @return int 0 to 600 seconds
+     */
+    int getPausedState();
+
+    /**
+     * Sets the amount of time that the OpenSprinkler will stop/pause zones.
+     *
+     * @param seconds for the pause duration in seconds (0 to 600)
+     * @throws UnauthorizedApiException
+     * @throws CommunicationApiException
+     */
+    void setPausePrograms(int seconds) throws UnauthorizedApiException, CommunicationApiException;
 }
index 4e6bad47d53a9fdfaaba1326159dc06e81323045..42fd39cde31da3b10d411eebf72e8f5b3436c70b 100644 (file)
@@ -70,8 +70,10 @@ public class OpenSprinklerApiFactory {
             return new OpenSprinklerHttpApiV213(this.httpClient, config);
         } else if (version >= 217 && version < 219) {
             return new OpenSprinklerHttpApiV217(this.httpClient, config);
-        } else if (version >= 219) {
+        } else if (version >= 219 && version < 220) {
             return new OpenSprinklerHttpApiV219(this.httpClient, config);
+        } else if (version >= 220) {
+            return new OpenSprinklerHttpApiV220(this.httpClient, config);
         } else {
             /* Need to make sure we have an older OpenSprinkler device by checking the first station. */
             try {
index 345242e2358a964c3c639e8f01eb75b6409906db..b282523f702ce4ac8dd10c18b85fa5dd636f1267 100644 (file)
@@ -432,4 +432,23 @@ class OpenSprinklerHttpApiV100 implements OpenSprinklerApi {
             return response.getContentAsString();
         }
     }
+
+    @Override
+    public int getQueuedZones() {
+        return state.jcReply.nq;
+    }
+
+    @Override
+    public int getCloudConnected() {
+        return state.jcReply.otcs;
+    }
+
+    @Override
+    public int getPausedState() {
+        return state.jcReply.pt;
+    }
+
+    @Override
+    public void setPausePrograms(int seconds) throws UnauthorizedApiException, CommunicationApiException {
+    }
 }
index 23f1ae7f997c83c02e43f869f55e8f848a725396..b2a9e2821c37719f8280eba1c7e698c3f31a0ce3 100644 (file)
@@ -21,7 +21,7 @@ import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterf
 
 /**
  * The {@link OpenSprinklerHttpApiV219} class is used for communicating with
- * the firmware versions 2.1.9 and up.
+ * the firmware versions 2.1.9
  *
  * @author Matthew Skinner - Initial contribution
  */
diff --git a/bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/api/OpenSprinklerHttpApiV220.java b/bundles/org.openhab.binding.opensprinkler/src/main/java/org/openhab/binding/opensprinkler/internal/api/OpenSprinklerHttpApiV220.java
new file mode 100644 (file)
index 0000000..962aa7c
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2010-2023 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.opensprinkler.internal.api;
+
+import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.CMD_PROGRAM_DATA;
+
+import java.util.ArrayList;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.opensprinkler.internal.OpenSprinklerState.JpResponse;
+import org.openhab.binding.opensprinkler.internal.api.exception.CommunicationApiException;
+import org.openhab.binding.opensprinkler.internal.api.exception.GeneralApiException;
+import org.openhab.binding.opensprinkler.internal.api.exception.UnauthorizedApiException;
+import org.openhab.binding.opensprinkler.internal.config.OpenSprinklerHttpInterfaceConfig;
+import org.openhab.core.types.StateOption;
+
+import com.google.gson.JsonParseException;
+
+/**
+ * The {@link OpenSprinklerHttpApiV220} class is used for communicating with
+ * the firmware versions 2.2.0 and up.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@NonNullByDefault
+public class OpenSprinklerHttpApiV220 extends OpenSprinklerHttpApiV219 {
+
+    OpenSprinklerHttpApiV220(HttpClient httpClient, OpenSprinklerHttpInterfaceConfig config)
+            throws GeneralApiException, CommunicationApiException {
+        super(httpClient, config);
+    }
+
+    @Override
+    public void getProgramData() throws CommunicationApiException, UnauthorizedApiException {
+        String returnContent;
+        try {
+            returnContent = http.sendHttpGet(getBaseUrl() + CMD_PROGRAM_DATA, getRequestRequiredOptions());
+        } catch (CommunicationApiException exp) {
+            throw new CommunicationApiException(
+                    "There was a problem in the HTTP communication with the OpenSprinkler API: " + exp.getMessage());
+        }
+        try {
+            JpResponse resp = gson.fromJson(returnContent, JpResponse.class);
+            if (resp != null && resp.pd.length > 0) {
+                state.programs = new ArrayList<>();
+                int counter = 0;
+                for (Object x : resp.pd) {
+                    String temp = x.toString();
+                    logger.trace("Program Data:{}", temp);
+                    int end = temp.lastIndexOf('[') - 2;
+                    int start = temp.lastIndexOf((','), end - 1) + 2;
+                    if (start > -1 && end > -1) {
+                        temp = temp.substring(start, end);
+                        state.programs.add(new StateOption(Integer.toString(counter++), temp));
+                    }
+                }
+            }
+        } catch (JsonParseException e) {
+            logger.debug("Following json could not be parsed:{}", returnContent);
+        }
+    }
+
+    @Override
+    public void setPausePrograms(int seconds) throws UnauthorizedApiException, CommunicationApiException {
+        http.sendHttpGet(getBaseUrl() + "pq", getRequestRequiredOptions() + "&dur=" + seconds);
+    }
+}
index 39b41d1052a47a5f74f78291eabcd57f0652ada9..92e74edf64d8d1444ca35002cb74fd028593ae9b 100644 (file)
@@ -116,6 +116,15 @@ public class OpenSprinklerDeviceHandler extends OpenSprinklerBaseHandler {
                 break;
             case CHANNEL_RESET_STATIONS:
                 break;
+            case CHANNEL_QUEUED_ZONES:
+                updateState(channel, new DecimalType(localAPI.getQueuedZones()));
+                break;
+            case CHANNEL_CLOUD_CONNECTED:
+                updateState(channel, OnOffType.from(localAPI.getCloudConnected() == 3));
+                break;
+            case CHANNEL_PAUSE_PROGRAMS:
+                updateState(channel, new QuantityType<>(localAPI.getPausedState(), Units.SECOND));
+                break;
             default:
                 logger.debug("Can not update the unknown channel {}", channel);
         }
@@ -145,6 +154,18 @@ public class OpenSprinklerDeviceHandler extends OpenSprinklerBaseHandler {
             if (localAPI.getSensor2State() == -1 && channel != null) {
                 removeChannels.add(channel);
             }
+            channel = thing.getChannel(CHANNEL_QUEUED_ZONES);
+            if (localAPI.getQueuedZones() == -1 && channel != null) {
+                removeChannels.add(channel);
+            }
+            channel = thing.getChannel(CHANNEL_CLOUD_CONNECTED);
+            if (localAPI.getCloudConnected() == -1 && channel != null) {
+                removeChannels.add(channel);
+            }
+            channel = thing.getChannel(CHANNEL_PAUSE_PROGRAMS);
+            if (localAPI.getPausedState() == -1 && channel != null) {
+                removeChannels.add(channel);
+            }
             if (!removeChannels.isEmpty()) {
                 ThingBuilder thingBuilder = editThing();
                 thingBuilder.withoutChannels(removeChannels);
@@ -233,6 +254,23 @@ public class OpenSprinklerDeviceHandler extends OpenSprinklerBaseHandler {
                     case CHANNEL_RAIN_DELAY:
                         handleRainDelayCommand(channelUID, command, api);
                         break;
+                    case CHANNEL_PAUSE_PROGRAMS:
+                        if (command == OnOffType.OFF) {
+                            api.setPausePrograms(0);
+                        } else if (command instanceof DecimalType) {
+                            api.setPausePrograms(((BigDecimal) command).intValue());
+                        } else if (command instanceof QuantityType<?>) {
+                            QuantityType<?> quantity = (QuantityType<?>) command;
+                            quantity = quantity.toUnit(Units.SECOND);
+                            if (quantity != null) {
+                                api.setPausePrograms(quantity.toBigDecimal().intValue());
+                            }
+                        } else {
+                            logger.warn(
+                                    "The CHANNEL_PAUSE_PROGRAMS only supports QuanityType in seconds, DecimalType and OFF");
+                            return;
+                        }
+                        break;
                 }
                 localBridge.delayedRefresh();// update sensors and controls after command is sent
             }
index 5b961ed9f563a2ed9d0773c1fe59a843464b80ec..7bb8cf3eea8b95694694a82591d986878d642e48 100644 (file)
@@ -31,6 +31,8 @@ thing-type.config.opensprinkler.station.stationIndex.description = The index of
 
 # channel types
 
+channel-type.opensprinkler.cloudConnected.label = Cloud Connected
+channel-type.opensprinkler.cloudConnected.description = If the device is fully connected to the OpenSprinkler cloud this will show as 'ON'.
 channel-type.opensprinkler.currentDraw.label = Current Draw
 channel-type.opensprinkler.currentDraw.description = The current draw in mA
 channel-type.opensprinkler.enablePrograms.label = Enable Programs
@@ -56,10 +58,23 @@ channel-type.opensprinkler.nextDuration.state.option.90min = 1.5 Hours
 channel-type.opensprinkler.nextDuration.state.option.2h = 2 Hours
 channel-type.opensprinkler.nextDuration.state.option.3h = 3 Hours
 channel-type.opensprinkler.nextDuration.state.option.4h = 4 Hours
+channel-type.opensprinkler.pausePrograms.label = Pause Programs
+channel-type.opensprinkler.pausePrograms.description = The duration that all zones will be paused for before resuming the watering.
+channel-type.opensprinkler.pausePrograms.state.option.0s = Not Paused
+channel-type.opensprinkler.pausePrograms.state.option.15s = 15 Seconds
+channel-type.opensprinkler.pausePrograms.state.option.30s = 30 Seconds
+channel-type.opensprinkler.pausePrograms.state.option.1min = 1 Minute
+channel-type.opensprinkler.pausePrograms.state.option.2min = 2 Minutes
+channel-type.opensprinkler.pausePrograms.state.option.3min = 3 Minutes
+channel-type.opensprinkler.pausePrograms.state.option.4min = 4 Minutes
+channel-type.opensprinkler.pausePrograms.state.option.5min = 5 Minutes
+channel-type.opensprinkler.pausePrograms.state.option.10min = 10 Minutes
 channel-type.opensprinkler.programs.label = Run Program
 channel-type.opensprinkler.programs.description = Run a program that is saved inside the OpenSprinkler Device.
 channel-type.opensprinkler.queued.label = Queued
 channel-type.opensprinkler.queued.description = Indicates if the station is queued to be turned on. Can be removed from the queue by turning off. ON is read-only.
+channel-type.opensprinkler.queuedZones.label = Number Of Queued Zones
+channel-type.opensprinkler.queuedZones.description = A count of how many zones are running and also waiting to run in the queue.
 channel-type.opensprinkler.rainDelay.label = Rain Delay
 channel-type.opensprinkler.rainDelay.description = The amount of time in hours to delay the running of any program.
 channel-type.opensprinkler.rainDelay.state.option.0s = Off
index 35e543e02998d3552cd4125172bed4233cd5b2ba..bdd001917a5a3bf1596bb3af98ba68f359c53e51 100644 (file)
                        <channel id="resetStations" typeId="resetStations"></channel>
                        <channel id="enablePrograms" typeId="enablePrograms"></channel>
                        <channel id="rainDelay" typeId="rainDelay"></channel>
+                       <channel id="queuedZones" typeId="queuedZones"></channel>
+                       <channel id="cloudConnected" typeId="cloudConnected"></channel>
+                       <channel id="pausePrograms" typeId="pausePrograms"></channel>
                </channels>
+
+               <properties>
+                       <property name="thingTypeVersion">1</property>
+               </properties>
+
        </thing-type>
 
        <channel-type id="rainsensor">
                <state readOnly="true"/>
        </channel-type>
 
+       <channel-type id="cloudConnected">
+               <item-type>Switch</item-type>
+               <label>Cloud Connected</label>
+               <description>If the device is fully connected to the OpenSprinkler cloud this will show as 'ON'.</description>
+               <category>Sensor</category>
+               <state readOnly="true"/>
+       </channel-type>
+
        <channel-type id="waterlevel">
                <item-type>Number:Dimensionless</item-type>
                <label>Water Level</label>
                <state readOnly="true"/>
        </channel-type>
 
+       <channel-type id="queuedZones">
+               <item-type>Number</item-type>
+               <label>Number Of Queued Zones</label>
+               <description>A count of how many zones are running and also waiting to run in the queue.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
        <channel-type id="currentDraw">
                <item-type>Number:ElectricCurrent</item-type>
                <label>Current Draw</label>
                <state readOnly="true" pattern="%.0f min"/>
        </channel-type>
 
+       <channel-type id="pausePrograms">
+               <item-type>Number:Time</item-type>
+               <label>Pause Programs</label>
+               <description>The duration that all zones will be paused for before resuming the watering.</description>
+               <category>Time</category>
+               <state>
+                       <options>
+                               <option value="0s">Not Paused</option>
+                               <option value="15s">15 Seconds</option>
+                               <option value="30s">30 Seconds</option>
+                               <option value="1min">1 Minute</option>
+                               <option value="2min">2 Minutes</option>
+                               <option value="3min">3 Minutes</option>
+                               <option value="4min">4 Minutes</option>
+                               <option value="5min">5 Minutes</option>
+                               <option value="10min">10 Minutes</option>
+                       </options>
+               </state>
+       </channel-type>
+
        <channel-type id="nextDuration">
                <item-type>Number:Time</item-type>
                <label>Next Duration</label>
diff --git a/bundles/org.openhab.binding.opensprinkler/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.opensprinkler/src/main/resources/OH-INF/update/instructions.xml
new file mode 100644 (file)
index 0000000..44ad9ee
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
+
+       <thing-type uid="opensprinkler:device">
+
+               <instruction-set targetVersion="1">
+                       <add-channel id="queuedZones">
+                               <type>opensprinkler:queuedZones</type>
+                       </add-channel>
+                       <add-channel id="cloudConnected">
+                               <type>opensprinkler:cloudConnected</type>
+                       </add-channel>
+                       <add-channel id="pausePrograms">
+                               <type>opensprinkler:pausePrograms</type>
+                       </add-channel>
+               </instruction-set>
+
+       </thing-type>
+
+</update:update-descriptions>