]> git.basschouten.com Git - openhab-addons.git/blob
c964e473a8b349c8d11306f014230b6cb17699db
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.automower.internal.bridge;
14
15 import static org.openhab.binding.automower.internal.AutomowerBindingConstants.THING_TYPE_BRIDGE;
16
17 import java.util.Optional;
18 import java.util.Set;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.eclipse.jetty.client.HttpClient;
25 import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult;
26 import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException;
27 import org.openhab.core.auth.client.oauth2.OAuthClientService;
28 import org.openhab.core.auth.client.oauth2.OAuthFactory;
29 import org.openhab.core.thing.Bridge;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.thing.binding.BaseBridgeHandler;
35 import org.openhab.core.types.Command;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * The {@link AutomowerBridgeHandler} is responsible for handling commands, which are
41  * sent to one of the channels.
42  *
43  * @author Markus Pfleger - Initial contribution
44  */
45 @NonNullByDefault
46 public class AutomowerBridgeHandler extends BaseBridgeHandler {
47     private final Logger logger = LoggerFactory.getLogger(AutomowerBridgeHandler.class);
48
49     private static final String HUSQVARNA_API_TOKEN_URL = "https://api.authentication.husqvarnagroup.dev/v1/oauth2/token";
50
51     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BRIDGE);
52     private static final long DEFAULT_POLLING_INTERVAL_S = TimeUnit.HOURS.toSeconds(1);
53
54     private final OAuthFactory oAuthFactory;
55
56     private @Nullable OAuthClientService oAuthService;
57     private @Nullable ScheduledFuture<?> automowerBridgePollingJob;
58     private @Nullable AutomowerBridge bridge;
59     private final HttpClient httpClient;
60
61     public AutomowerBridgeHandler(Bridge bridge, OAuthFactory oAuthFactory, HttpClient httpClient) {
62         super(bridge);
63         this.oAuthFactory = oAuthFactory;
64         this.httpClient = httpClient;
65     }
66
67     private void pollAutomowers(AutomowerBridge bridge) {
68         MowerListResult automowers;
69         try {
70             automowers = bridge.getAutomowers();
71             updateStatus(ThingStatus.ONLINE);
72             logger.debug("Found {} automowers", automowers.getData().size());
73         } catch (AutomowerCommunicationException e) {
74             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
75                     "@text/comm-error-query-mowers-failed");
76             logger.warn("Unable to fetch automowers: {}", e.getMessage());
77         }
78     }
79
80     @Override
81     public void dispose() {
82         AutomowerBridge currentBridge = bridge;
83         if (currentBridge != null) {
84             stopAutomowerBridgePolling(currentBridge);
85             bridge = null;
86         }
87         OAuthClientService oAuthService = this.oAuthService;
88         if (oAuthService != null) {
89             oAuthFactory.ungetOAuthService(thing.getUID().getAsString());
90             this.oAuthService = null;
91         }
92     }
93
94     @Override
95     public void initialize() {
96         AutomowerBridgeConfiguration bridgeConfiguration = getConfigAs(AutomowerBridgeConfiguration.class);
97
98         final String appKey = bridgeConfiguration.getAppKey();
99         final String appSecret = bridgeConfiguration.getAppSecret();
100         final Integer pollingIntervalS = bridgeConfiguration.getPollingInterval();
101
102         if (appKey == null || appKey.isEmpty()) {
103             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/conf-error-no-app-key");
104         } else if (appSecret == null || appSecret.isEmpty()) {
105             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/conf-error-no-app-secret");
106         } else if (pollingIntervalS != null && pollingIntervalS < 1) {
107             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
108                     "@text/conf-error-invalid-polling-interval");
109         } else {
110             OAuthClientService oAuthService = oAuthFactory.createOAuthClientService(thing.getUID().getAsString(),
111                     HUSQVARNA_API_TOKEN_URL, null, appKey, appSecret, null, null);
112             this.oAuthService = oAuthService;
113
114             if (bridge == null) {
115                 AutomowerBridge currentBridge = new AutomowerBridge(oAuthService, appKey, httpClient, scheduler);
116                 bridge = currentBridge;
117                 startAutomowerBridgePolling(currentBridge, pollingIntervalS);
118             }
119             updateStatus(ThingStatus.UNKNOWN);
120         }
121     }
122
123     @Override
124     public void handleRemoval() {
125         oAuthFactory.deleteServiceAndAccessToken(thing.getUID().getAsString());
126         super.handleRemoval();
127     }
128
129     private void startAutomowerBridgePolling(AutomowerBridge bridge, @Nullable Integer pollingIntervalS) {
130         ScheduledFuture<?> currentPollingJob = automowerBridgePollingJob;
131         if (currentPollingJob == null) {
132             final long pollingIntervalToUse = pollingIntervalS == null ? DEFAULT_POLLING_INTERVAL_S : pollingIntervalS;
133             automowerBridgePollingJob = scheduler.scheduleWithFixedDelay(() -> pollAutomowers(bridge), 1,
134                     pollingIntervalToUse, TimeUnit.SECONDS);
135         }
136     }
137
138     private void stopAutomowerBridgePolling(AutomowerBridge bridge) {
139         ScheduledFuture<?> currentPollingJob = automowerBridgePollingJob;
140         if (currentPollingJob != null) {
141             currentPollingJob.cancel(true);
142             automowerBridgePollingJob = null;
143         }
144     }
145
146     @Override
147     public void handleCommand(ChannelUID channelUID, Command command) {
148     }
149
150     public @Nullable AutomowerBridge getAutomowerBridge() {
151         return bridge;
152     }
153
154     public Optional<MowerListResult> getAutomowers() {
155         AutomowerBridge currentBridge = bridge;
156         if (currentBridge == null) {
157             return Optional.empty();
158         }
159
160         try {
161             return Optional.of(currentBridge.getAutomowers());
162         } catch (AutomowerCommunicationException e) {
163             logger.debug("Bridge cannot get list of available automowers {}", e.getMessage());
164             return Optional.empty();
165         }
166     }
167 }