2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.icloud.internal.handler;
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.*;
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;
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;
49 * Handles updates of an iCloud device Thing.
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
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;
62 public ICloudDeviceHandler(Thing thing) {
67 public void deviceInformationUpdate(List<ICloudDeviceInformation> deviceInformationList) {
68 ICloudDeviceInformation deviceInformationRecord = getDeviceInformationRecord(deviceInformationList);
69 if (deviceInformationRecord != null) {
70 if (deviceInformationRecord.getDeviceStatus() == 200 || deviceInformationRecord.getDeviceStatus() == 203) {
73 updateStatus(OFFLINE, COMMUNICATION_ERROR, "Reported offline by iCloud webservice");
76 updateState(BATTERY_STATUS, new StringType(deviceInformationRecord.getBatteryStatus()));
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);
84 if (deviceInformationRecord.getLocation() != null) {
85 updateLocationRelatedStates(deviceInformationRecord);
88 updateStatus(OFFLINE, CONFIGURATION_ERROR, "The device is not included in the current account");
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);
98 ICloudDeviceThingConfiguration configuration = getConfigAs(ICloudDeviceThingConfiguration.class);
99 this.deviceId = configuration.deviceId;
101 ICloudAccountBridgeHandler handler = getIcloudAccount();
102 if (handler != null) {
105 updateStatus(OFFLINE, BRIDGE_UNINITIALIZED, "Bridge not found");
109 private void refreshData() {
110 ICloudAccountBridgeHandler bridge = getIcloudAccount();
111 if (bridge != null) {
112 bridge.refreshData();
117 public void handleCommand(ChannelUID channelUID, Command command) {
118 logger.trace("Command '{}' received for channel '{}'", command, channelUID);
120 ICloudAccountBridgeHandler bridge = getIcloudAccount();
121 if (bridge == null) {
122 logger.debug("No bridge found, ignoring command");
126 String channelId = channelUID.getId();
127 if (channelId.equals(FIND_MY_PHONE)) {
128 if (command == OnOffType.ON) {
130 final String deviceId = this.deviceId;
131 if (deviceId == null) {
132 logger.debug("Can't send Find My Device request, because deviceId is null!");
135 bridge.findMyDevice(deviceId);
136 } catch (IOException e) {
137 logger.warn("Unable to execute find my device request", e);
139 updateState(FIND_MY_PHONE, OnOffType.OFF);
143 if (command instanceof RefreshType) {
144 bridge.refreshData();
149 public void dispose() {
150 ICloudAccountBridgeHandler bridge = getIcloudAccount();
151 if (bridge != null) {
152 bridge.unregisterListener(this);
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());
163 PointType location = new PointType(latitude, longitude, altitude);
165 updateState(LOCATION, location);
166 updateState(LOCATION_ACCURACY, accuracy);
167 updateState(LOCATION_LASTUPDATE, getLastLocationUpdateDateTimeState(deviceInformationRecord));
170 private @Nullable ICloudDeviceInformation getDeviceInformationRecord(
171 List<ICloudDeviceInformation> deviceInformationList) {
172 logger.debug("Device: [{}]", deviceId);
174 for (ICloudDeviceInformation deviceInformationRecord : deviceInformationList) {
175 String currentId = deviceInformationRecord.getId();
177 logger.debug("Current data element: [id = {}]", currentId);
179 if (currentId != null && currentId.equals(deviceId)) {
180 return deviceInformationRecord;
184 logger.debug("Unable to find device data.");
188 private State getLastLocationUpdateDateTimeState(ICloudDeviceInformation deviceInformationRecord) {
189 State dateTime = UnDefType.UNDEF;
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);
200 private @Nullable ICloudAccountBridgeHandler getIcloudAccount() {
201 if (icloudAccount == null) {
202 Bridge bridge = getBridge();
203 if (bridge == null) {
206 ThingHandler handler = bridge.getHandler();
207 if (handler instanceof ICloudAccountBridgeHandler) {
208 icloudAccount = (ICloudAccountBridgeHandler) handler;
209 icloudAccount.registerListener(this);
214 return icloudAccount;