]> git.basschouten.com Git - openhab-addons.git/blob
a3df6fb063245337bd8a6875b8cfc8e16d269b72
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.deconz.internal.handler;
14
15 import static org.openhab.binding.deconz.internal.BindingConstants.*;
16 import static org.openhab.core.library.unit.SIUnits.CELSIUS;
17 import static org.openhab.core.library.unit.Units.PERCENT;
18
19 import java.math.BigDecimal;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Set;
24
25 import javax.measure.quantity.Temperature;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
30 import org.openhab.binding.deconz.internal.dto.SensorConfig;
31 import org.openhab.binding.deconz.internal.dto.SensorMessage;
32 import org.openhab.binding.deconz.internal.dto.SensorState;
33 import org.openhab.binding.deconz.internal.dto.ThermostatUpdateConfig;
34 import org.openhab.binding.deconz.internal.types.ThermostatMode;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.library.types.OpenClosedType;
37 import org.openhab.core.library.types.QuantityType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingTypeUID;
42 import org.openhab.core.thing.binding.builder.ChannelBuilder;
43 import org.openhab.core.thing.binding.builder.ThingBuilder;
44 import org.openhab.core.thing.type.ChannelTypeUID;
45 import org.openhab.core.types.Command;
46 import org.openhab.core.types.RefreshType;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import com.google.gson.Gson;
51
52 /**
53  * This sensor Thermostat Thing doesn't establish any connections, that is done by the bridge Thing.
54  *
55  * It waits for the bridge to come online, grab the websocket connection and bridge configuration
56  * and registers to the websocket connection as a listener.
57  *
58  * A REST API call is made to get the initial sensor state.
59  *
60  * Only the Thermostat is supported by this Thing, because a unified state is kept
61  * in {@link #sensorState}. Every field that got received by the REST API for this specific
62  * sensor is published to the framework.
63  *
64  * @author Lukas Agethen - Initial contribution
65  */
66 @NonNullByDefault
67 public class SensorThermostatThingHandler extends SensorBaseThingHandler {
68     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_THERMOSTAT);
69
70     private static final List<String> CONFIG_CHANNELS = Arrays.asList(CHANNEL_BATTERY_LEVEL, CHANNEL_BATTERY_LOW,
71             CHANNEL_HEATSETPOINT, CHANNEL_TEMPERATURE_OFFSET, CHANNEL_THERMOSTAT_MODE);
72
73     private final Logger logger = LoggerFactory.getLogger(SensorThermostatThingHandler.class);
74
75     public SensorThermostatThingHandler(Thing thing, Gson gson) {
76         super(thing, gson);
77     }
78
79     @Override
80     public void handleCommand(ChannelUID channelUID, Command command) {
81         if (command instanceof RefreshType) {
82             sensorState.buttonevent = null;
83             valueUpdated(channelUID, sensorState, false);
84             return;
85         }
86         ThermostatUpdateConfig newConfig = new ThermostatUpdateConfig();
87         switch (channelUID.getId()) {
88             case CHANNEL_HEATSETPOINT:
89                 Integer newHeatsetpoint = getTemperatureFromCommand(command);
90                 if (newHeatsetpoint == null) {
91                     logger.warn("Heatsetpoint must not be null.");
92                     return;
93                 }
94                 newConfig.heatsetpoint = newHeatsetpoint;
95                 break;
96             case CHANNEL_TEMPERATURE_OFFSET:
97                 Integer newOffset = getTemperatureFromCommand(command);
98                 if (newOffset == null) {
99                     logger.warn("Offset must not be null.");
100                     return;
101                 }
102                 newConfig.offset = newOffset;
103                 break;
104             case CHANNEL_THERMOSTAT_MODE:
105                 if (command instanceof StringType) {
106                     String thermostatMode = ((StringType) command).toString();
107                     try {
108                         newConfig.mode = ThermostatMode.valueOf(thermostatMode);
109                     } catch (IllegalArgumentException ex) {
110                         logger.warn("Invalid thermostat mode: {}. Valid values: {}", thermostatMode,
111                                 ThermostatMode.values());
112                         return;
113                     }
114                     if (newConfig.mode == ThermostatMode.UNKNOWN) {
115                         logger.warn("Invalid thermostat mode: {}. Valid values: {}", thermostatMode,
116                                 ThermostatMode.values());
117                         return;
118                     }
119                 } else {
120                     return;
121                 }
122                 break;
123             default:
124                 // no supported command
125                 return;
126
127         }
128
129         sendCommand(newConfig, command, channelUID, null);
130     }
131
132     @Override
133     protected void valueUpdated(ChannelUID channelUID, SensorConfig newConfig) {
134         super.valueUpdated(channelUID, newConfig);
135         ThermostatMode thermostatMode = newConfig.mode;
136         String mode = thermostatMode != null ? thermostatMode.name() : ThermostatMode.UNKNOWN.name();
137         switch (channelUID.getId()) {
138             case CHANNEL_HEATSETPOINT:
139                 updateQuantityTypeChannel(channelUID, newConfig.heatsetpoint, CELSIUS, 1.0 / 100);
140                 break;
141             case CHANNEL_TEMPERATURE_OFFSET:
142                 updateQuantityTypeChannel(channelUID, newConfig.offset, CELSIUS, 1.0 / 100);
143                 break;
144             case CHANNEL_THERMOSTAT_MODE:
145                 updateState(channelUID, new StringType(mode));
146                 break;
147         }
148     }
149
150     @Override
151     protected void valueUpdated(ChannelUID channelUID, SensorState newState, boolean initializing) {
152         super.valueUpdated(channelUID, newState, initializing);
153         switch (channelUID.getId()) {
154             case CHANNEL_TEMPERATURE:
155                 updateQuantityTypeChannel(channelUID, newState.temperature, CELSIUS, 1.0 / 100);
156                 break;
157             case CHANNEL_VALVE_POSITION:
158                 updateQuantityTypeChannel(channelUID, newState.valve, PERCENT, 100.0 / 255);
159                 break;
160             case CHANNEL_WINDOWOPEN:
161                 String open = newState.windowopen;
162                 if (open != null) {
163                     updateState(channelUID, "Closed".equals(open) ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
164                 }
165                 break;
166         }
167     }
168
169     @Override
170     protected void createTypeSpecificChannels(SensorConfig sensorConfig, SensorState sensorState) {
171     }
172
173     @Override
174     protected List<String> getConfigChannels() {
175         return CONFIG_CHANNELS;
176     }
177
178     private @Nullable Integer getTemperatureFromCommand(Command command) {
179         BigDecimal newTemperature;
180         if (command instanceof DecimalType) {
181             newTemperature = ((DecimalType) command).toBigDecimal();
182         } else if (command instanceof QuantityType) {
183             @SuppressWarnings("unchecked")
184             QuantityType<Temperature> temperatureCelsius = ((QuantityType<Temperature>) command).toUnit(CELSIUS);
185             if (temperatureCelsius != null) {
186                 newTemperature = temperatureCelsius.toBigDecimal();
187             } else {
188                 return null;
189             }
190         } else {
191             return null;
192         }
193         return newTemperature.scaleByPowerOfTen(2).intValue();
194     }
195
196     @Override
197     protected void processStateResponse(DeconzBaseMessage stateResponse) {
198         if (!(stateResponse instanceof SensorMessage)) {
199             return;
200         }
201
202         SensorMessage sensorMessage = (SensorMessage) stateResponse;
203         SensorState sensorState = sensorMessage.state;
204         if (sensorState != null && sensorState.windowopen != null && thing.getChannel(CHANNEL_WINDOWOPEN) == null) {
205             ThingBuilder thingBuilder = editThing();
206             thingBuilder.withChannel(ChannelBuilder.create(new ChannelUID(thing.getUID(), CHANNEL_WINDOWOPEN), "String")
207                     .withType(new ChannelTypeUID(BINDING_ID, "open")).build());
208             updateThing(thingBuilder.build());
209         }
210
211         super.processStateResponse(stateResponse);
212     }
213 }