2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.gardena.internal.handler;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.concurrent.TimeUnit;
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;
47 * The {@link GardenaAccountHandler} is the handler for a Gardena smart system access and connects it to the framework.
49 * @author Gerhard Riegler - Initial contribution
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;
57 private @Nullable GardenaDeviceDiscoveryService discoveryService;
59 private @Nullable GardenaSmart gardenaSmart;
60 private HttpClientFactory httpClientFactory;
61 private WebSocketFactory webSocketFactory;
63 public GardenaAccountHandler(Bridge bridge, HttpClientFactory httpClientFactory,
64 WebSocketFactory webSocketFactory) {
66 this.httpClientFactory = httpClientFactory;
67 this.webSocketFactory = webSocketFactory;
71 public void initialize() {
72 logger.debug("Initializing Gardena account '{}'", getThing().getUID().getId());
76 public void setDiscoveryService(GardenaDeviceDiscoveryService discoveryService) {
77 this.discoveryService = discoveryService;
81 * Initializes the GardenaSmart account.
83 private void initializeGardena() {
84 final GardenaAccountHandler instance = this;
85 scheduler.execute(() -> {
87 GardenaConfig gardenaConfig = getThing().getConfiguration().as(GardenaConfig.class);
88 logger.debug("{}", gardenaConfig);
90 String id = getThing().getUID().getId();
91 gardenaSmart = new GardenaSmartImpl(id, gardenaConfig, instance, scheduler, httpClientFactory,
93 final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
94 if (discoveryService != null) {
95 discoveryService.startScan(null);
96 discoveryService.waitForScanFinishing();
98 updateStatus(ThingStatus.ONLINE);
99 } catch (GardenaException ex) {
100 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
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);
106 // otherwise reinitialize after 120 seconds
107 scheduleReinitialize(REINITIALIZE_DELAY_SECONDS, TimeUnit.SECONDS);
109 logger.warn("{}", ex.getMessage());
115 * Schedules a reinitialization, if Gardena smart system account is not reachable.
117 private void scheduleReinitialize(long delay, TimeUnit unit) {
118 scheduler.schedule(() -> {
119 if (getThing().getStatus() != ThingStatus.UNINITIALIZED) {
126 public void dispose() {
132 * Disposes the GardenaSmart account.
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();
140 final GardenaSmart gardenaSmart = this.gardenaSmart;
141 if (gardenaSmart != null) {
142 gardenaSmart.dispose();
147 * Returns the Gardena smart system implementation.
149 public @Nullable GardenaSmart getGardenaSmart() {
154 public Collection<Class<? extends ThingHandlerService>> getServices() {
155 return Collections.singleton(GardenaDeviceDiscoveryService.class);
159 public void handleCommand(ChannelUID channelUID, Command command) {
160 if (RefreshType.REFRESH == command) {
161 logger.debug("Refreshing Gardena account '{}'", getThing().getUID().getId());
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);
175 final ThingHandler thingHandler = gardenaThing.getHandler();
176 if (!(thingHandler instanceof GardenaThingHandler)) {
177 logger.debug("Handler for thingUID:{} is not a 'GardenaThingHandler' ({})", thingUID, thingHandler);
180 final GardenaThingHandler gardenaThingHandler = (GardenaThingHandler) thingHandler;
182 gardenaThingHandler.updateProperties(device);
183 for (Channel channel : gardenaThing.getChannels()) {
184 gardenaThingHandler.updateChannel(channel.getUID());
186 gardenaThingHandler.updateStatus(device);
187 } catch (GardenaException ex) {
188 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
189 } catch (AccountHandlerNotAvailableException ignore) {
195 public void onNewDevice(Device device) {
196 final GardenaDeviceDiscoveryService discoveryService = this.discoveryService;
197 if (discoveryService != null) {
198 discoveryService.deviceDiscovered(device);
200 onDeviceUpdated(device);
204 public void onError() {
205 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection lost");
207 scheduleReinitialize(REINITIALIZE_DELAY_SECONDS, TimeUnit.SECONDS);