]> git.basschouten.com Git - openhab-addons.git/blob
a4169030e76acb1a2f79360d65a426b72343b44d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.velbus.internal.handler;
14
15 import static org.openhab.binding.velbus.internal.VelbusBindingConstants.*;
16
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.openhab.binding.velbus.internal.packets.VelbusPacket;
19 import org.openhab.binding.velbus.internal.packets.VelbusSensorSettingsRequestPacket;
20 import org.openhab.binding.velbus.internal.packets.VelbusSetTemperaturePacket;
21 import org.openhab.binding.velbus.internal.packets.VelbusThermostatModePacket;
22 import org.openhab.binding.velbus.internal.packets.VelbusThermostatOperatingModePacket;
23 import org.openhab.core.library.types.DecimalType;
24 import org.openhab.core.library.types.QuantityType;
25 import org.openhab.core.library.types.StringType;
26 import org.openhab.core.library.unit.SIUnits;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.thing.CommonTriggerEvents;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.types.Command;
33 import org.openhab.core.types.RefreshType;
34
35 /**
36  * The {@link VelbusThermostatHandler} is responsible for handling commands, which are
37  * sent to one of the channels.
38  *
39  * @author Cedric Boon - Initial contribution
40  */
41 @NonNullByDefault
42 public abstract class VelbusThermostatHandler extends VelbusTemperatureSensorHandler {
43     private static final double THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION = 0.5;
44
45     private static final StringType OPERATING_MODE_HEATING = new StringType("HEATING");
46     private static final StringType OPERATING_MODE_COOLING = new StringType("COOLING");
47
48     private static final byte OPERATING_MODE_MASK = (byte) 0x80;
49     private static final byte COOLING_MODE_MASK = (byte) 0x80;
50
51     private static final StringType MODE_COMFORT = new StringType("COMFORT");
52     private static final StringType MODE_DAY = new StringType("DAY");
53     private static final StringType MODE_NIGHT = new StringType("NIGHT");
54     private static final StringType MODE_SAFE = new StringType("SAFE");
55
56     private static final byte MODE_MASK = (byte) 0x70;
57     private static final byte COMFORT_MODE_MASK = (byte) 0x40;
58     private static final byte DAY_MODE_MASK = (byte) 0x20;
59     private static final byte NIGHT_MODE_MASK = (byte) 0x10;
60
61     private final ChannelUID currentTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
62             "currentTemperatureSetpoint");
63     private final ChannelUID heatingModeComfortTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
64             "heatingModeComfortTemperatureSetpoint");
65     private final ChannelUID heatingModeDayTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
66             "heatingModeDayTemperatureSetpoint");
67     private final ChannelUID heatingModeNightTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
68             "heatingModeNightTemperatureSetpoint");
69     private final ChannelUID heatingModeAntifrostTemperatureSetpointChannel = new ChannelUID(thing.getUID(),
70             "thermostat", "heatingModeAntiFrostTemperatureSetpoint");
71     private final ChannelUID coolingModeComfortTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
72             "coolingModeComfortTemperatureSetpoint");
73     private final ChannelUID coolingModeDayTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
74             "coolingModeDayTemperatureSetpoint");
75     private final ChannelUID coolingModeNightTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
76             "coolingModeNightTemperatureSetpoint");
77     private final ChannelUID coolingModeSafeTemperatureSetpointChannel = new ChannelUID(thing.getUID(), "thermostat",
78             "coolingModeSafeTemperatureSetpoint");
79     private final ChannelUID operatingModeChannel = new ChannelUID(thing.getUID(), "thermostat", "operatingMode");
80     private final ChannelUID modeChannel = new ChannelUID(thing.getUID(), "thermostat", "mode");
81     private final ChannelUID heaterChannel = new ChannelUID(thing.getUID(), "thermostat", "heater");
82     private final ChannelUID boostChannel = new ChannelUID(thing.getUID(), "thermostat", "boost");
83     private final ChannelUID pumpChannel = new ChannelUID(thing.getUID(), "thermostat", "pump");
84     private final ChannelUID coolerChannel = new ChannelUID(thing.getUID(), "thermostat", "cooler");
85     private final ChannelUID alarm1Channel = new ChannelUID(thing.getUID(), "thermostat", "alarm1");
86     private final ChannelUID alarm2Channel = new ChannelUID(thing.getUID(), "thermostat", "alarm2");
87     private final ChannelUID alarm3Channel = new ChannelUID(thing.getUID(), "thermostat", "alarm3");
88     private final ChannelUID alarm4Channel = new ChannelUID(thing.getUID(), "thermostat", "alarm4");
89
90     public VelbusThermostatHandler(Thing thing, int numberOfSubAddresses, ChannelUID temperatureChannel) {
91         super(thing, numberOfSubAddresses, temperatureChannel);
92     }
93
94     @Override
95     public void handleCommand(ChannelUID channelUID, Command command) {
96         super.handleCommand(channelUID, command);
97
98         VelbusBridgeHandler velbusBridgeHandler = getVelbusBridgeHandler();
99         if (velbusBridgeHandler == null) {
100             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
101             return;
102         }
103
104         if (isThermostatChannel(channelUID) && command instanceof RefreshType) {
105             VelbusSensorSettingsRequestPacket packet = new VelbusSensorSettingsRequestPacket(
106                     getModuleAddress().getAddress());
107
108             byte[] packetBytes = packet.getBytes();
109             velbusBridgeHandler.sendPacket(packetBytes);
110         } else if (isThermostatChannel(channelUID)
111                 && (command instanceof QuantityType<?> || command instanceof DecimalType)) {
112             byte temperatureVariable = determineTemperatureVariable(channelUID);
113             QuantityType<?> temperatureInDegreesCelcius = (command instanceof QuantityType<?>)
114                     ? ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS)
115                     : new QuantityType<>(((DecimalType) command), SIUnits.CELSIUS);
116
117             if (temperatureInDegreesCelcius != null) {
118                 byte temperature = convertToTwoComplementByte(temperatureInDegreesCelcius.doubleValue(),
119                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
120
121                 VelbusSetTemperaturePacket packet = new VelbusSetTemperaturePacket(getModuleAddress().getAddress(),
122                         temperatureVariable, temperature);
123
124                 byte[] packetBytes = packet.getBytes();
125                 velbusBridgeHandler.sendPacket(packetBytes);
126             }
127         } else if (channelUID.equals(operatingModeChannel) && command instanceof StringType) {
128             byte commandByte = ((StringType) command).equals(OPERATING_MODE_HEATING) ? COMMAND_SET_HEATING_MODE
129                     : COMMAND_SET_COOLING_MODE;
130
131             VelbusThermostatOperatingModePacket packet = new VelbusThermostatOperatingModePacket(
132                     getModuleAddress().getAddress(), commandByte);
133
134             byte[] packetBytes = packet.getBytes();
135             velbusBridgeHandler.sendPacket(packetBytes);
136         } else if (channelUID.equals(modeChannel) && command instanceof StringType) {
137             byte commandByte = COMMAND_SWITCH_TO_SAFE_MODE;
138
139             StringType stringTypeCommand = (StringType) command;
140             if (stringTypeCommand.equals(MODE_COMFORT)) {
141                 commandByte = COMMAND_SWITCH_TO_COMFORT_MODE;
142             } else if (stringTypeCommand.equals(MODE_DAY)) {
143                 commandByte = COMMAND_SWITCH_TO_DAY_MODE;
144             } else if (stringTypeCommand.equals(MODE_NIGHT)) {
145                 commandByte = COMMAND_SWITCH_TO_NIGHT_MODE;
146             }
147
148             VelbusThermostatModePacket packet = new VelbusThermostatModePacket(getModuleAddress().getAddress(),
149                     commandByte);
150
151             byte[] packetBytes = packet.getBytes();
152             velbusBridgeHandler.sendPacket(packetBytes);
153         } else {
154             logger.debug("The command '{}' is not supported by this handler.", command.getClass());
155         }
156     }
157
158     @Override
159     public void onPacketReceived(byte[] packet) {
160         super.onPacketReceived(packet);
161
162         logger.trace("onPacketReceived() was called");
163
164         if (packet[0] == VelbusPacket.STX && packet.length >= 5) {
165             byte address = packet[2];
166             byte command = packet[4];
167
168             if (command == COMMAND_TEMP_SENSOR_SETTINGS_PART1 && packet.length >= 9) {
169                 byte currentTemperatureSetByte = packet[5];
170                 byte heatingModeComfortTemperatureSetByte = packet[6];
171                 byte heatingModeDayTemperatureSetByte = packet[7];
172                 byte heatingModeNightTemperatureSetByte = packet[8];
173                 byte heatingModeAntiFrostTemperatureSetByte = packet[9];
174
175                 double currentTemperatureSet = convertFromTwoComplementByte(currentTemperatureSetByte,
176                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
177                 double heatingModeComfortTemperatureSet = convertFromTwoComplementByte(
178                         heatingModeComfortTemperatureSetByte, THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
179                 double heatingModeDayTemperatureSet = convertFromTwoComplementByte(heatingModeDayTemperatureSetByte,
180                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
181                 double heatingModeNightTemperatureSet = convertFromTwoComplementByte(heatingModeNightTemperatureSetByte,
182                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
183                 double heatingModeAntiFrostTemperatureSet = convertFromTwoComplementByte(
184                         heatingModeAntiFrostTemperatureSetByte, THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
185
186                 updateState(currentTemperatureSetpointChannel,
187                         new QuantityType<>(currentTemperatureSet, SIUnits.CELSIUS));
188                 updateState(heatingModeComfortTemperatureSetpointChannel,
189                         new QuantityType<>(heatingModeComfortTemperatureSet, SIUnits.CELSIUS));
190                 updateState(heatingModeDayTemperatureSetpointChannel,
191                         new QuantityType<>(heatingModeDayTemperatureSet, SIUnits.CELSIUS));
192                 updateState(heatingModeNightTemperatureSetpointChannel,
193                         new QuantityType<>(heatingModeNightTemperatureSet, SIUnits.CELSIUS));
194                 updateState(heatingModeAntifrostTemperatureSetpointChannel,
195                         new QuantityType<>(heatingModeAntiFrostTemperatureSet, SIUnits.CELSIUS));
196             } else if (command == COMMAND_TEMP_SENSOR_SETTINGS_PART2 && packet.length >= 8) {
197                 byte coolingModeComfortTemperatureSetByte = packet[5];
198                 byte coolingModeDayTemperatureSetByte = packet[6];
199                 byte coolingModeNightTemperatureSetByte = packet[7];
200                 byte coolingModeSafeTemperatureSetByte = packet[8];
201
202                 double coolingModeComfortTemperatureSet = convertFromTwoComplementByte(
203                         coolingModeComfortTemperatureSetByte, THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
204                 double coolingModeDayTemperatureSet = convertFromTwoComplementByte(coolingModeDayTemperatureSetByte,
205                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
206                 double coolingModeNightTemperatureSet = convertFromTwoComplementByte(coolingModeNightTemperatureSetByte,
207                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
208                 double coolingModeSafeTemperatureSet = convertFromTwoComplementByte(coolingModeSafeTemperatureSetByte,
209                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
210
211                 updateState(coolingModeComfortTemperatureSetpointChannel,
212                         new QuantityType<>(coolingModeComfortTemperatureSet, SIUnits.CELSIUS));
213                 updateState(coolingModeDayTemperatureSetpointChannel,
214                         new QuantityType<>(coolingModeDayTemperatureSet, SIUnits.CELSIUS));
215                 updateState(coolingModeNightTemperatureSetpointChannel,
216                         new QuantityType<>(coolingModeNightTemperatureSet, SIUnits.CELSIUS));
217                 updateState(coolingModeSafeTemperatureSetpointChannel,
218                         new QuantityType<>(coolingModeSafeTemperatureSet, SIUnits.CELSIUS));
219             } else if (command == COMMAND_TEMP_SENSOR_STATUS && packet.length >= 9) {
220                 byte operatingMode = packet[5];
221                 byte targetTemperature = packet[9];
222
223                 if ((operatingMode & OPERATING_MODE_MASK) == COOLING_MODE_MASK) {
224                     updateState(operatingModeChannel, OPERATING_MODE_COOLING);
225                 } else {
226                     updateState(operatingModeChannel, OPERATING_MODE_HEATING);
227                 }
228
229                 if ((operatingMode & MODE_MASK) == COMFORT_MODE_MASK) {
230                     updateState(modeChannel, MODE_COMFORT);
231                 } else if ((operatingMode & MODE_MASK) == DAY_MODE_MASK) {
232                     updateState(modeChannel, MODE_DAY);
233                 } else if ((operatingMode & MODE_MASK) == NIGHT_MODE_MASK) {
234                     updateState(modeChannel, MODE_NIGHT);
235                 } else {
236                     updateState(modeChannel, MODE_SAFE);
237                 }
238
239                 double targetTemperatureValue = convertFromTwoComplementByte(targetTemperature,
240                         THERMOSTAT_TEMPERATURE_SETPOINT_RESOLUTION);
241                 updateState(currentTemperatureSetpointChannel,
242                         new QuantityType<>(targetTemperatureValue, SIUnits.CELSIUS));
243             } else if (address != this.getModuleAddress().getAddress() && command == COMMAND_PUSH_BUTTON_STATUS) {
244                 byte outputChannelsJustActivated = packet[5];
245                 byte outputChannelsJustDeactivated = packet[6];
246
247                 triggerThermostatChannels(outputChannelsJustActivated, CommonTriggerEvents.PRESSED);
248                 triggerThermostatChannels(outputChannelsJustDeactivated, CommonTriggerEvents.RELEASED);
249             }
250         }
251     }
252
253     private void triggerThermostatChannels(byte outputChannels, String event) {
254         if ((outputChannels & 0x01) == 0x01) {
255             triggerChannel(heaterChannel, event);
256         }
257         if ((outputChannels & 0x02) == 0x02) {
258             triggerChannel(boostChannel, event);
259         }
260         if ((outputChannels & 0x04) == 0x04) {
261             triggerChannel(pumpChannel, event);
262         }
263         if ((outputChannels & 0x08) == 0x08) {
264             triggerChannel(coolerChannel, event);
265         }
266         if ((outputChannels & 0x10) == 0x10) {
267             triggerChannel(alarm1Channel, event);
268         }
269         if ((outputChannels & 0x20) == 0x20) {
270             triggerChannel(alarm2Channel, event);
271         }
272         if ((outputChannels & 0x40) == 0x40) {
273             triggerChannel(alarm3Channel, event);
274         }
275         if ((outputChannels & 0x80) == 0x80) {
276             triggerChannel(alarm4Channel, event);
277         }
278     }
279
280     protected boolean isThermostatChannel(ChannelUID channelUID) {
281         return channelUID.equals(currentTemperatureSetpointChannel)
282                 || channelUID.equals(heatingModeComfortTemperatureSetpointChannel)
283                 || channelUID.equals(heatingModeDayTemperatureSetpointChannel)
284                 || channelUID.equals(heatingModeNightTemperatureSetpointChannel)
285                 || channelUID.equals(heatingModeAntifrostTemperatureSetpointChannel)
286                 || channelUID.equals(coolingModeComfortTemperatureSetpointChannel)
287                 || channelUID.equals(coolingModeDayTemperatureSetpointChannel)
288                 || channelUID.equals(coolingModeNightTemperatureSetpointChannel)
289                 || channelUID.equals(coolingModeSafeTemperatureSetpointChannel)
290                 || channelUID.equals(operatingModeChannel) || channelUID.equals(modeChannel);
291     }
292
293     protected byte determineTemperatureVariable(ChannelUID channelUID) {
294         if (channelUID.equals(currentTemperatureSetpointChannel)) {
295             return 0x00;
296         } else if (channelUID.equals(heatingModeComfortTemperatureSetpointChannel)) {
297             return 0x01;
298         } else if (channelUID.equals(heatingModeDayTemperatureSetpointChannel)) {
299             return 0x02;
300         } else if (channelUID.equals(heatingModeNightTemperatureSetpointChannel)) {
301             return 0x03;
302         } else if (channelUID.equals(heatingModeAntifrostTemperatureSetpointChannel)) {
303             return 0x04;
304         } else if (channelUID.equals(coolingModeComfortTemperatureSetpointChannel)) {
305             return 0x07;
306         } else if (channelUID.equals(coolingModeDayTemperatureSetpointChannel)) {
307             return 0x08;
308         } else if (channelUID.equals(coolingModeNightTemperatureSetpointChannel)) {
309             return 0x09;
310         } else if (channelUID.equals(coolingModeSafeTemperatureSetpointChannel)) {
311             return 0x0A;
312         } else {
313             throw new IllegalArgumentException("The given channelUID is not a thermostat channel: " + channelUID);
314         }
315     }
316
317     protected double convertFromTwoComplementByte(byte value, double resolution) {
318         return ((value & 0x80) == 0x00) ? value * resolution : ((value & 0x7F) - 0x80) * resolution;
319     }
320
321     protected byte convertToTwoComplementByte(double value, double resolution) {
322         return (byte) (value / resolution);
323     }
324 }