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