]> git.basschouten.com Git - openhab-addons.git/commitdiff
[hydrawise] Various Fixes (#17345)
authorDan Cunningham <dan@digitaldan.com>
Wed, 28 Aug 2024 22:50:26 +0000 (15:50 -0700)
committerGitHub <noreply@github.com>
Wed, 28 Aug 2024 22:50:26 +0000 (00:50 +0200)
* Workaround for a bad response from the Hydrawise API

Signed-off-by: Dan Cunningham <dan@digitaldan.com>
12 files changed:
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/HydrawiseConnectionException.java
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/HydrawiseGraphQLClient.java
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/dto/Controller.java
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/dto/Hardware.java [new file with mode: 0644]
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/dto/Model.java [new file with mode: 0644]
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/discovery/HydrawiseCloudControllerDiscoveryService.java
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/handler/HydrawiseAccountHandler.java
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/handler/HydrawiseControllerHandler.java
bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/handler/HydrawiseLocalHandler.java
bundles/org.openhab.binding.hydrawise/src/main/resources/OH-INF/thing/things.xml
bundles/org.openhab.binding.hydrawise/src/main/resources/query.graphql
bundles/org.openhab.binding.hydrawise/src/main/resources/weather.graphql [new file with mode: 0644]

index 0421f15e2e0ca766fc9901145737aa7946342662..acfc1fb6979914c3c91cd175920a29979f13afd0 100644 (file)
@@ -23,6 +23,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 public class HydrawiseConnectionException extends Exception {
     private static final long serialVersionUID = 1L;
 
+    private int code = 0;
+    private String response = "";
+
     public HydrawiseConnectionException(Exception e) {
         super(e);
     }
@@ -30,4 +33,22 @@ public class HydrawiseConnectionException extends Exception {
     public HydrawiseConnectionException(String message) {
         super(message);
     }
+
+    public HydrawiseConnectionException(String message, int code, String response) {
+        super(message);
+        this.code = code;
+        this.response = response;
+    }
+
+    public static long getSerialversionuid() {
+        return serialVersionUID;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getResponse() {
+        return response;
+    }
 }
index 381d78000ebe0056a5543526bec54a5fa910807c..4d933306324cb82d215f449d1f68140e8a34d3f0 100644 (file)
@@ -36,6 +36,7 @@ import org.openhab.binding.hydrawise.internal.api.HydrawiseCommandException;
 import org.openhab.binding.hydrawise.internal.api.HydrawiseConnectionException;
 import org.openhab.binding.hydrawise.internal.api.graphql.dto.ControllerStatus;
 import org.openhab.binding.hydrawise.internal.api.graphql.dto.Forecast;
+import org.openhab.binding.hydrawise.internal.api.graphql.dto.Hardware;
 import org.openhab.binding.hydrawise.internal.api.graphql.dto.Mutation;
 import org.openhab.binding.hydrawise.internal.api.graphql.dto.MutationResponse;
 import org.openhab.binding.hydrawise.internal.api.graphql.dto.MutationResponse.MutationResponseStatus;
@@ -77,7 +78,8 @@ public class HydrawiseGraphQLClient {
             .registerTypeAdapter(ZoneRun.class, new ResponseDeserializer<ZoneRun>())
             .registerTypeAdapter(Forecast.class, new ResponseDeserializer<Forecast>())
             .registerTypeAdapter(Sensor.class, new ResponseDeserializer<Forecast>())
-            .registerTypeAdapter(ControllerStatus.class, new ResponseDeserializer<ControllerStatus>()).create();
+            .registerTypeAdapter(ControllerStatus.class, new ResponseDeserializer<ControllerStatus>())
+            .registerTypeAdapter(Hardware.class, new ResponseDeserializer<ControllerStatus>()).create();
 
     private static final String GRAPH_URL = "https://app.hydrawise.com/api/v2/graph";
     private static final String MUTATION_START_ZONE = "startZone(zoneId: %d) { status }";
@@ -94,6 +96,7 @@ public class HydrawiseGraphQLClient {
     private final HttpClient httpClient;
     private final OAuthClientService oAuthService;
     private String queryString = "";
+    private String weatherString = "";
 
     public HydrawiseGraphQLClient(HttpClient httpClient, OAuthClientService oAuthService) {
         this.httpClient = httpClient;
@@ -110,19 +113,48 @@ public class HydrawiseGraphQLClient {
     public @Nullable QueryResponse queryControllers()
             throws HydrawiseConnectionException, HydrawiseAuthenticationException {
         try {
-            QueryRequest query = new QueryRequest(getQueryString());
-            String queryJson = gson.toJson(query);
-            String response = sendGraphQLQuery(queryJson);
-            try {
-                return gson.fromJson(response, QueryResponse.class);
-            } catch (JsonSyntaxException e) {
-                throw new HydrawiseConnectionException("Invalid Response: " + response);
-            }
+            return queryRequest(getQueryString());
+        } catch (IOException e) {
+            throw new HydrawiseConnectionException(e);
+        }
+    }
+
+    /**
+     * Sends a GrapQL query for controller data
+     *
+     * @return QueryResponse
+     * @throws HydrawiseConnectionException
+     * @throws HydrawiseAuthenticationException
+     */
+    public @Nullable QueryResponse queryWeather()
+            throws HydrawiseConnectionException, HydrawiseAuthenticationException {
+        try {
+            return queryRequest(getWeatherString());
         } catch (IOException e) {
             throw new HydrawiseConnectionException(e);
         }
     }
 
+    /**
+     * Sends a GrapQL query for controller data
+     *
+     * @param queryString
+     * @return QueryResponse
+     * @throws HydrawiseConnectionException
+     * @throws HydrawiseAuthenticationException
+     */
+    private @Nullable QueryResponse queryRequest(String queryString)
+            throws HydrawiseConnectionException, HydrawiseAuthenticationException {
+        QueryRequest query = new QueryRequest(queryString);
+        String queryJson = gson.toJson(query);
+        String response = sendGraphQLQuery(queryJson);
+        try {
+            return gson.fromJson(response, QueryResponse.class);
+        } catch (JsonSyntaxException e) {
+            throw new HydrawiseConnectionException("Invalid Response: " + response);
+        }
+    }
+
     /***
      * Stops a given relay
      *
@@ -313,7 +345,8 @@ public class HydrawiseGraphQLClient {
             int statusCode = response.getStatus();
             if (!HttpStatus.isSuccess(statusCode)) {
                 throw new HydrawiseConnectionException(
-                        "Request failed with HTTP status code: " + statusCode + " response: " + stringResponse);
+                        "Request failed with HTTP status code: " + statusCode + " response: " + stringResponse,
+                        statusCode, stringResponse);
             }
             return stringResponse;
         } catch (InterruptedException | TimeoutException | OAuthException | IOException e) {
@@ -338,15 +371,25 @@ public class HydrawiseGraphQLClient {
 
     private String getQueryString() throws IOException {
         if (queryString.isBlank()) {
-            try (InputStream inputStream = HydrawiseGraphQLClient.class.getClassLoader()
-                    .getResourceAsStream("query.graphql");
-                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
-                queryString = bufferedReader.lines().collect(Collectors.joining("\n"));
-            }
+            queryString = getResourceString("query.graphql");
         }
         return queryString;
     }
 
+    private String getWeatherString() throws IOException {
+        if (weatherString.isBlank()) {
+            weatherString = getResourceString("weather.graphql");
+        }
+        return weatherString;
+    }
+
+    private String getResourceString(String name) throws IOException {
+        try (InputStream inputStream = HydrawiseGraphQLClient.class.getClassLoader().getResourceAsStream(name);
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
+            return bufferedReader.lines().collect(Collectors.joining("\n"));
+        }
+    }
+
     class ResponseDeserializer<T> implements JsonDeserializer<T> {
         @Override
         @Nullable
index d339efe4a62a5f8b06a8fa1c9b6f3314d1970b0b..e5e3d794f9b2775cd2cbddde27c3b06a27ce7459 100644 (file)
@@ -22,8 +22,8 @@ public class Controller {
     public Integer id;
     public String name;
     public ControllerStatus status;
+    public Hardware hardware;
     public Location location;
     public List<Zone> zones = null;
     public List<Sensor> sensors = null;
-    public List<Forecast> forecast = null;
 }
diff --git a/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/dto/Hardware.java b/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/dto/Hardware.java
new file mode 100644 (file)
index 0000000..478a144
--- /dev/null
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2010-2024 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.hydrawise.internal.api.graphql.dto;
+
+/**
+ *
+ * @author Dan Cunningham - Initial contribution
+ *
+ */
+public class Hardware {
+    public String version;
+    public Model model;
+}
diff --git a/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/dto/Model.java b/bundles/org.openhab.binding.hydrawise/src/main/java/org/openhab/binding/hydrawise/internal/api/graphql/dto/Model.java
new file mode 100644 (file)
index 0000000..0981ca7
--- /dev/null
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2010-2024 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.hydrawise.internal.api.graphql.dto;
+
+/**
+ *
+ * @author Dan Cunningham - Initial contribution
+ *
+ */
+public class Model {
+    public Integer maxZones;
+    public String name;
+    public String description;
+}
index b01ab60632545ab900f492453c56246ae466059e..fe8c8c11e0626d88fef8c743dbcebdd89ed3c727 100644 (file)
  */
 package org.openhab.binding.hydrawise.internal.discovery;
 
-import java.time.Instant;
+import java.util.Date;
 import java.util.List;
 import java.util.Set;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.hydrawise.internal.HydrawiseBindingConstants;
 import org.openhab.binding.hydrawise.internal.HydrawiseControllerListener;
 import org.openhab.binding.hydrawise.internal.api.graphql.dto.Controller;
 import org.openhab.binding.hydrawise.internal.api.graphql.dto.Customer;
 import org.openhab.binding.hydrawise.internal.handler.HydrawiseAccountHandler;
-import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
 import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
 import org.openhab.core.thing.binding.ThingHandlerService;
 import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.ServiceScope;
 
 /**
  *
@@ -36,33 +37,44 @@ import org.osgi.service.component.annotations.ServiceScope;
  */
 
 @NonNullByDefault
-@Component(scope = ServiceScope.PROTOTYPE, service = ThingHandlerService.class)
-public class HydrawiseCloudControllerDiscoveryService
-        extends AbstractThingHandlerDiscoveryService<HydrawiseAccountHandler> implements HydrawiseControllerListener {
+@Component(service = ThingHandlerService.class)
+public class HydrawiseCloudControllerDiscoveryService extends AbstractDiscoveryService
+        implements HydrawiseControllerListener, ThingHandlerService {
+
     private static final int TIMEOUT = 5;
+    @Nullable
+    HydrawiseAccountHandler handler;
 
     public HydrawiseCloudControllerDiscoveryService() {
-        super(HydrawiseAccountHandler.class, Set.of(HydrawiseBindingConstants.THING_TYPE_CONTROLLER), TIMEOUT, true);
+        super(Set.of(HydrawiseBindingConstants.THING_TYPE_CONTROLLER), TIMEOUT, true);
     }
 
     @Override
     protected void startScan() {
-        Customer data = thingHandler.lastData();
-        if (data != null) {
-            data.controllers.forEach(controller -> addDiscoveryResults(controller));
+        HydrawiseAccountHandler localHandler = this.handler;
+        if (localHandler != null) {
+            Customer data = localHandler.lastData();
+            if (data != null) {
+                data.controllers.forEach(controller -> addDiscoveryResults(controller));
+            }
         }
     }
 
     @Override
-    public void dispose() {
-        super.dispose();
-        removeOlderResults(Instant.now().toEpochMilli(), thingHandler.getThing().getUID());
+    public void deactivate() {
+        HydrawiseAccountHandler localHandler = this.handler;
+        if (localHandler != null) {
+            removeOlderResults(new Date().getTime(), localHandler.getThing().getUID());
+        }
     }
 
     @Override
     protected synchronized void stopScan() {
         super.stopScan();
-        removeOlderResults(getTimestampOfLastScan(), thingHandler.getThing().getUID());
+        HydrawiseAccountHandler localHandler = this.handler;
+        if (localHandler != null) {
+            removeOlderResults(getTimestampOfLastScan(), localHandler.getThing().getUID());
+        }
     }
 
     @Override
@@ -71,19 +83,27 @@ public class HydrawiseCloudControllerDiscoveryService
     }
 
     @Override
-    public void initialize() {
-        thingHandler.addControllerListeners(this);
-        super.initialize();
+    public void setThingHandler(ThingHandler handler) {
+        this.handler = (HydrawiseAccountHandler) handler;
+        this.handler.addControllerListeners(this);
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return handler;
     }
 
     private void addDiscoveryResults(Controller controller) {
-        String label = String.format("Hydrawise Controller %s", controller.name);
-        int id = controller.id;
-        ThingUID bridgeUID = thingHandler.getThing().getUID();
-        ThingUID thingUID = new ThingUID(HydrawiseBindingConstants.THING_TYPE_CONTROLLER, bridgeUID,
-                String.valueOf(id));
-        thingDiscovered(DiscoveryResultBuilder.create(thingUID).withLabel(label).withBridge(bridgeUID)
-                .withProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID, id)
-                .withRepresentationProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID).build());
+        HydrawiseAccountHandler localHandler = this.handler;
+        if (localHandler != null) {
+            String label = String.format("Hydrawise Controller %s", controller.name);
+            int id = controller.id;
+            ThingUID bridgeUID = localHandler.getThing().getUID();
+            ThingUID thingUID = new ThingUID(HydrawiseBindingConstants.THING_TYPE_CONTROLLER, bridgeUID,
+                    String.valueOf(id));
+            thingDiscovered(DiscoveryResultBuilder.create(thingUID).withLabel(label).withBridge(bridgeUID)
+                    .withProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID, id)
+                    .withRepresentationProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID).build());
+        }
     }
 }
index 8be5e5c2e1b24417d879d1a65b3ee575e236467a..f12ab20bc901fbaa7b9478de919f7a234382c126 100644 (file)
@@ -63,6 +63,7 @@ public class HydrawiseAccountHandler extends BaseBridgeHandler implements Access
      */
     private static final int MIN_REFRESH_SECONDS = 30;
     private static final int TOKEN_REFRESH_SECONDS = 60;
+    private static final int WEATHER_REFRESH_MILLIS = 60 * 60 * 1000; // 1 hour
     private static final String BASE_URL = "https://app.hydrawise.com/api/v2/";
     private static final String AUTH_URL = BASE_URL + "oauth/access-token";
     private static final String CLIENT_SECRET = "zn3CrjglwNV1";
@@ -77,6 +78,7 @@ public class HydrawiseAccountHandler extends BaseBridgeHandler implements Access
     private @Nullable ScheduledFuture<?> pollFuture;
     private @Nullable ScheduledFuture<?> tokenFuture;
     private @Nullable Customer lastData;
+    private long lastWeatherUpdate;
     private int refresh;
 
     public HydrawiseAccountHandler(final Bridge bridge, final HttpClient httpClient, final OAuthFactory oAuthFactory) {
@@ -228,6 +230,11 @@ public class HydrawiseAccountHandler extends BaseBridgeHandler implements Access
     }
 
     private void poll(boolean retry) {
+        HydrawiseGraphQLClient apiClient = this.apiClient;
+        if (apiClient == null) {
+            logger.debug("apiclient not initalized");
+            return;
+        }
         try {
             QueryResponse response = apiClient.queryControllers();
             if (response == null) {
@@ -240,6 +247,21 @@ public class HydrawiseAccountHandler extends BaseBridgeHandler implements Access
             if (getThing().getStatus() != ThingStatus.ONLINE) {
                 updateStatus(ThingStatus.ONLINE);
             }
+            long currentTime = System.currentTimeMillis();
+            if (currentTime > lastWeatherUpdate + WEATHER_REFRESH_MILLIS) {
+                lastWeatherUpdate = currentTime;
+                try {
+                    QueryResponse weatherResponse = apiClient.queryWeather();
+                    if (weatherResponse != null) {
+                        response.data.me.controllers.forEach(controller -> {
+                            weatherResponse.data.me.controllers.stream().filter(c -> c.id.equals(controller.id))
+                                    .findFirst().ifPresent(c -> controller.location.forecast = c.location.forecast);
+                        });
+                    }
+                } catch (HydrawiseConnectionException e) {
+                    logger.debug("Weather data is not supported", e);
+                }
+            }
             lastData = response.data.me;
             synchronized (controllerListeners) {
                 controllerListeners.forEach(listener -> {
index 60c90f82ef1dfd432f65b567d56078aedc6d6967..218a9ed92ae947ca195ea4795edc958d7af1f312 100644 (file)
@@ -27,6 +27,8 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.measure.quantity.Speed;
+import javax.measure.quantity.Temperature;
 import javax.measure.quantity.Volume;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -250,7 +252,7 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
                 updateForecast(controller.location.forecast);
             }
             if (controller.zones != null) {
-                updateZones(controller.zones);
+                updateZones(controller.zones, controller.hardware.model.maxZones);
             }
 
             // update values with what the cloud tells us even though the controller may be offline
@@ -278,20 +280,22 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
                         : UnDefType.NULL);
     }
 
-    private void updateZones(List<Zone> zones) {
+    private void updateZones(List<Zone> zones, int maxZones) {
         AtomicReference<Boolean> anyRunning = new AtomicReference<>(false);
         AtomicReference<Boolean> anySuspended = new AtomicReference<>(false);
         for (Zone zone : zones) {
-            // there are 12 relays per expander, expanders will have a zoneNumber like:
+            // for expansion modules who zones numbers are > 99
+            // there are maxZones relays per expander, expanders will have a zoneNumber like:
+            // maxZones = 12
             // 10 for expander 0, relay 10 = zone10
             // 101 for expander 1, relay 1 = zone13
             // 212 for expander 2, relay 12 = zone36
             // division of integers in Java give whole numbers, not remainders FYI
-            int zoneNumber = ((zone.number.value / 100) * 12) + (zone.number.value % 100);
-
+            int zoneNumber = zone.number.value <= 99 ? zone.number.value
+                    : ((zone.number.value / 100) * maxZones) + (zone.number.value % 100);
             String group = "zone" + zoneNumber;
             zoneMaps.put(group, zone);
-            logger.trace("Updateing Zone {} {} ", group, zone.name);
+            logger.trace("Updating Zone {} {} ", group, zone.name);
             updateGroupState(group, CHANNEL_ZONE_NAME, new StringType(zone.name));
             updateGroupState(group, CHANNEL_ZONE_ICON, new StringType(BASE_IMAGE_URL + zone.icon.fileName));
             if (zone.scheduledRuns != null) {
@@ -328,8 +332,9 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
                 updateGroupState(group, CHANNEL_ZONE_SUSPENDUNTIL, UnDefType.UNDEF);
             }
         }
-        updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_RUN, OnOffType.from(anyRunning.get()));
-        updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_SUSPEND, OnOffType.from(anySuspended.get()));
+        updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_RUN, anyRunning.get() ? OnOffType.ON : OnOffType.OFF);
+        updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_SUSPEND,
+                anySuspended.get() ? OnOffType.ON : OnOffType.OFF);
         updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_SUSPENDUNTIL, UnDefType.UNDEF);
     }
 
@@ -362,6 +367,7 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
         int i = 1;
         for (Forecast forecast : forecasts) {
             String group = "forecast" + (i++);
+            logger.trace("Updating {} {}", group, forecast.time);
             updateGroupState(group, CHANNEL_FORECAST_TIME, stringToDateTime(forecast.time));
             updateGroupState(group, CHANNEL_FORECAST_CONDITIONS, new StringType(forecast.conditions));
             updateGroupState(group, CHANNEL_FORECAST_HUMIDITY, new DecimalType(forecast.averageHumidity.intValue()));
@@ -383,12 +389,12 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
 
     private void updateTemperature(UnitValue temperature, String group, String channel) {
         logger.debug("TEMP {} {} {} {}", group, channel, temperature.unit, temperature.value);
-        updateGroupState(group, channel, new QuantityType<>(temperature.value,
-                "\\u00b0F".equals(temperature.unit) ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
+        updateGroupState(group, channel, new QuantityType<Temperature>(temperature.value,
+                temperature.unit.indexOf("F") >= 0 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
     }
 
     private void updateWindspeed(UnitValue wind, String group, String channel) {
-        updateGroupState(group, channel, new QuantityType<>(wind.value,
+        updateGroupState(group, channel, new QuantityType<Speed>(wind.value,
                 "mph".equals(wind.unit) ? ImperialUnits.MILES_PER_HOUR : SIUnits.KILOMETRE_PER_HOUR));
     }
 
@@ -439,10 +445,7 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
     }
 
     private QuantityType<Volume> waterFlowToQuantityType(Number flow, String units) {
-        double waterFlow = flow.doubleValue();
-        if ("gals".equals(units)) {
-            waterFlow = waterFlow * 3.785;
-        }
-        return new QuantityType<>(waterFlow, Units.LITRE);
+        return new QuantityType<>(flow.doubleValue(),
+                "gal".equals(units) ? ImperialUnits.GALLON_LIQUID_US : Units.LITRE);
     }
 }
index 667f7c0a801b2dab4adfb66215d78c254cc56d64..e57d527c743baadecb8ff5e4684337cf3b0a1ead 100644 (file)
@@ -214,7 +214,8 @@ public class HydrawiseLocalHandler extends BaseThingHandler {
                 updateGroupState(group, CHANNEL_ZONE_TIME_LEFT, new QuantityType<>(0, Units.SECOND));
             }
 
-            updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_RUN, OnOffType.from(!status.running.isEmpty()));
+            updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_RUN,
+                    !status.running.isEmpty() ? OnOffType.ON : OnOffType.OFF);
         });
     }
 
index 8f0434e97e31fb7518263dbcdce8534ac46d1530..1998c62bd57b1e524797205ab8319873dc7bdf20 100644 (file)
@@ -38,7 +38,8 @@
                <label>Hydrawise Controller Thing</label>
                <description>Hydrawise connected irrigation controller</description>
 
-               <!-- Until we have https://github.com/eclipse/smarthome/issues/1118 fixed, we need to list all possible channel groups.
+               <!-- Until we have https://github.com/eclipse/smarthome/issues/1118 fixed, we need to list
+                       all possible channel groups.
                        Once this is fixed we can dynamically add them to the thing and not list them here. -->
                <channel-groups>
 
                                <label>Zone 36</label>
                                <description>Sprinkler Zone 36</description>
                        </channel-group>
+                       <channel-group id="zone37" typeId="zone">
+                               <label>Zone 37</label>
+                               <description>Sprinkler Zone 37</description>
+                       </channel-group>
+                       <channel-group id="zone38" typeId="zone">
+                               <label>Zone 38</label>
+                               <description>Sprinkler Zone 38</description>
+                       </channel-group>
+                       <channel-group id="zone39" typeId="zone">
+                               <label>Zone 39</label>
+                               <description>Sprinkler Zone 39</description>
+                       </channel-group>
+                       <channel-group id="zone40" typeId="zone">
+                               <label>Zone 40</label>
+                               <description>Sprinkler Zone 40</description>
+                       </channel-group>
+                       <channel-group id="zone41" typeId="zone">
+                               <label>Zone 41</label>
+                               <description>Sprinkler Zone 41</description>
+                       </channel-group>
+                       <channel-group id="zone42" typeId="zone">
+                               <label>Zone 42</label>
+                               <description>Sprinkler Zone 42</description>
+                       </channel-group>
+                       <channel-group id="zone43" typeId="zone">
+                               <label>Zone 43</label>
+                               <description>Sprinkler Zone 43</description>
+                       </channel-group>
+                       <channel-group id="zone44" typeId="zone">
+                               <label>Zone 44</label>
+                               <description>Sprinkler Zone 44</description>
+                       </channel-group>
+                       <channel-group id="zone45" typeId="zone">
+                               <label>Zone 45</label>
+                               <description>Sprinkler Zone 45</description>
+                       </channel-group>
+                       <channel-group id="zone46" typeId="zone">
+                               <label>Zone 46</label>
+                               <description>Sprinkler Zone 46</description>
+                       </channel-group>
+                       <channel-group id="zone47" typeId="zone">
+                               <label>Zone 47</label>
+                               <description>Sprinkler Zone 47</description>
+                       </channel-group>
+                       <channel-group id="zone48" typeId="zone">
+                               <label>Zone 48</label>
+                               <description>Sprinkler Zone 48</description>
+                       </channel-group>
+                       <channel-group id="zone49" typeId="zone">
+                               <label>Zone 49</label>
+                               <description>Sprinkler Zone 49</description>
+                       </channel-group>
+                       <channel-group id="zone50" typeId="zone">
+                               <label>Zone 50</label>
+                               <description>Sprinkler Zone 50</description>
+                       </channel-group>
+                       <channel-group id="zone51" typeId="zone">
+                               <label>Zone 51</label>
+                               <description>Sprinkler Zone 51</description>
+                       </channel-group>
+                       <channel-group id="zone52" typeId="zone">
+                               <label>Zone 52</label>
+                               <description>Sprinkler Zone 52</description>
+                       </channel-group>
+                       <channel-group id="zone53" typeId="zone">
+                               <label>Zone 53</label>
+                               <description>Sprinkler Zone 53</description>
+                       </channel-group>
+                       <channel-group id="zone54" typeId="zone">
+                               <label>Zone 54</label>
+                               <description>Sprinkler Zone 54</description>
+                       </channel-group>
                </channel-groups>
                <config-description>
                        <parameter name="controllerId" type="integer" required="true">
index 6699a943c692d5d1ad0f71770763d494a1d108c7..b20d9ab8b694e49ea89b05c513d03459d5d9520b 100644 (file)
@@ -5,41 +5,26 @@
       controllers {
       id
       name
-        status {
+      status {
         summary
         online
         lastContact {
           timestamp
         }
-      }    
+      }
+      hardware {
+          version
+          model {
+            maxZones
+            name
+            description
+          }
+        }    
       location {
         coordinates {
           latitude
           longitude
         }
-        forecast(days: 3) {
-          time
-          updateTime
-          conditions
-          averageWindSpeed {
-            value
-            unit
-          }
-          highTemperature {
-            value
-            unit
-          }
-          lowTemperature {
-            value
-            unit
-          }
-          probabilityOfPrecipitation
-          precipitation {
-            value
-            unit
-          }
-          averageHumidity
-        }
       }
       zones {
         id
diff --git a/bundles/org.openhab.binding.hydrawise/src/main/resources/weather.graphql b/bundles/org.openhab.binding.hydrawise/src/main/resources/weather.graphql
new file mode 100644 (file)
index 0000000..1b933f8
--- /dev/null
@@ -0,0 +1,34 @@
+{
+  me {
+    email
+    lastContact
+    controllers {
+      id
+      location {
+        forecast(days: 3) {
+          time
+          updateTime
+          conditions
+          averageWindSpeed {
+            value
+            unit
+          }
+          highTemperature {
+            value
+            unit
+          }
+          lowTemperature {
+            value
+            unit
+          }
+          probabilityOfPrecipitation
+          precipitation {
+            value
+            unit
+          }
+          averageHumidity
+        }
+      }
+    }
+  }
+}