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