[opensprinkler] Fix Program names and add new features for firmware 2.2.0 (#15410)

* Fix Program names are not parsed correctly in firmware 2.2.0

---------

Signed-off-by: Matthew Skinner <matt@pcmus.com>
This commit is contained in:
Matthew Skinner 2023-12-10 20:25:57 +11:00 committed by GitHub
parent d791e7900a
commit a950f19e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 269 additions and 14 deletions

View 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. |
| programs | String | RW | Displays a list of the programs that are setup in your OpenSprinkler and when |
| | | | selected will start that program for you. |
| 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. |
| 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. |
| 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

View File

@ -13,5 +13,4 @@
<artifactId>org.openhab.binding.opensprinkler</artifactId>
<name>openHAB Add-ons :: Bundles :: OpenSprinkler Binding</name>
</project>

View 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";
}

View 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 {

View 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;
}

View 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 {

View 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 {
}
}

View 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
*/

View File

@ -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);
}
}

View 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
}

View 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

View File

@ -89,7 +89,15 @@
<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">
@ -108,6 +116,14 @@
<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>
@ -123,6 +139,13 @@
<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>
@ -176,6 +199,26 @@
<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>

View File

@ -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>