]> git.basschouten.com Git - openhab-addons.git/blob
284d59e51ea48ab6d1b69831ad4dbf94b4a20bf1
[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.melcloud.internal.handler;
14
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21
22 import org.openhab.binding.melcloud.internal.api.MelCloudConnection;
23 import org.openhab.binding.melcloud.internal.api.json.Device;
24 import org.openhab.binding.melcloud.internal.api.json.DeviceStatus;
25 import org.openhab.binding.melcloud.internal.api.json.HeatpumpDeviceStatus;
26 import org.openhab.binding.melcloud.internal.config.AccountConfig;
27 import org.openhab.binding.melcloud.internal.discovery.MelCloudDiscoveryService;
28 import org.openhab.binding.melcloud.internal.exceptions.MelCloudCommException;
29 import org.openhab.binding.melcloud.internal.exceptions.MelCloudLoginException;
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.ThingUID;
35 import org.openhab.core.thing.binding.BaseBridgeHandler;
36 import org.openhab.core.thing.binding.ThingHandlerService;
37 import org.openhab.core.types.Command;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * {@link MelCloudAccountHandler} is the handler for MELCloud API and connects it
43  * to the webservice.
44  *
45  * @author Luca Calcaterra - Initial contribution
46  * @author Pauli Anttila - Refactoring
47  * @author Wietse van Buitenen - Return all devices, added heatpump device
48  */
49 public class MelCloudAccountHandler extends BaseBridgeHandler {
50     private final Logger logger = LoggerFactory.getLogger(MelCloudAccountHandler.class);
51
52     private MelCloudConnection connection;
53     private List<Device> devices;
54     private ScheduledFuture<?> connectionCheckTask;
55     private AccountConfig config;
56     private boolean loginCredentialError;
57
58     public MelCloudAccountHandler(Bridge bridge) {
59         super(bridge);
60     }
61
62     @Override
63     public Collection<Class<? extends ThingHandlerService>> getServices() {
64         return Collections.singleton(MelCloudDiscoveryService.class);
65     }
66
67     @Override
68     public void initialize() {
69         logger.debug("Initializing MELCloud account handler.");
70         config = getConfigAs(AccountConfig.class);
71         connection = new MelCloudConnection();
72         devices = Collections.emptyList();
73         loginCredentialError = false;
74         startConnectionCheck();
75     }
76
77     @Override
78     public void dispose() {
79         logger.debug("Running dispose()");
80         stopConnectionCheck();
81         connection = null;
82         devices = Collections.emptyList();
83         config = null;
84     }
85
86     @Override
87     public void handleCommand(ChannelUID channelUID, Command command) {
88     }
89
90     public ThingUID getID() {
91         return getThing().getUID();
92     }
93
94     public List<Device> getDeviceList() throws MelCloudCommException, MelCloudLoginException {
95         connectIfNotConnected();
96         return connection.fetchDeviceList();
97     }
98
99     private void connect() throws MelCloudCommException, MelCloudLoginException {
100         if (loginCredentialError) {
101             throw new MelCloudLoginException("Connection to MELCloud can't be opened because of wrong credentials");
102         }
103         logger.debug("Initializing connection to MELCloud");
104         updateStatus(ThingStatus.OFFLINE);
105         try {
106             connection.login(config.username, config.password, config.language);
107             devices = connection.fetchDeviceList();
108             updateStatus(ThingStatus.ONLINE);
109         } catch (MelCloudLoginException e) {
110             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
111             loginCredentialError = true;
112             throw e;
113         } catch (MelCloudCommException e) {
114             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
115             throw e;
116         }
117     }
118
119     private synchronized void connectIfNotConnected() throws MelCloudCommException, MelCloudLoginException {
120         if (!isConnected()) {
121             connect();
122         }
123     }
124
125     public boolean isConnected() {
126         return connection.isConnected();
127     }
128
129     public DeviceStatus sendDeviceStatus(DeviceStatus deviceStatus)
130             throws MelCloudCommException, MelCloudLoginException {
131         connectIfNotConnected();
132         try {
133             return connection.sendDeviceStatus(deviceStatus);
134         } catch (MelCloudCommException e) {
135             logger.debug("Sending failed, retry once with relogin");
136             connect();
137             return connection.sendDeviceStatus(deviceStatus);
138         }
139     }
140
141     public DeviceStatus fetchDeviceStatus(int deviceId, Optional<Integer> buildingId)
142             throws MelCloudCommException, MelCloudLoginException {
143         connectIfNotConnected();
144         int bid = buildingId.orElse(findBuildingId(deviceId));
145
146         try {
147             return connection.fetchDeviceStatus(deviceId, bid);
148         } catch (MelCloudCommException e) {
149             logger.debug("Sending failed, retry once with relogin");
150             connect();
151             return connection.fetchDeviceStatus(deviceId, bid);
152         }
153     }
154
155     public HeatpumpDeviceStatus sendHeatpumpDeviceStatus(HeatpumpDeviceStatus heatpumpDeviceStatus)
156             throws MelCloudCommException, MelCloudLoginException {
157         connectIfNotConnected();
158         try {
159             return connection.sendHeatpumpDeviceStatus(heatpumpDeviceStatus);
160         } catch (MelCloudCommException e) {
161             logger.debug("Sending failed, retry once with relogin");
162             connect();
163             return connection.sendHeatpumpDeviceStatus(heatpumpDeviceStatus);
164         }
165     }
166
167     public HeatpumpDeviceStatus fetchHeatpumpDeviceStatus(int deviceId, Optional<Integer> buildingId)
168             throws MelCloudCommException, MelCloudLoginException {
169         connectIfNotConnected();
170         int bid = buildingId.orElse(findBuildingId(deviceId));
171
172         try {
173             return connection.fetchHeatpumpDeviceStatus(deviceId, bid);
174         } catch (MelCloudCommException e) {
175             logger.debug("Sending failed, retry once with relogin");
176             connect();
177             return connection.fetchHeatpumpDeviceStatus(deviceId, bid);
178         }
179     }
180
181     private int findBuildingId(int deviceId) throws MelCloudCommException {
182         if (devices != null) {
183             return devices.stream().filter(d -> d.getDeviceID() == deviceId).findFirst().orElseThrow(
184                     () -> new MelCloudCommException(String.format("Can't find building id for device id %s", deviceId)))
185                     .getBuildingID();
186         }
187         throw new MelCloudCommException(String.format("Can't find building id for device id %s", deviceId));
188     }
189
190     private void startConnectionCheck() {
191         if (connectionCheckTask == null || connectionCheckTask.isCancelled()) {
192             logger.debug("Start periodic connection check");
193             Runnable runnable = () -> {
194                 logger.debug("Check MELCloud connection");
195                 if (connection.isConnected()) {
196                     logger.debug("Connection to MELCloud open");
197                 } else {
198                     try {
199                         connect();
200                     } catch (MelCloudLoginException e) {
201                         logger.debug("Connection to MELCloud down due to login error, reason: {}.", e.getMessage());
202                     } catch (MelCloudCommException e) {
203                         logger.debug("Connection to MELCloud down, reason: {}.", e.getMessage());
204                     } catch (RuntimeException e) {
205                         logger.warn("Unknown error occurred during connection check, reason: {}.", e.getMessage(), e);
206                     }
207                 }
208             };
209             connectionCheckTask = scheduler.scheduleWithFixedDelay(runnable, 0, 60, TimeUnit.SECONDS);
210         } else {
211             logger.debug("Connection check task already running");
212         }
213     }
214
215     private void stopConnectionCheck() {
216         if (connectionCheckTask != null) {
217             logger.debug("Stop periodic connection check");
218             connectionCheckTask.cancel(true);
219             connectionCheckTask = null;
220         }
221     }
222 }