]> git.basschouten.com Git - openhab-addons.git/blob
45c5e20b47037b79bab6666edc82a0bb70b65ae1
[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.wolfsmartset.internal.handler;
14
15 import static org.openhab.binding.wolfsmartset.internal.WolfSmartsetBindingConstants.CONFIG_SYSTEM_ID;
16
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.CopyOnWriteArraySet;
24 import java.util.concurrent.Future;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.atomic.AtomicInteger;
27 import java.util.stream.Collectors;
28
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.eclipse.jetty.client.HttpClient;
32 import org.openhab.binding.wolfsmartset.internal.api.WolfSmartsetApi;
33 import org.openhab.binding.wolfsmartset.internal.api.WolfSmartsetCloudException;
34 import org.openhab.binding.wolfsmartset.internal.config.WolfSmartsetAccountConfiguration;
35 import org.openhab.binding.wolfsmartset.internal.discovery.WolfSmartsetAccountDiscoveryService;
36 import org.openhab.binding.wolfsmartset.internal.dto.GetGuiDescriptionForGatewayDTO;
37 import org.openhab.binding.wolfsmartset.internal.dto.GetSystemListDTO;
38 import org.openhab.core.thing.Bridge;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.thing.binding.BaseBridgeHandler;
44 import org.openhab.core.thing.binding.ThingHandler;
45 import org.openhab.core.thing.binding.ThingHandlerService;
46 import org.openhab.core.types.Command;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /**
51  * The {@link WolfSmartsetAccountBridgeHandler} is responsible for managing
52  * communication with the WolfSmartset API.
53  *
54  * @author Bo Biene - Initial contribution
55  */
56 @NonNullByDefault
57 public class WolfSmartsetAccountBridgeHandler extends BaseBridgeHandler {
58     private static final int REFRESH_STARTUP_DELAY_SECONDS = 3;
59     private static final int REFRESH_INTERVAL_SECONDS = 1;
60     private static final int DEFAULT_REFRESH_INTERVAL_CONFIGURATION_MINUTES = 10;
61     private static final int DEFAULT_REFRESH_INTERVAL_VALUES_SECONDS = 15;
62
63     private final Logger logger = LoggerFactory.getLogger(WolfSmartsetAccountBridgeHandler.class);
64
65     private final HttpClient httpClient;
66
67     private @NonNullByDefault({}) WolfSmartsetApi api;
68     private int refreshIntervalStructureMinutes;
69     private int refreshIntervalValuesSeconds;
70     private boolean discoveryEnabled;
71     private @Nullable List<GetSystemListDTO> cachedSystems = null;
72
73     private final Map<String, WolfSmartsetSystemBridgeHandler> systemHandlers = new ConcurrentHashMap<>();
74     private final Set<String> systemIds = new CopyOnWriteArraySet<>();
75
76     private @Nullable Future<?> refreshSystemsJob;
77     private final AtomicInteger refreshConfigurationCounter = new AtomicInteger(REFRESH_STARTUP_DELAY_SECONDS);
78     private final AtomicInteger refreshValuesCounter = new AtomicInteger(REFRESH_STARTUP_DELAY_SECONDS);
79
80     public WolfSmartsetAccountBridgeHandler(final Bridge bridge, HttpClient httpClient) {
81         super(bridge);
82         this.httpClient = httpClient;
83     }
84
85     @Override
86     public void initialize() {
87         logger.debug("AccountBridge: Initializing");
88
89         WolfSmartsetAccountConfiguration config = getConfigAs(WolfSmartsetAccountConfiguration.class);
90
91         Integer value;
92         value = config.refreshIntervalStructure;
93         refreshIntervalStructureMinutes = value == null ? DEFAULT_REFRESH_INTERVAL_CONFIGURATION_MINUTES : value;
94
95         value = config.refreshIntervalValues;
96         refreshIntervalValuesSeconds = value == null ? DEFAULT_REFRESH_INTERVAL_VALUES_SECONDS : value;
97
98         String username = config.username;
99         String password = config.password;
100         username = username == null ? "" : username;
101         password = password == null ? "" : password;
102
103         Boolean booleanValue = config.discoveryEnabled;
104         discoveryEnabled = booleanValue == null ? false : booleanValue.booleanValue();
105         logger.debug("AccountBridge: System and unit discovery is {}", discoveryEnabled ? "enabled" : "disabled");
106         if (username.trim().isEmpty() || password.trim().isEmpty()) {
107             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing username or password");
108         } else {
109             try {
110                 api = new WolfSmartsetApi(username, password, httpClient, scheduler);
111                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Checking authorization");
112                 scheduleRefreshJob();
113             } catch (WolfSmartsetCloudException e) {
114                 logger.error("unable to create wolf smartset api", e);
115                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
116             }
117         }
118     }
119
120     @Override
121     public void dispose() {
122         cancelRefreshJob();
123         api.stopRequestQueue();
124         logger.debug("AccountBridge: Disposing");
125     }
126
127     @Override
128     public Collection<Class<? extends ThingHandlerService>> getServices() {
129         return Collections.singleton(WolfSmartsetAccountDiscoveryService.class);
130     }
131
132     @Override
133     public void handleCommand(ChannelUID channelUID, Command command) {
134     }
135
136     @Override
137     public void childHandlerInitialized(ThingHandler systemHandler, Thing systemThing) {
138         String systemId = (String) systemThing.getConfiguration().get(CONFIG_SYSTEM_ID);
139         systemHandlers.put(systemId, (WolfSmartsetSystemBridgeHandler) systemHandler);
140         systemIds.add(systemId);
141         scheduleRefreshJob();
142         logger.debug("AccountBridge: Adding system handler for {} with id {}", systemThing.getUID(), systemId);
143     }
144
145     @Override
146     public void childHandlerDisposed(ThingHandler systemHandler, Thing systemThing) {
147         String systemId = (String) systemThing.getConfiguration().get(CONFIG_SYSTEM_ID);
148         systemHandlers.remove(systemId);
149         systemIds.remove(systemId);
150         logger.debug("AccountBridge: Removing system handler for {} with id {}", systemThing.getUID(), systemId);
151     }
152
153     /**
154      * returns truee if BackgroundDiscoveryEnabled
155      */
156     public boolean isBackgroundDiscoveryEnabled() {
157         return discoveryEnabled;
158     }
159
160     /**
161      * returns the list of the GetSystemListDTO available
162      */
163     public @Nullable List<GetSystemListDTO> getRegisteredSystems() {
164         return cachedSystems;
165     }
166
167     /**
168      * force a full update of the wolf smartset cloud configuration
169      */
170     public void scheduleRefreshJob() {
171         logger.debug("AccountBridge: Scheduling system refresh job");
172         cancelRefreshJob();
173         refreshConfigurationCounter.set(0);
174         refreshValuesCounter.set(0);
175         refreshSystemsJob = scheduler.scheduleWithFixedDelay(this::refreshSystems, REFRESH_STARTUP_DELAY_SECONDS,
176                 REFRESH_INTERVAL_SECONDS, TimeUnit.SECONDS);
177     }
178
179     /**
180      * The refresh job updates the system channels on the refresh interval set in the system thing config.
181      * The system update process involves first running a system summary transaction to
182      * determine if any system data has changed since the last summary. If any change is detected,
183      * a full query of the systems is performed.
184      */
185     private void refreshSystems() {
186         if (refreshConfigurationCounter.getAndDecrement() == 0) {
187             refreshConfigurationCounter.set(refreshIntervalStructureMinutes * 60);
188             if (api.login()) {
189                 logger.debug("AccountBridge: refreshing configuration");
190                 updateStatus(ThingStatus.ONLINE);
191                 cachedSystems = api.getSystems();
192                 if (cachedSystems != null) {
193                     for (GetSystemListDTO system : api.getSystems()) {
194                         WolfSmartsetSystemBridgeHandler handler = systemHandlers.get(system.getId().toString());
195                         if (handler != null) {
196                             GetGuiDescriptionForGatewayDTO systemDescription = api.getSystemDescription(system.getId(),
197                                     system.getGatewayId());
198                             handler.updateConfiguration(system, systemDescription);
199                         }
200                     }
201                 }
202             } else {
203                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Authorization failed");
204             }
205         }
206
207         if (refreshValuesCounter.getAndDecrement() == 0) {
208             refreshValuesCounter.set(refreshIntervalValuesSeconds);
209             if (api.login()) {
210                 logger.debug("AccountBridge: refreshing values");
211                 updateStatus(ThingStatus.ONLINE);
212
213                 var systemConfigs = systemHandlers.values().stream().map(s -> s.getSystemConfig())
214                         .filter(s -> s != null).collect(Collectors.toSet());
215                 if (systemConfigs != null && systemConfigs.size() > 0) {
216                     var systemStates = api.getSystemState(systemConfigs);
217                     if (systemStates != null) {
218                         for (var systemState : systemStates) {
219                             if (systemState != null) {
220                                 var systemHandler = systemHandlers.get(systemState.getSystemId().toString());
221                                 if (systemHandler != null) {
222                                     systemHandler.updateSystemState(systemState);
223                                 }
224                             }
225                         }
226                     } else {
227                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
228                                 "Failed to update system states");
229                     }
230
231                     for (var systemHandler : systemHandlers.values()) {
232                         if (systemHandler != null) {
233                             var systemConfig = systemHandler.getSystemConfig();
234                             if (systemConfig != null) {
235                                 var faultMessages = api.getFaultMessages(systemConfig.getId(),
236                                         systemConfig.getGatewayId());
237
238                                 systemHandler.updateFaultMessages(faultMessages);
239
240                                 for (var unitHandler : systemHandler.getUnitHandler()) {
241                                     if (unitHandler != null) {
242                                         var tabmenu = unitHandler.getTabMenu();
243                                         if (tabmenu != null) {
244                                             var lastRefreshTime = unitHandler.getLastRefreshTime();
245                                             var valueIds = tabmenu.parameterDescriptors.stream()
246                                                     .filter(p -> p.valueId > 0).map(p -> p.valueId)
247                                                     .collect(Collectors.toList());
248                                             var paramValues = api.getGetParameterValues(systemConfig.getId(),
249                                                     systemConfig.getGatewayId(), tabmenu.bundleId, valueIds,
250                                                     lastRefreshTime);
251
252                                             unitHandler.updateValues(paramValues);
253                                         }
254                                     }
255                                 }
256                             } else {
257                                 // waiting for config.
258                                 systemHandler.updateSystemState(null);
259                             }
260                         }
261                     }
262                 }
263             } else {
264                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Authorization failed");
265             }
266         }
267     }
268
269     private void cancelRefreshJob() {
270         Future<?> localRefreshSystemsJob = refreshSystemsJob;
271         if (localRefreshSystemsJob != null) {
272             localRefreshSystemsJob.cancel(true);
273             logger.debug("AccountBridge: Canceling system refresh job");
274         }
275     }
276 }