2 * Copyright (c) 2010-2023 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.nest.internal.wwn.handler;
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;
22 import javax.measure.Quantity;
23 import javax.measure.Unit;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.nest.internal.wwn.config.WWNDeviceConfiguration;
29 import org.openhab.binding.nest.internal.wwn.dto.WWNIdentifiable;
30 import org.openhab.binding.nest.internal.wwn.dto.WWNUpdateRequest;
31 import org.openhab.binding.nest.internal.wwn.listener.WWNThingDataListener;
32 import org.openhab.core.library.types.DateTimeType;
33 import org.openhab.core.library.types.DecimalType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.QuantityType;
36 import org.openhab.core.library.types.StringType;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.ThingStatusInfo;
43 import org.openhab.core.thing.binding.BaseThingHandler;
44 import org.openhab.core.types.State;
45 import org.openhab.core.types.UnDefType;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * Deals with the structures on the WWN API, turning them into a thing in openHAB.
52 * @author David Bennett - Initial contribution
53 * @author Martin van Wingerden - Splitted of NestBaseHandler
54 * @author Wouter Born - Add generic update data type
56 * @param <T> the type of update data
59 public abstract class WWNBaseHandler<@NonNull T> extends BaseThingHandler
60 implements WWNThingDataListener<T>, WWNIdentifiable {
61 private final Logger logger = LoggerFactory.getLogger(WWNBaseHandler.class);
63 private String deviceId = "";
64 private Class<T> dataClass;
66 WWNBaseHandler(Thing thing, Class<T> dataClass) {
68 this.dataClass = dataClass;
72 public void initialize() {
73 logger.debug("Initializing handler for {}", getClass().getName());
75 WWNAccountHandler handler = getAccountHandler();
76 if (handler != null) {
77 boolean success = handler.addThingDataListener(dataClass, getId(), this);
78 logger.debug("Adding {} with ID '{}' as device data listener, result: {}", getClass().getSimpleName(),
81 logger.debug("Unable to add {} with ID '{}' as device data listener because bridge is null",
82 getClass().getSimpleName(), getId());
85 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Waiting for refresh");
87 final @Nullable T lastUpdate = getLastUpdate();
88 if (lastUpdate != null) {
89 update(null, lastUpdate);
94 public void dispose() {
95 WWNAccountHandler handler = getAccountHandler();
96 if (handler != null) {
97 handler.removeThingDataListener(dataClass, getId(), this);
101 protected @Nullable T getLastUpdate() {
102 WWNAccountHandler handler = getAccountHandler();
103 if (handler != null) {
104 return handler.getLastUpdate(dataClass, getId());
109 protected void addUpdateRequest(String updatePath, String field, Object value) {
110 WWNAccountHandler handler = getAccountHandler();
111 if (handler != null) {
112 handler.addUpdateRequest(new WWNUpdateRequest.Builder() //
113 .withBasePath(updatePath) //
114 .withIdentifier(getId()) //
115 .withAdditionalValue(field, value) //
121 public String getId() {
122 return getDeviceId();
125 protected String getDeviceId() {
126 String localDeviceId = deviceId;
127 if (localDeviceId.isEmpty()) {
128 localDeviceId = getConfigAs(WWNDeviceConfiguration.class).deviceId;
129 deviceId = localDeviceId;
131 return localDeviceId;
134 protected @Nullable WWNAccountHandler getAccountHandler() {
135 Bridge bridge = getBridge();
136 return bridge != null ? (WWNAccountHandler) bridge.getHandler() : null;
139 protected abstract State getChannelState(ChannelUID channelUID, T data);
141 protected State getAsDateTimeTypeOrNull(@Nullable Date date) {
143 return UnDefType.NULL;
146 long offsetMillis = TimeZone.getDefault().getOffset(date.getTime());
147 Instant instant = date.toInstant().plusMillis(offsetMillis);
148 return new DateTimeType(ZonedDateTime.ofInstant(instant, TimeZone.getDefault().toZoneId()));
151 protected State getAsDecimalTypeOrNull(@Nullable Integer value) {
152 return value == null ? UnDefType.NULL : new DecimalType(value);
155 protected State getAsOnOffTypeOrNull(@Nullable Boolean value) {
156 return value == null ? UnDefType.NULL : value ? OnOffType.ON : OnOffType.OFF;
159 protected <U extends Quantity<U>> State getAsQuantityTypeOrNull(@Nullable Number value, Unit<U> unit) {
160 return value == null ? UnDefType.NULL : new QuantityType<>(value, unit);
163 protected State getAsStringTypeOrNull(@Nullable Object value) {
164 return value == null ? UnDefType.NULL : new StringType(value.toString());
167 protected State getAsStringTypeListOrNull(@Nullable Collection<@NonNull ?> values) {
168 return values == null || values.isEmpty() ? UnDefType.NULL
169 : new StringType(values.stream().map(value -> value.toString()).collect(Collectors.joining(",")));
172 protected boolean isNotHandling(WWNIdentifiable nestIdentifiable) {
173 return !(getId().equals(nestIdentifiable.getId()));
176 protected void updateLinkedChannels(@Nullable T oldData, T data) {
177 getThing().getChannels().stream().map(channel -> channel.getUID()).filter(this::isLinked)
178 .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);
188 public void onNewData(T data) {
193 public void onUpdatedData(T oldData, T data) {
194 update(oldData, data);
198 public void onMissingData(String nestId) {
200 new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.GONE, "Missing from streaming updates"));
203 protected abstract void update(@Nullable T oldData, T data);