Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 14 commits into from
Dec 10, 2023
23 changes: 13 additions & 10 deletions bundles/org.openhab.binding.opensprinkler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'.|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am asking myself if a channel is really the good option.
The connection status should probably be something defining the thing status ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jlaur : WDYT ?

Copy link
Contributor

@jlaur jlaur Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I need some more context. The binding is declared as local, so if I understand this correctly, this is just some information from the device whether it is also integrated with a cloud service in addition to being integrated through the local API?

In that case, what would be the use of this channel? Perhaps it would be sufficient to provide this as a property? And also, in that case it probably shouldn't impact the thing status, since it wouldn't interfere with the openHAB integration in any way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe a property is wrong as it has a state that changes. Read only channel seemed to fit. Your thoughts are correct and I also do not see the use case, but was easy to add it when doing other channels instead of someone requesting it later. It could possibly be used to fault find why you can not use their cloud to connect and control the sprinkler. The binding is full local and it is perhaps strange that the device can do both without trying to force you to do one over the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to delete the channel then to spend any more time on changing or adding extra lines to add it to things that are missing the channel. I don't have a lot of spare time these days.

| 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:Dimensionless | RO | A count of how many zones are running and also waiting to run in the queue. |
Skinah marked this conversation as resolved.
Show resolved Hide resolved

## Textual Example

Expand Down
1 change: 0 additions & 1 deletion bundles/org.openhab.binding.opensprinkler/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@
<artifactId>org.openhab.binding.opensprinkler</artifactId>

<name>openHAB Add-ons :: Bundles :: OpenSprinkler Binding</name>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public static class JcResponse {
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ public OpenSprinklerApi getHttpApi(OpenSprinklerHttpInterfaceConfig config)
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,4 +432,23 @@ public String sendHttpPost(String url, String urlParameters) throws Communicatio
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 {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

/**
* 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
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* 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;

/**
* 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());
}
JpResponse resp = gson.fromJson(returnContent, JpResponse.class);
Skinah marked this conversation as resolved.
Show resolved Hide resolved
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));
}
}
}
}

@Override
public void setPausePrograms(int seconds) throws UnauthorizedApiException, CommunicationApiException {
http.sendHttpGet(getBaseUrl() + "pq", getRequestRequiredOptions() + "&dur=" + seconds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,19 @@ protected void updateChannel(ChannelUID channel) {
break;
case CHANNEL_RESET_STATIONS:
break;
case CHANNEL_QUEUED_ZONES:
updateState(channel, new DecimalType(localAPI.getQueuedZones()));
break;
case CHANNEL_CLOUD_CONNECTED:
if (localAPI.getCloudConnected() == 3) {
updateState(channel, OnOffType.ON);
} else {
updateState(channel, OnOffType.OFF);
}
Skinah marked this conversation as resolved.
Show resolved Hide resolved
break;
case CHANNEL_PAUSE_PROGRAMS:
updateState(channel, new QuantityType<>(localAPI.getPausedState(), Units.SECOND));
break;
default:
logger.debug("Can not update the unknown channel {}", channel);
}
Expand Down Expand Up @@ -145,6 +158,18 @@ public void initialize() {
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);
Expand Down Expand Up @@ -233,6 +258,24 @@ public void handleCommand(ChannelUID channelUID, Command command) {
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());
}
api.setPausePrograms(((BigDecimal) command).intValue());
Skinah marked this conversation as resolved.
Show resolved Hide resolved
} 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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@
<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>
Skinah marked this conversation as resolved.
Show resolved Hide resolved
</channels>
</thing-type>

Expand All @@ -108,6 +111,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>
Expand All @@ -123,6 +134,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>
Expand Down Expand Up @@ -176,6 +194,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>
Expand Down