mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-02-04 03:14:07 +01:00
[hydrawise] Various Fixes (#17345)
* Workaround for a bad response from the Hydrawise API Signed-off-by: Dan Cunningham <dan@digitaldan.com> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
91e95268e6
commit
3c01ededa1
@ -23,6 +23,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
public class HydrawiseConnectionException extends Exception {
|
public class HydrawiseConnectionException extends Exception {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private int code = 0;
|
||||||
|
private String response = "";
|
||||||
|
|
||||||
public HydrawiseConnectionException(Exception e) {
|
public HydrawiseConnectionException(Exception e) {
|
||||||
super(e);
|
super(e);
|
||||||
}
|
}
|
||||||
@ -30,4 +33,22 @@ public class HydrawiseConnectionException extends Exception {
|
|||||||
public HydrawiseConnectionException(String message) {
|
public HydrawiseConnectionException(String message) {
|
||||||
super(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.HydrawiseConnectionException;
|
||||||
import org.openhab.binding.hydrawise.internal.api.graphql.dto.ControllerStatus;
|
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.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.Mutation;
|
||||||
import org.openhab.binding.hydrawise.internal.api.graphql.dto.MutationResponse;
|
import org.openhab.binding.hydrawise.internal.api.graphql.dto.MutationResponse;
|
||||||
import org.openhab.binding.hydrawise.internal.api.graphql.dto.MutationResponse.MutationResponseStatus;
|
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(ZoneRun.class, new ResponseDeserializer<ZoneRun>())
|
||||||
.registerTypeAdapter(Forecast.class, new ResponseDeserializer<Forecast>())
|
.registerTypeAdapter(Forecast.class, new ResponseDeserializer<Forecast>())
|
||||||
.registerTypeAdapter(Sensor.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 GRAPH_URL = "https://app.hydrawise.com/api/v2/graph";
|
||||||
private static final String MUTATION_START_ZONE = "startZone(zoneId: %d) { status }";
|
private static final String MUTATION_START_ZONE = "startZone(zoneId: %d) { status }";
|
||||||
@ -94,6 +96,7 @@ public class HydrawiseGraphQLClient {
|
|||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
private final OAuthClientService oAuthService;
|
private final OAuthClientService oAuthService;
|
||||||
private String queryString = "";
|
private String queryString = "";
|
||||||
|
private String weatherString = "";
|
||||||
|
|
||||||
public HydrawiseGraphQLClient(HttpClient httpClient, OAuthClientService oAuthService) {
|
public HydrawiseGraphQLClient(HttpClient httpClient, OAuthClientService oAuthService) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
@ -110,19 +113,48 @@ public class HydrawiseGraphQLClient {
|
|||||||
public @Nullable QueryResponse queryControllers()
|
public @Nullable QueryResponse queryControllers()
|
||||||
throws HydrawiseConnectionException, HydrawiseAuthenticationException {
|
throws HydrawiseConnectionException, HydrawiseAuthenticationException {
|
||||||
try {
|
try {
|
||||||
QueryRequest query = new QueryRequest(getQueryString());
|
return 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);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new HydrawiseConnectionException(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
|
* Stops a given relay
|
||||||
*
|
*
|
||||||
@ -313,7 +345,8 @@ public class HydrawiseGraphQLClient {
|
|||||||
int statusCode = response.getStatus();
|
int statusCode = response.getStatus();
|
||||||
if (!HttpStatus.isSuccess(statusCode)) {
|
if (!HttpStatus.isSuccess(statusCode)) {
|
||||||
throw new HydrawiseConnectionException(
|
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;
|
return stringResponse;
|
||||||
} catch (InterruptedException | TimeoutException | OAuthException | IOException e) {
|
} catch (InterruptedException | TimeoutException | OAuthException | IOException e) {
|
||||||
@ -338,15 +371,25 @@ public class HydrawiseGraphQLClient {
|
|||||||
|
|
||||||
private String getQueryString() throws IOException {
|
private String getQueryString() throws IOException {
|
||||||
if (queryString.isBlank()) {
|
if (queryString.isBlank()) {
|
||||||
try (InputStream inputStream = HydrawiseGraphQLClient.class.getClassLoader()
|
queryString = getResourceString("query.graphql");
|
||||||
.getResourceAsStream("query.graphql");
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
|
|
||||||
queryString = bufferedReader.lines().collect(Collectors.joining("\n"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return queryString;
|
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> {
|
class ResponseDeserializer<T> implements JsonDeserializer<T> {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -22,8 +22,8 @@ public class Controller {
|
|||||||
public Integer id;
|
public Integer id;
|
||||||
public String name;
|
public String name;
|
||||||
public ControllerStatus status;
|
public ControllerStatus status;
|
||||||
|
public Hardware hardware;
|
||||||
public Location location;
|
public Location location;
|
||||||
public List<Zone> zones = null;
|
public List<Zone> zones = null;
|
||||||
public List<Sensor> sensors = null;
|
public List<Sensor> sensors = null;
|
||||||
public List<Forecast> forecast = 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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -12,22 +12,23 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.hydrawise.internal.discovery;
|
package org.openhab.binding.hydrawise.internal.discovery;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
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.HydrawiseBindingConstants;
|
||||||
import org.openhab.binding.hydrawise.internal.HydrawiseControllerListener;
|
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.Controller;
|
||||||
import org.openhab.binding.hydrawise.internal.api.graphql.dto.Customer;
|
import org.openhab.binding.hydrawise.internal.api.graphql.dto.Customer;
|
||||||
import org.openhab.binding.hydrawise.internal.handler.HydrawiseAccountHandler;
|
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.config.discovery.DiscoveryResultBuilder;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
import org.osgi.service.component.annotations.Component;
|
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
|
@NonNullByDefault
|
||||||
@Component(scope = ServiceScope.PROTOTYPE, service = ThingHandlerService.class)
|
@Component(service = ThingHandlerService.class)
|
||||||
public class HydrawiseCloudControllerDiscoveryService
|
public class HydrawiseCloudControllerDiscoveryService extends AbstractDiscoveryService
|
||||||
extends AbstractThingHandlerDiscoveryService<HydrawiseAccountHandler> implements HydrawiseControllerListener {
|
implements HydrawiseControllerListener, ThingHandlerService {
|
||||||
|
|
||||||
private static final int TIMEOUT = 5;
|
private static final int TIMEOUT = 5;
|
||||||
|
@Nullable
|
||||||
|
HydrawiseAccountHandler handler;
|
||||||
|
|
||||||
public HydrawiseCloudControllerDiscoveryService() {
|
public HydrawiseCloudControllerDiscoveryService() {
|
||||||
super(HydrawiseAccountHandler.class, Set.of(HydrawiseBindingConstants.THING_TYPE_CONTROLLER), TIMEOUT, true);
|
super(Set.of(HydrawiseBindingConstants.THING_TYPE_CONTROLLER), TIMEOUT, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startScan() {
|
protected void startScan() {
|
||||||
Customer data = thingHandler.lastData();
|
HydrawiseAccountHandler localHandler = this.handler;
|
||||||
if (data != null) {
|
if (localHandler != null) {
|
||||||
data.controllers.forEach(controller -> addDiscoveryResults(controller));
|
Customer data = localHandler.lastData();
|
||||||
|
if (data != null) {
|
||||||
|
data.controllers.forEach(controller -> addDiscoveryResults(controller));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void deactivate() {
|
||||||
super.dispose();
|
HydrawiseAccountHandler localHandler = this.handler;
|
||||||
removeOlderResults(Instant.now().toEpochMilli(), thingHandler.getThing().getUID());
|
if (localHandler != null) {
|
||||||
|
removeOlderResults(new Date().getTime(), localHandler.getThing().getUID());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected synchronized void stopScan() {
|
protected synchronized void stopScan() {
|
||||||
super.stopScan();
|
super.stopScan();
|
||||||
removeOlderResults(getTimestampOfLastScan(), thingHandler.getThing().getUID());
|
HydrawiseAccountHandler localHandler = this.handler;
|
||||||
|
if (localHandler != null) {
|
||||||
|
removeOlderResults(getTimestampOfLastScan(), localHandler.getThing().getUID());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -71,19 +83,27 @@ public class HydrawiseCloudControllerDiscoveryService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void setThingHandler(ThingHandler handler) {
|
||||||
thingHandler.addControllerListeners(this);
|
this.handler = (HydrawiseAccountHandler) handler;
|
||||||
super.initialize();
|
this.handler.addControllerListeners(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ThingHandler getThingHandler() {
|
||||||
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDiscoveryResults(Controller controller) {
|
private void addDiscoveryResults(Controller controller) {
|
||||||
String label = String.format("Hydrawise Controller %s", controller.name);
|
HydrawiseAccountHandler localHandler = this.handler;
|
||||||
int id = controller.id;
|
if (localHandler != null) {
|
||||||
ThingUID bridgeUID = thingHandler.getThing().getUID();
|
String label = String.format("Hydrawise Controller %s", controller.name);
|
||||||
ThingUID thingUID = new ThingUID(HydrawiseBindingConstants.THING_TYPE_CONTROLLER, bridgeUID,
|
int id = controller.id;
|
||||||
String.valueOf(id));
|
ThingUID bridgeUID = localHandler.getThing().getUID();
|
||||||
thingDiscovered(DiscoveryResultBuilder.create(thingUID).withLabel(label).withBridge(bridgeUID)
|
ThingUID thingUID = new ThingUID(HydrawiseBindingConstants.THING_TYPE_CONTROLLER, bridgeUID,
|
||||||
.withProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID, id)
|
String.valueOf(id));
|
||||||
.withRepresentationProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID).build());
|
thingDiscovered(DiscoveryResultBuilder.create(thingUID).withLabel(label).withBridge(bridgeUID)
|
||||||
|
.withProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID, id)
|
||||||
|
.withRepresentationProperty(HydrawiseBindingConstants.CONFIG_CONTROLLER_ID).build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ public class HydrawiseAccountHandler extends BaseBridgeHandler implements Access
|
|||||||
*/
|
*/
|
||||||
private static final int MIN_REFRESH_SECONDS = 30;
|
private static final int MIN_REFRESH_SECONDS = 30;
|
||||||
private static final int TOKEN_REFRESH_SECONDS = 60;
|
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 BASE_URL = "https://app.hydrawise.com/api/v2/";
|
||||||
private static final String AUTH_URL = BASE_URL + "oauth/access-token";
|
private static final String AUTH_URL = BASE_URL + "oauth/access-token";
|
||||||
private static final String CLIENT_SECRET = "zn3CrjglwNV1";
|
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<?> pollFuture;
|
||||||
private @Nullable ScheduledFuture<?> tokenFuture;
|
private @Nullable ScheduledFuture<?> tokenFuture;
|
||||||
private @Nullable Customer lastData;
|
private @Nullable Customer lastData;
|
||||||
|
private long lastWeatherUpdate;
|
||||||
private int refresh;
|
private int refresh;
|
||||||
|
|
||||||
public HydrawiseAccountHandler(final Bridge bridge, final HttpClient httpClient, final OAuthFactory oAuthFactory) {
|
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) {
|
private void poll(boolean retry) {
|
||||||
|
HydrawiseGraphQLClient apiClient = this.apiClient;
|
||||||
|
if (apiClient == null) {
|
||||||
|
logger.debug("apiclient not initalized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
QueryResponse response = apiClient.queryControllers();
|
QueryResponse response = apiClient.queryControllers();
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
@ -240,6 +247,21 @@ public class HydrawiseAccountHandler extends BaseBridgeHandler implements Access
|
|||||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||||
updateStatus(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;
|
lastData = response.data.me;
|
||||||
synchronized (controllerListeners) {
|
synchronized (controllerListeners) {
|
||||||
controllerListeners.forEach(listener -> {
|
controllerListeners.forEach(listener -> {
|
||||||
|
@ -27,6 +27,8 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import javax.measure.quantity.Speed;
|
||||||
|
import javax.measure.quantity.Temperature;
|
||||||
import javax.measure.quantity.Volume;
|
import javax.measure.quantity.Volume;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
@ -250,7 +252,7 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
|
|||||||
updateForecast(controller.location.forecast);
|
updateForecast(controller.location.forecast);
|
||||||
}
|
}
|
||||||
if (controller.zones != null) {
|
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
|
// 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);
|
: UnDefType.NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateZones(List<Zone> zones) {
|
private void updateZones(List<Zone> zones, int maxZones) {
|
||||||
AtomicReference<Boolean> anyRunning = new AtomicReference<>(false);
|
AtomicReference<Boolean> anyRunning = new AtomicReference<>(false);
|
||||||
AtomicReference<Boolean> anySuspended = new AtomicReference<>(false);
|
AtomicReference<Boolean> anySuspended = new AtomicReference<>(false);
|
||||||
for (Zone zone : zones) {
|
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
|
// 10 for expander 0, relay 10 = zone10
|
||||||
// 101 for expander 1, relay 1 = zone13
|
// 101 for expander 1, relay 1 = zone13
|
||||||
// 212 for expander 2, relay 12 = zone36
|
// 212 for expander 2, relay 12 = zone36
|
||||||
// division of integers in Java give whole numbers, not remainders FYI
|
// 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;
|
String group = "zone" + zoneNumber;
|
||||||
zoneMaps.put(group, zone);
|
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_NAME, new StringType(zone.name));
|
||||||
updateGroupState(group, CHANNEL_ZONE_ICON, new StringType(BASE_IMAGE_URL + zone.icon.fileName));
|
updateGroupState(group, CHANNEL_ZONE_ICON, new StringType(BASE_IMAGE_URL + zone.icon.fileName));
|
||||||
if (zone.scheduledRuns != null) {
|
if (zone.scheduledRuns != null) {
|
||||||
@ -328,8 +332,9 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
|
|||||||
updateGroupState(group, CHANNEL_ZONE_SUSPENDUNTIL, UnDefType.UNDEF);
|
updateGroupState(group, CHANNEL_ZONE_SUSPENDUNTIL, UnDefType.UNDEF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_RUN, OnOffType.from(anyRunning.get()));
|
updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_RUN, anyRunning.get() ? OnOffType.ON : OnOffType.OFF);
|
||||||
updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_SUSPEND, OnOffType.from(anySuspended.get()));
|
updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_SUSPEND,
|
||||||
|
anySuspended.get() ? OnOffType.ON : OnOffType.OFF);
|
||||||
updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_SUSPENDUNTIL, UnDefType.UNDEF);
|
updateGroupState(CHANNEL_GROUP_ALLZONES, CHANNEL_ZONE_SUSPENDUNTIL, UnDefType.UNDEF);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,6 +367,7 @@ public class HydrawiseControllerHandler extends BaseThingHandler implements Hydr
|
|||||||
int i = 1;
|
int i = 1;
|
||||||
for (Forecast forecast : forecasts) {
|
for (Forecast forecast : forecasts) {
|
||||||
String group = "forecast" + (i++);
|
String group = "forecast" + (i++);
|
||||||
|
logger.trace("Updating {} {}", group, forecast.time);
|
||||||
updateGroupState(group, CHANNEL_FORECAST_TIME, stringToDateTime(forecast.time));
|
updateGroupState(group, CHANNEL_FORECAST_TIME, stringToDateTime(forecast.time));
|
||||||
updateGroupState(group, CHANNEL_FORECAST_CONDITIONS, new StringType(forecast.conditions));
|
updateGroupState(group, CHANNEL_FORECAST_CONDITIONS, new StringType(forecast.conditions));
|
||||||
updateGroupState(group, CHANNEL_FORECAST_HUMIDITY, new DecimalType(forecast.averageHumidity.intValue()));
|
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) {
|
private void updateTemperature(UnitValue temperature, String group, String channel) {
|
||||||
logger.debug("TEMP {} {} {} {}", group, channel, temperature.unit, temperature.value);
|
logger.debug("TEMP {} {} {} {}", group, channel, temperature.unit, temperature.value);
|
||||||
updateGroupState(group, channel, new QuantityType<>(temperature.value,
|
updateGroupState(group, channel, new QuantityType<Temperature>(temperature.value,
|
||||||
"\\u00b0F".equals(temperature.unit) ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
|
temperature.unit.indexOf("F") >= 0 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateWindspeed(UnitValue wind, String group, String channel) {
|
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));
|
"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) {
|
private QuantityType<Volume> waterFlowToQuantityType(Number flow, String units) {
|
||||||
double waterFlow = flow.doubleValue();
|
return new QuantityType<>(flow.doubleValue(),
|
||||||
if ("gals".equals(units)) {
|
"gal".equals(units) ? ImperialUnits.GALLON_LIQUID_US : Units.LITRE);
|
||||||
waterFlow = waterFlow * 3.785;
|
|
||||||
}
|
|
||||||
return new QuantityType<>(waterFlow, Units.LITRE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,8 @@ public class HydrawiseLocalHandler extends BaseThingHandler {
|
|||||||
updateGroupState(group, CHANNEL_ZONE_TIME_LEFT, new QuantityType<>(0, Units.SECOND));
|
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@
|
|||||||
<label>Hydrawise Controller Thing</label>
|
<label>Hydrawise Controller Thing</label>
|
||||||
<description>Hydrawise connected irrigation controller</description>
|
<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. -->
|
Once this is fixed we can dynamically add them to the thing and not list them here. -->
|
||||||
<channel-groups>
|
<channel-groups>
|
||||||
|
|
||||||
@ -230,6 +231,78 @@
|
|||||||
<label>Zone 36</label>
|
<label>Zone 36</label>
|
||||||
<description>Sprinkler Zone 36</description>
|
<description>Sprinkler Zone 36</description>
|
||||||
</channel-group>
|
</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>
|
</channel-groups>
|
||||||
<config-description>
|
<config-description>
|
||||||
<parameter name="controllerId" type="integer" required="true">
|
<parameter name="controllerId" type="integer" required="true">
|
||||||
|
@ -5,41 +5,26 @@
|
|||||||
controllers {
|
controllers {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
status {
|
status {
|
||||||
summary
|
summary
|
||||||
online
|
online
|
||||||
lastContact {
|
lastContact {
|
||||||
timestamp
|
timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hardware {
|
||||||
|
version
|
||||||
|
model {
|
||||||
|
maxZones
|
||||||
|
name
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
location {
|
location {
|
||||||
coordinates {
|
coordinates {
|
||||||
latitude
|
latitude
|
||||||
longitude
|
longitude
|
||||||
}
|
}
|
||||||
forecast(days: 3) {
|
|
||||||
time
|
|
||||||
updateTime
|
|
||||||
conditions
|
|
||||||
averageWindSpeed {
|
|
||||||
value
|
|
||||||
unit
|
|
||||||
}
|
|
||||||
highTemperature {
|
|
||||||
value
|
|
||||||
unit
|
|
||||||
}
|
|
||||||
lowTemperature {
|
|
||||||
value
|
|
||||||
unit
|
|
||||||
}
|
|
||||||
probabilityOfPrecipitation
|
|
||||||
precipitation {
|
|
||||||
value
|
|
||||||
unit
|
|
||||||
}
|
|
||||||
averageHumidity
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
zones {
|
zones {
|
||||||
id
|
id
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user