]> git.basschouten.com Git - openhab-addons.git/blob
199e0569246e90b50305791ad05115ab59e5bbac
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.gardena.internal.handler;
14
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.concurrent.TimeUnit;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.gardena.internal.GardenaSmart;
22 import org.openhab.binding.gardena.internal.GardenaSmartEventListener;
23 import org.openhab.binding.gardena.internal.GardenaSmartImpl;
24 import org.openhab.binding.gardena.internal.config.GardenaConfig;
25 import org.openhab.binding.gardena.internal.discovery.GardenaDeviceDiscoveryService;
26 import org.openhab.binding.gardena.internal.exception.GardenaException;
27 import org.openhab.binding.gardena.internal.model.dto.Device;
28 import org.openhab.binding.gardena.internal.util.UidUtils;
29 import org.openhab.core.io.net.http.HttpClientFactory;
30 import org.openhab.core.io.net.http.WebSocketFactory;
31 import org.openhab.core.thing.Bridge;
32 import org.openhab.core.thing.Channel;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.BaseBridgeHandler;
39 import org.openhab.core.thing.binding.ThingHandlerService;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * The {@link GardenaAccountHandler} is the handler for a Gardena smart system access and connects it to the framework.
47  *
48  * @author Gerhard Riegler - Initial contribution
49  */
50 @NonNullByDefault
51 public class GardenaAccountHandler extends BaseBridgeHandler implements GardenaSmartEventListener {
52     private final Logger logger = LoggerFactory.getLogger(GardenaAccountHandler.class);
53     private static final long REINITIALIZE_DELAY_SECONDS = 120;
54     private static final long REINITIALIZE_DELAY_HOURS_LIMIT_EXCEEDED = 24;
55
56     private @Nullable GardenaDeviceDiscoveryService discoveryService;
57
58     private @Nullable GardenaSmart gardenaSmart;
59     private HttpClientFactory httpClientFactory;
60     private WebSocketFactory webSocketFactory;
61
62     public GardenaAccountHandler(Bridge bridge, HttpClientFactory httpClientFactory,
63             WebSocketFactory webSocketFactory) {
64         super(bridge);
65         this.httpClientFactory = httpClientFactory;
66         this.webSocketFactory = webSocketFactory;
67     }
68
69     @Override
70     public void initialize() {
71         logger.debug("Initializing Gardena account '{}'", getThing().getUID().getId());
72         initializeGardena();
73     }
74
75     public void setDiscoveryService(GardenaDeviceDiscoveryService discoveryService) {
76         this.discoveryService = discoveryService;
77     }
78
79     /**
80      * Initializes the GardenaSmart account.
81      */
82     private void initializeGardena() {
83         final GardenaAccountHandler instance = this;
84         scheduler.execute(() -> {
85             try {
86                 GardenaConfig gardenaConfig = getThing().getConfiguration().as(GardenaConfig.class);
87                 logger.debug("{}", gardenaConfig);
88
89                 String id = getThing().getUID().getId();
90                 gardenaSmart = new GardenaSmartImpl(id, gardenaConfig, instance, scheduler, httpClientFactory,
91                         webSocketFactory);
92                 final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
93                 if (discoveryService != null) {
94                     discoveryService.startScan(null);
95                     discoveryService.waitForScanFinishing();
96                 }
97                 updateStatus(ThingStatus.ONLINE);
98             } catch (GardenaException ex) {
99                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
100                 disposeGardena();
101                 if (ex.getStatus() == 429) {
102                     // if there was an error 429 (Too Many Requests), wait for 24 hours before trying again
103                     scheduleReinitialize(REINITIALIZE_DELAY_HOURS_LIMIT_EXCEEDED, TimeUnit.HOURS);
104                 } else {
105                     // otherwise reinitialize after 120 seconds
106                     scheduleReinitialize(REINITIALIZE_DELAY_SECONDS, TimeUnit.SECONDS);
107                 }
108                 logger.warn("{}", ex.getMessage());
109             }
110         });
111     }
112
113     /**
114      * Schedules a reinitialization, if Gardena smart system account is not reachable.
115      */
116     private void scheduleReinitialize(long delay, TimeUnit unit) {
117         scheduler.schedule(() -> {
118             if (getThing().getStatus() != ThingStatus.UNINITIALIZED) {
119                 initializeGardena();
120             }
121         }, delay, unit);
122     }
123
124     @Override
125     public void dispose() {
126         super.dispose();
127         disposeGardena();
128     }
129
130     /**
131      * Disposes the GardenaSmart account.
132      */
133     private void disposeGardena() {
134         logger.debug("Disposing Gardena account '{}'", getThing().getUID().getId());
135         final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
136         if (discoveryService != null) {
137             discoveryService.stopScan();
138         }
139         final GardenaSmart gardenaSmart = this.gardenaSmart;
140         if (gardenaSmart != null) {
141             gardenaSmart.dispose();
142         }
143     }
144
145     /**
146      * Returns the Gardena smart system implementation.
147      */
148     public @Nullable GardenaSmart getGardenaSmart() {
149         return gardenaSmart;
150     }
151
152     @Override
153     public Collection<Class<? extends ThingHandlerService>> getServices() {
154         return Collections.singleton(GardenaDeviceDiscoveryService.class);
155     }
156
157     @Override
158     public void handleCommand(ChannelUID channelUID, Command command) {
159         if (RefreshType.REFRESH == command) {
160             logger.debug("Refreshing Gardena account '{}'", getThing().getUID().getId());
161             disposeGardena();
162             initializeGardena();
163         }
164     }
165
166     @Override
167     public void onDeviceUpdated(Device device) {
168         for (ThingUID thingUID : UidUtils.getThingUIDs(device, getThing())) {
169             final Thing gardenaThing;
170             final GardenaThingHandler gardenaThingHandler;
171             if ((gardenaThing = getThing().getThing(thingUID)) != null
172                     && (gardenaThingHandler = (GardenaThingHandler) gardenaThing.getHandler()) != null) {
173                 try {
174                     gardenaThingHandler.updateProperties(device);
175                     for (Channel channel : gardenaThing.getChannels()) {
176                         gardenaThingHandler.updateChannel(channel.getUID());
177                     }
178                     gardenaThingHandler.updateStatus(device);
179                 } catch (GardenaException ex) {
180                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
181                 } catch (AccountHandlerNotAvailableException ignore) {
182                 }
183             }
184         }
185     }
186
187     @Override
188     public void onNewDevice(Device device) {
189         final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
190         if (discoveryService != null) {
191             discoveryService.deviceDiscovered(device);
192         }
193         onDeviceUpdated(device);
194     }
195
196     @Override
197     public void onError() {
198         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection lost");
199         disposeGardena();
200         scheduleReinitialize(REINITIALIZE_DELAY_SECONDS, TimeUnit.SECONDS);
201     }
202 }