]> git.basschouten.com Git - openhab-addons.git/blob
7ce10106d5e4de6a3276837f8f8a2041a393a601
[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.icloud.internal.handler;
14
15 import static org.openhab.binding.icloud.internal.ICloudBindingConstants.*;
16 import static org.openhab.core.thing.ThingStatus.OFFLINE;
17 import static org.openhab.core.thing.ThingStatus.ONLINE;
18 import static org.openhab.core.thing.ThingStatusDetail.*;
19
20 import java.io.IOException;
21 import java.time.ZoneId;
22 import java.time.ZonedDateTime;
23 import java.util.Date;
24 import java.util.List;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.icloud.internal.ICloudDeviceInformationListener;
29 import org.openhab.binding.icloud.internal.configuration.ICloudDeviceThingConfiguration;
30 import org.openhab.binding.icloud.internal.handler.dto.json.response.ICloudDeviceInformation;
31 import org.openhab.core.library.types.DateTimeType;
32 import org.openhab.core.library.types.DecimalType;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.library.types.PointType;
35 import org.openhab.core.library.types.StringType;
36 import org.openhab.core.thing.Bridge;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.Thing;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.binding.BaseThingHandler;
42 import org.openhab.core.thing.binding.ThingHandler;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.RefreshType;
45 import org.openhab.core.types.State;
46 import org.openhab.core.types.UnDefType;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /**
51  * Handles updates of an iCloud device Thing.
52  *
53  * @author Patrik Gfeller - Initial contribution
54  * @author Hans-Jörg Merk - Helped with testing and feedback
55  * @author Gaël L'hopital - Added low battery
56  * @author Simon Spielmann - Rework for new iCloud API
57  *
58  */
59 @NonNullByDefault
60 public class ICloudDeviceHandler extends BaseThingHandler implements ICloudDeviceInformationListener {
61     private final Logger logger = LoggerFactory.getLogger(ICloudDeviceHandler.class);
62
63     private @Nullable String deviceId;
64
65     public ICloudDeviceHandler(Thing thing) {
66         super(thing);
67     }
68
69     @Override
70     public void deviceInformationUpdate(List<ICloudDeviceInformation> deviceInformationList) {
71         ICloudDeviceInformation deviceInformationRecord = getDeviceInformationRecord(deviceInformationList);
72         if (deviceInformationRecord != null) {
73             if (deviceInformationRecord.getDeviceStatus() == 200) {
74                 updateStatus(ONLINE);
75             } else {
76                 updateStatus(OFFLINE, COMMUNICATION_ERROR, "Reported offline by iCloud webservice");
77             }
78
79             updateState(BATTERY_STATUS, new StringType(deviceInformationRecord.getBatteryStatus()));
80
81             Double batteryLevel = deviceInformationRecord.getBatteryLevel();
82             if (batteryLevel != Double.NaN) {
83                 updateState(BATTERY_LEVEL, new DecimalType(deviceInformationRecord.getBatteryLevel() * 100));
84                 updateState(LOW_BATTERY, batteryLevel < 0.2 ? OnOffType.ON : OnOffType.OFF);
85             }
86
87             if (deviceInformationRecord.getLocation() != null) {
88                 updateLocationRelatedStates(deviceInformationRecord);
89             }
90         } else {
91             updateStatus(OFFLINE, CONFIGURATION_ERROR, "The device is not included in the current account");
92         }
93     }
94
95     @Override
96     public void initialize() {
97         ICloudDeviceThingConfiguration configuration = getConfigAs(ICloudDeviceThingConfiguration.class);
98         this.deviceId = configuration.deviceId;
99
100         Bridge bridge = getBridge();
101         if (bridge != null) {
102             ICloudAccountBridgeHandler handler = (ICloudAccountBridgeHandler) bridge.getHandler();
103             if (handler != null) {
104                 handler.registerListener(this);
105                 if (bridge.getStatus() == ThingStatus.ONLINE) {
106                     handler.refreshData();
107                     updateStatus(ThingStatus.ONLINE);
108                 } else {
109                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
110                 }
111             } else {
112                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
113                         "Bridge handler is not configured");
114             }
115         } else {
116             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Bridge is not configured");
117         }
118     }
119
120     @Override
121     public void handleCommand(ChannelUID channelUID, Command command) {
122         this.logger.trace("Command '{}' received for channel '{}'", command, channelUID);
123
124         Bridge bridge = getBridge();
125         if (bridge == null) {
126             this.logger.debug("No bridge found, ignoring command");
127             return;
128         }
129
130         ICloudAccountBridgeHandler bridgeHandler = (ICloudAccountBridgeHandler) bridge.getHandler();
131         if (bridgeHandler == null) {
132             this.logger.debug("No bridge handler found, ignoring command");
133             return;
134         }
135
136         String channelId = channelUID.getId();
137         if (channelId.equals(FIND_MY_PHONE)) {
138             if (command == OnOffType.ON) {
139                 try {
140                     final String deviceId = this.deviceId;
141                     if (deviceId == null) {
142                         this.logger.debug("Can't send Find My Device request, because deviceId is null!");
143                         return;
144                     }
145                     bridgeHandler.findMyDevice(deviceId);
146                 } catch (IOException | InterruptedException e) {
147                     this.logger.warn("Unable to execute find my device request", e);
148                 }
149                 updateState(FIND_MY_PHONE, OnOffType.OFF);
150             }
151         }
152
153         if (command instanceof RefreshType) {
154             bridgeHandler.refreshData();
155         }
156     }
157
158     @Override
159     public void dispose() {
160         Bridge bridge = getBridge();
161         if (bridge != null) {
162             ThingHandler bridgeHandler = bridge.getHandler();
163             if (bridgeHandler instanceof ICloudAccountBridgeHandler iCloudAccountBridgeHandler) {
164                 iCloudAccountBridgeHandler.unregisterListener(this);
165             }
166         }
167         super.dispose();
168     }
169
170     private void updateLocationRelatedStates(ICloudDeviceInformation deviceInformationRecord) {
171         DecimalType latitude = new DecimalType(deviceInformationRecord.getLocation().getLatitude());
172         DecimalType longitude = new DecimalType(deviceInformationRecord.getLocation().getLongitude());
173         DecimalType altitude = new DecimalType(deviceInformationRecord.getLocation().getAltitude());
174         DecimalType accuracy = new DecimalType(deviceInformationRecord.getLocation().getHorizontalAccuracy());
175
176         PointType location = new PointType(latitude, longitude, altitude);
177
178         updateState(LOCATION, location);
179         updateState(LOCATION_ACCURACY, accuracy);
180         updateState(LOCATION_LASTUPDATE, getLastLocationUpdateDateTimeState(deviceInformationRecord));
181     }
182
183     private @Nullable ICloudDeviceInformation getDeviceInformationRecord(
184             List<ICloudDeviceInformation> deviceInformationList) {
185         this.logger.debug("Device: [{}]", this.deviceId);
186
187         for (ICloudDeviceInformation deviceInformationRecord : deviceInformationList) {
188             String currentId = deviceInformationRecord.getDeviceDiscoveryId();
189             if (currentId == null || currentId.isBlank()) {
190                 logger.debug("deviceDiscoveryId is empty, using device name for identification.");
191                 currentId = deviceInformationRecord.getName();
192             }
193             logger.debug("Current data element: [id = {}]", currentId);
194
195             if (currentId != null && currentId.equals(deviceId)) {
196                 return deviceInformationRecord;
197             }
198         }
199
200         logger.debug("Unable to find device data.");
201         return null;
202     }
203
204     private State getLastLocationUpdateDateTimeState(ICloudDeviceInformation deviceInformationRecord) {
205         State dateTime = UnDefType.UNDEF;
206
207         if (deviceInformationRecord.getLocation().getTimeStamp() > 0) {
208             Date date = new Date(deviceInformationRecord.getLocation().getTimeStamp());
209             ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
210             dateTime = new DateTimeType(zonedDateTime);
211         }
212
213         return dateTime;
214     }
215 }