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