forked from openhab/openhab-addons
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial work at moving to a GraphQL based API for cloud control
Signed-off-by: digitaldan <[email protected]>
- Loading branch information
1 parent
8aa0b30
commit 87d89d5
Showing
68 changed files
with
2,860 additions
and
1,109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 5 additions & 5 deletions
10
bundles/org.openhab.binding.hydrawise/src/main/feature/feature.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<features name="org.openhab.binding.hydrawise-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0"> | ||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository> | ||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository> | ||
|
||
<feature name="openhab-binding-hydrawise" description="Hydrawise Binding" version="${project.version}"> | ||
<feature>openhab-runtime-base</feature> | ||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.hydrawise/${project.version}</bundle> | ||
</feature> | ||
<feature name="openhab-binding-hydrawise" description="Hydrawise Binding" version="${project.version}"> | ||
<feature>openhab-runtime-base</feature> | ||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.hydrawise/${project.version}</bundle> | ||
</feature> | ||
</features> |
34 changes: 34 additions & 0 deletions
34
...e/src/main/java/org/openhab/binding/hydrawise/internal/HydrawiseAccountConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* Copyright (c) 2010-2020 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; | ||
|
||
/** | ||
* The {@link HydrawiseAccountConfiguration} class contains fields mapping thing configuration parameters. | ||
* | ||
* @author Dan Cunningham - Initial contribution | ||
*/ | ||
public class HydrawiseAccountConfiguration { | ||
|
||
public String refreshToken; | ||
|
||
public String userName; | ||
|
||
public String password; | ||
|
||
public Boolean savePassword; | ||
|
||
/** | ||
* refresh interval in seconds. | ||
*/ | ||
public Integer refreshInterval; | ||
} |
167 changes: 167 additions & 0 deletions
167
...drawise/src/main/java/org/openhab/binding/hydrawise/internal/HydrawiseAccountHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package org.openhab.binding.hydrawise.internal; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.concurrent.ScheduledFuture; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.apache.commons.lang.StringUtils; | ||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
import org.eclipse.jdt.annotation.Nullable; | ||
import org.eclipse.jetty.client.HttpClient; | ||
import org.eclipse.smarthome.config.core.Configuration; | ||
import org.eclipse.smarthome.core.thing.Bridge; | ||
import org.eclipse.smarthome.core.thing.ChannelUID; | ||
import org.eclipse.smarthome.core.thing.ThingStatus; | ||
import org.eclipse.smarthome.core.thing.ThingStatusDetail; | ||
import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; | ||
import org.eclipse.smarthome.core.types.Command; | ||
import org.openhab.binding.hydrawise.internal.api.HydrawiseAuthenticationException; | ||
import org.openhab.binding.hydrawise.internal.api.HydrawiseConnectionException; | ||
import org.openhab.binding.hydrawise.internal.api.graphql.HydrawiseAuthTokenProvider; | ||
import org.openhab.binding.hydrawise.internal.api.graphql.HydrawiseGraphQLClient; | ||
import org.openhab.binding.hydrawise.internal.api.graphql.schema.AuthToken; | ||
import org.openhab.binding.hydrawise.internal.api.graphql.schema.Customer; | ||
import org.openhab.binding.hydrawise.internal.api.graphql.schema.QueryResponse; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
@NonNullByDefault | ||
public class HydrawiseAccountHandler extends BaseBridgeHandler implements HydrawiseAuthTokenProvider { | ||
/** | ||
* Minimum amount of time we can poll for updates | ||
*/ | ||
protected static final int MIN_REFRESH_SECONDS = 30; | ||
protected static final int DEFAULT_REFRESH_SECONDS = 60; | ||
private final Logger logger = LoggerFactory.getLogger(HydrawiseAccountHandler.class); | ||
private HydrawiseGraphQLClient apiClient; | ||
private @Nullable AuthToken token; | ||
private @Nullable ScheduledFuture<?> pollFuture; | ||
private int refresh; | ||
private @Nullable Customer lastData; | ||
private final List<HydrawiseControllerListener> controllerListeners = new ArrayList<HydrawiseControllerListener>(); | ||
|
||
public HydrawiseAccountHandler(Bridge bridge, HttpClient httpClient) { | ||
super(bridge); | ||
this.apiClient = new HydrawiseGraphQLClient(httpClient, this); | ||
} | ||
|
||
@Override | ||
public void handleCommand(ChannelUID channelUID, Command command) { | ||
|
||
} | ||
|
||
@Override | ||
public void initialize() { | ||
logger.debug("Handler initialized."); | ||
scheduler.schedule(this::configure, 0, TimeUnit.SECONDS); | ||
} | ||
|
||
@Override | ||
public void dispose() { | ||
logger.debug("Handler disposed."); | ||
clearPolling(); | ||
} | ||
|
||
public void addControllerListeners(HydrawiseControllerListener listener) { | ||
this.controllerListeners.add(listener); | ||
Customer data = lastData; | ||
if (data != null) { | ||
listener.onData(data.controllers); | ||
} | ||
} | ||
|
||
public void removeControllerListeners(HydrawiseControllerListener listener) { | ||
this.controllerListeners.remove(listener); | ||
} | ||
|
||
public @Nullable HydrawiseGraphQLClient graphQLClient() { | ||
return apiClient; | ||
} | ||
|
||
public @Nullable Customer lastData() { | ||
return lastData; | ||
} | ||
|
||
private void configure() { | ||
HydrawiseAccountConfiguration config = getConfig().as(HydrawiseAccountConfiguration.class); | ||
// TODO switch to Java 11 String.isBlank | ||
if (StringUtils.isNotBlank(config.userName) && StringUtils.isNotBlank(config.password)) { | ||
if (!config.savePassword) { | ||
Configuration editedConfig = editConfiguration(); | ||
editedConfig.remove("password"); | ||
updateConfiguration(editedConfig); | ||
} | ||
try { | ||
authTokenUpdated(apiClient.login(config.userName, config.password)); | ||
} catch (HydrawiseConnectionException | HydrawiseAuthenticationException e) { | ||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); | ||
return; | ||
} | ||
} else if (StringUtils.isNotBlank(config.refreshToken)) { | ||
authTokenUpdated(new AuthToken(config.refreshToken)); | ||
} else { | ||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Login credentials required."); | ||
return; | ||
} | ||
this.refresh = Math.max(config.refreshInterval != null ? config.refreshInterval : DEFAULT_REFRESH_SECONDS, | ||
MIN_REFRESH_SECONDS); | ||
initPolling(0, refresh); | ||
} | ||
|
||
/** | ||
* Starts/Restarts polling with an initial delay. This allows changes in the poll cycle for when commands are sent | ||
* and we need to poll sooner then the next refresh cycle. | ||
*/ | ||
private synchronized void initPolling(int initalDelay, int refresh) { | ||
clearPolling(); | ||
pollFuture = scheduler.scheduleWithFixedDelay(this::poll, initalDelay, refresh, TimeUnit.SECONDS); | ||
} | ||
|
||
/** | ||
* Stops/clears this thing's polling future | ||
*/ | ||
private void clearPolling() { | ||
ScheduledFuture<?> localFuture = pollFuture; | ||
if (isFutureValid(localFuture)) { | ||
if (localFuture != null) { | ||
localFuture.cancel(false); | ||
} | ||
} | ||
} | ||
|
||
private boolean isFutureValid(@Nullable ScheduledFuture<?> future) { | ||
return future != null && !future.isCancelled(); | ||
} | ||
|
||
private void poll() { | ||
try { | ||
QueryResponse response = apiClient.queryControllers(); | ||
if (getThing().getStatus() != ThingStatus.ONLINE) { | ||
updateStatus(ThingStatus.ONLINE); | ||
} | ||
lastData = response.data.me; | ||
controllerListeners.forEach(listener -> { | ||
listener.onData(response.data.me.controllers); | ||
}); | ||
} catch (HydrawiseConnectionException e) { | ||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); | ||
} catch (HydrawiseAuthenticationException e) { | ||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); | ||
clearPolling(); | ||
} | ||
} | ||
|
||
@Override | ||
public @Nullable AuthToken getAuthToken() { | ||
return token; | ||
} | ||
|
||
@Override | ||
public void authTokenUpdated(AuthToken token) { | ||
this.token = token; | ||
Configuration editedConfig = editConfiguration(); | ||
editedConfig.put(HydrawiseBindingConstants.CONFIG_REFRESHTOKEN, token.refreshToken); | ||
updateConfiguration(editedConfig); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.