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.ecobee.internal.handler;
15 import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
18 import java.util.concurrent.ConcurrentHashMap;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.binding.ecobee.internal.config.EcobeeSensorConfiguration;
22 import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorCapabilityDTO;
23 import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorDTO;
24 import org.openhab.core.library.unit.Units;
25 import org.openhab.core.thing.Channel;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.Thing;
28 import org.openhab.core.thing.ThingStatus;
29 import org.openhab.core.thing.ThingStatusDetail;
30 import org.openhab.core.thing.ThingStatusInfo;
31 import org.openhab.core.thing.binding.BaseThingHandler;
32 import org.openhab.core.thing.binding.builder.ChannelBuilder;
33 import org.openhab.core.thing.binding.builder.ThingBuilder;
34 import org.openhab.core.thing.type.ChannelTypeUID;
35 import org.openhab.core.types.Command;
36 import org.openhab.core.types.RefreshType;
37 import org.openhab.core.types.State;
38 import org.openhab.core.types.UnDefType;
39 import org.openhab.core.util.StringUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 * The {@link EcobeeSensorThingHandler} is responsible for updating the channels associated
45 * with an Ecobee remote sensor.
47 * @author Mark Hilbush - Initial contribution
50 public class EcobeeSensorThingHandler extends BaseThingHandler {
52 public static final String CAPABILITY_ADC = "adc";
53 public static final String CAPABILITY_AIR_PRESSURE = "airPressure";
54 public static final String CAPABILITY_AIR_QUALITY = "airQuality";
55 public static final String CAPABILITY_AIR_QUALITY_ACCURACY = "airQualityAccuracy";
56 public static final String CAPABILITY_CO2 = "co2";
57 public static final String CAPABILITY_CO2_PPM = "co2PPM";
58 public static final String CAPABILITY_DRY_CONTACT = "dryContact";
59 public static final String CAPABILITY_HUMIDITY = "humidity";
60 public static final String CAPABILITY_OCCUPANCY = "occupancy";
61 public static final String CAPABILITY_TEMPERATURE = "temperature";
62 public static final String CAPABILITY_VOC_PPM = "vocPPM";
63 public static final String CAPABILITY_UNKNOWN = "unknown";
65 private final Logger logger = LoggerFactory.getLogger(EcobeeSensorThingHandler.class);
67 private @NonNullByDefault({}) String sensorId;
69 private Map<String, State> stateCache = new ConcurrentHashMap<>();
71 public EcobeeSensorThingHandler(Thing thing) {
76 public void initialize() {
77 sensorId = getConfigAs(EcobeeSensorConfiguration.class).sensorId;
78 logger.debug("SensorThing: Initializing sensor '{}'", sensorId);
80 updateStatus(EcobeeUtils.isBridgeOnline(getBridge()) ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
84 public void dispose() {
85 logger.debug("SensorThing: Disposing sensor '{}'", sensorId);
89 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
90 if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
91 updateStatus(ThingStatus.ONLINE);
93 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
97 @SuppressWarnings("null")
99 public void handleCommand(ChannelUID channelUID, Command command) {
100 if (command instanceof RefreshType) {
101 State state = stateCache.get(channelUID.getId());
103 updateState(channelUID.getId(), state);
109 public void updateChannels(RemoteSensorDTO sensor) {
110 logger.debug("SensorThing: Updating channels for sensor '{}({})'", sensor.id, sensor.name);
111 updateChannel(CH_SENSOR_ID, EcobeeUtils.undefOrString(sensor.id));
112 updateChannel(CH_SENSOR_NAME, EcobeeUtils.undefOrString(sensor.name));
113 updateChannel(CH_SENSOR_TYPE, EcobeeUtils.undefOrString(sensor.type));
114 updateChannel(CH_SENSOR_CODE, EcobeeUtils.undefOrString(sensor.code));
115 updateChannel(CH_SENSOR_IN_USE, EcobeeUtils.undefOrOnOff(sensor.inUse));
116 for (RemoteSensorCapabilityDTO capability : sensor.capability) {
117 updateCapabilityChannels(capability);
121 private void updateCapabilityChannels(RemoteSensorCapabilityDTO capability) {
122 ChannelUID uid = new ChannelUID(thing.getUID(), capability.type);
123 Channel channel = thing.getChannel(uid);
124 if (channel == null) {
125 logger.debug("SensorThing: Create channel '{}'", uid);
126 ThingBuilder thingBuilder;
127 thingBuilder = editThing();
128 channel = ChannelBuilder.create(uid, getAcceptedItemType(capability.type))
129 .withLabel("Sensor " + StringUtils.capitalizeByWhitespace(capability.type))
130 .withType(getChannelTypeUID(capability.type)).build();
131 thingBuilder.withChannel(channel);
132 updateThing(thingBuilder.build());
134 logger.trace("Capability '{}' has type '{}' with value '{}'", capability.id, capability.type, capability.value);
135 updateCapabilityState(capability.type, capability.value);
138 // adc, co2, dryContact, humidity, temperature, occupancy, unknown.
139 private String getAcceptedItemType(String capabilityType) {
140 String acceptedItemType;
141 switch (capabilityType) {
142 case CAPABILITY_TEMPERATURE:
143 acceptedItemType = "Number:Temperature";
145 case CAPABILITY_HUMIDITY:
146 acceptedItemType = "Number:Dimensionless";
148 case CAPABILITY_OCCUPANCY:
149 acceptedItemType = "Switch";
152 case CAPABILITY_AIR_PRESSURE:
153 case CAPABILITY_AIR_QUALITY:
154 case CAPABILITY_AIR_QUALITY_ACCURACY:
156 case CAPABILITY_CO2_PPM:
157 case CAPABILITY_DRY_CONTACT:
158 case CAPABILITY_UNKNOWN:
159 case CAPABILITY_VOC_PPM:
161 acceptedItemType = "String";
164 return acceptedItemType;
167 private ChannelTypeUID getChannelTypeUID(String capabilityType) {
168 ChannelTypeUID channelTypeUID;
169 switch (capabilityType) {
170 case CAPABILITY_TEMPERATURE:
171 channelTypeUID = CHANNELTYPEUID_TEMPERATURE;
173 case CAPABILITY_HUMIDITY:
174 channelTypeUID = CHANNELTYPEUID_HUMIDITY;
176 case CAPABILITY_OCCUPANCY:
177 channelTypeUID = CHANNELTYPEUID_OCCUPANCY;
180 case CAPABILITY_AIR_PRESSURE:
181 case CAPABILITY_AIR_QUALITY:
182 case CAPABILITY_AIR_QUALITY_ACCURACY:
184 case CAPABILITY_CO2_PPM:
185 case CAPABILITY_DRY_CONTACT:
186 case CAPABILITY_UNKNOWN:
187 case CAPABILITY_VOC_PPM:
189 channelTypeUID = CHANNELTYPEUID_GENERIC;
192 return channelTypeUID;
195 private void updateCapabilityState(String capabilityType, String value) {
197 switch (capabilityType) {
198 case CAPABILITY_TEMPERATURE:
200 state = EcobeeUtils.undefOrTemperature(Integer.parseInt(value));
201 } catch (NumberFormatException e) {
202 state = UnDefType.UNDEF;
205 case CAPABILITY_HUMIDITY:
207 state = EcobeeUtils.undefOrQuantity(Integer.parseInt(value), Units.PERCENT);
208 } catch (NumberFormatException e) {
209 state = UnDefType.UNDEF;
212 case CAPABILITY_OCCUPANCY:
213 state = EcobeeUtils.undefOrOnOff("true".equals(value));
216 case CAPABILITY_AIR_PRESSURE:
217 case CAPABILITY_AIR_QUALITY:
218 case CAPABILITY_AIR_QUALITY_ACCURACY:
220 case CAPABILITY_CO2_PPM:
221 case CAPABILITY_DRY_CONTACT:
222 case CAPABILITY_UNKNOWN:
223 case CAPABILITY_VOC_PPM:
225 state = EcobeeUtils.undefOrString(value);
228 updateChannel(capabilityType, state);
231 private void updateChannel(String channelId, State state) {
232 updateState(channelId, state);
233 stateCache.put(channelId, state);
236 private void clearSavedState() {