]> git.basschouten.com Git - openhab-addons.git/blob
d70032bb66d7bffe217f1a3431e6e0547352385c
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.nest.internal.handler;
14
15 import java.time.Instant;
16 import java.time.ZonedDateTime;
17 import java.util.Collection;
18 import java.util.Date;
19 import java.util.TimeZone;
20 import java.util.stream.Collectors;
21
22 import javax.measure.Quantity;
23 import javax.measure.Unit;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.nest.internal.config.NestDeviceConfiguration;
28 import org.openhab.binding.nest.internal.data.NestIdentifiable;
29 import org.openhab.binding.nest.internal.listener.NestThingDataListener;
30 import org.openhab.binding.nest.internal.rest.NestUpdateRequest;
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.QuantityType;
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.ThingStatusInfo;
42 import org.openhab.core.thing.binding.BaseThingHandler;
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  * Deals with the structures on the Nest API, turning them into a thing in openHAB.
50  *
51  * @author David Bennett - Initial contribution
52  * @author Martin van Wingerden - Splitted of NestBaseHandler
53  * @author Wouter Born - Add generic update data type
54  *
55  * @param <T> the type of update data
56  */
57 @NonNullByDefault
58 public abstract class NestBaseHandler<T> extends BaseThingHandler
59         implements NestThingDataListener<T>, NestIdentifiable {
60     private final Logger logger = LoggerFactory.getLogger(NestBaseHandler.class);
61
62     private @Nullable String deviceId;
63     private Class<T> dataClass;
64
65     NestBaseHandler(Thing thing, Class<T> dataClass) {
66         super(thing);
67         this.dataClass = dataClass;
68     }
69
70     @Override
71     public void initialize() {
72         logger.debug("Initializing handler for {}", getClass().getName());
73
74         NestBridgeHandler handler = getNestBridgeHandler();
75         if (handler != null) {
76             boolean success = handler.addThingDataListener(dataClass, getId(), this);
77             logger.debug("Adding {} with ID '{}' as device data listener, result: {}", getClass().getSimpleName(),
78                     getId(), success);
79         } else {
80             logger.debug("Unable to add {} with ID '{}' as device data listener because bridge is null",
81                     getClass().getSimpleName(), getId());
82         }
83
84         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Waiting for refresh");
85
86         T lastUpdate = getLastUpdate();
87         if (lastUpdate != null) {
88             update(null, lastUpdate);
89         }
90     }
91
92     @Override
93     public void dispose() {
94         NestBridgeHandler handler = getNestBridgeHandler();
95         if (handler != null) {
96             handler.removeThingDataListener(dataClass, getId(), this);
97         }
98     }
99
100     protected @Nullable T getLastUpdate() {
101         NestBridgeHandler handler = getNestBridgeHandler();
102         if (handler != null) {
103             return handler.getLastUpdate(dataClass, getId());
104         }
105         return null;
106     }
107
108     protected void addUpdateRequest(String updatePath, String field, Object value) {
109         NestBridgeHandler handler = getNestBridgeHandler();
110         if (handler != null) {
111             // @formatter:off
112             handler.addUpdateRequest(new NestUpdateRequest.Builder()
113                 .withBasePath(updatePath)
114                 .withIdentifier(getId())
115                 .withAdditionalValue(field, value)
116                 .build());
117             // @formatter:on
118         }
119     }
120
121     @Override
122     public String getId() {
123         return getDeviceId();
124     }
125
126     protected String getDeviceId() {
127         String localDeviceId = deviceId;
128         if (localDeviceId == null) {
129             localDeviceId = getConfigAs(NestDeviceConfiguration.class).deviceId;
130             deviceId = localDeviceId;
131         }
132         return localDeviceId;
133     }
134
135     protected @Nullable NestBridgeHandler getNestBridgeHandler() {
136         Bridge bridge = getBridge();
137         return bridge != null ? (NestBridgeHandler) bridge.getHandler() : null;
138     }
139
140     protected abstract State getChannelState(ChannelUID channelUID, T data);
141
142     protected State getAsDateTimeTypeOrNull(@Nullable Date date) {
143         if (date == null) {
144             return UnDefType.NULL;
145         }
146
147         long offsetMillis = TimeZone.getDefault().getOffset(date.getTime());
148         Instant instant = date.toInstant().plusMillis(offsetMillis);
149         return new DateTimeType(ZonedDateTime.ofInstant(instant, TimeZone.getDefault().toZoneId()));
150     }
151
152     protected State getAsDecimalTypeOrNull(@Nullable Integer value) {
153         return value == null ? UnDefType.NULL : new DecimalType(value);
154     }
155
156     protected State getAsOnOffTypeOrNull(@Nullable Boolean value) {
157         return value == null ? UnDefType.NULL : value ? OnOffType.ON : OnOffType.OFF;
158     }
159
160     protected <U extends Quantity<U>> State getAsQuantityTypeOrNull(@Nullable Number value, Unit<U> unit) {
161         return value == null ? UnDefType.NULL : new QuantityType<>(value, unit);
162     }
163
164     protected State getAsStringTypeOrNull(@Nullable Object value) {
165         return value == null ? UnDefType.NULL : new StringType(value.toString());
166     }
167
168     protected State getAsStringTypeListOrNull(@Nullable Collection<?> values) {
169         return values == null || values.isEmpty() ? UnDefType.NULL
170                 : new StringType(values.stream().map(v -> v.toString()).collect(Collectors.joining(",")));
171     }
172
173     protected boolean isNotHandling(NestIdentifiable nestIdentifiable) {
174         return !(getId().equals(nestIdentifiable.getId()));
175     }
176
177     protected void updateLinkedChannels(T oldData, T data) {
178         getThing().getChannels().stream().map(c -> c.getUID()).filter(this::isLinked).forEach(channelUID -> {
179             State newState = getChannelState(channelUID, data);
180             if (oldData == null || !getChannelState(channelUID, oldData).equals(newState)) {
181                 logger.debug("Updating {}", channelUID);
182                 updateState(channelUID, newState);
183             }
184         });
185     }
186
187     @Override
188     public void onNewData(T data) {
189         update(null, data);
190     }
191
192     @Override
193     public void onUpdatedData(T oldData, T data) {
194         update(oldData, data);
195     }
196
197     @Override
198     public void onMissingData(String nestId) {
199         thing.setStatusInfo(
200                 new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.GONE, "Missing from streaming updates"));
201     }
202
203     protected abstract void update(T oldData, T data);
204 }