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