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