2 * Copyright (c) 2010-2022 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.rfxcom.internal.messages;
15 import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*;
16 import static org.openhab.binding.rfxcom.internal.messages.ByteEnumUtil.fromByte;
17 import static org.openhab.binding.rfxcom.internal.messages.RFXComFanMessage.Commands.*;
18 import static org.openhab.binding.rfxcom.internal.messages.RFXComFanMessage.SubType.*;
20 import java.util.Arrays;
21 import java.util.List;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
25 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
26 import org.openhab.binding.rfxcom.internal.exceptions.RFXComInvalidStateException;
27 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
28 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException;
29 import org.openhab.binding.rfxcom.internal.handler.DeviceState;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.library.types.OnOffType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.library.types.UpDownType;
34 import org.openhab.core.types.State;
35 import org.openhab.core.types.Type;
36 import org.openhab.core.types.UnDefType;
39 * RFXCOM data class for fan message.
41 * @author Martin van Wingerden - initial contribution
43 public class RFXComFanMessage extends RFXComDeviceMessageImpl<RFXComFanMessage.SubType> {
45 public enum SubType implements ByteEnumWrapper {
50 WESTINGHOUSE_7226640(0x04),
55 LUCCI_AIR_DC_II(0x09),
56 ITHO_CVE_ECO_RFT(0x0A),
59 private final int subType;
61 SubType(int subType) {
62 this.subType = subType;
66 public byte toByte() {
67 return (byte) subType;
71 public enum Commands implements ByteEnumWrapperWithSupportedSubTypes<SubType> {
72 HI(1, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
73 MED(2, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
74 LOW(3, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
75 OFF(4, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
76 LIGHT(5, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
78 FALMEC_POWER_OFF(1, 0, FALMEC),
79 FALMEC_SPEED_1(2, 1, FALMEC),
80 FALMEC_SPEED_2(3, 2, FALMEC),
81 FALMEC_SPEED_3(4, 3, FALMEC),
82 FALMEC_SPEED_4(5, 4, FALMEC),
83 FALMEC_TIMER_1(6, FALMEC),
84 FALMEC_TIMER_2(7, FALMEC),
85 FALMEC_TIMER_3(8, FALMEC),
86 FALMEC_TIMER_4(9, FALMEC),
87 FALMEC_LIGHT_ON(10, FALMEC),
88 FALMEC_LIGHT_OFF(11, FALMEC),
90 FT1211R_POWER(1, 0, FT1211R),
91 FT1211R_LIGHT(2, FT1211R),
92 FT1211R_SPEED_1(3, 1, FT1211R),
93 FT1211R_SPEED_2(4, 2, FT1211R),
94 FT1211R_SPEED_3(5, 3, FT1211R),
95 FT1211R_SPEED_4(6, 4, FT1211R),
96 FT1211R_SPEED_5(7, 5, FT1211R),
97 FT1211R_FORWARD_REVERSE(8, FT1211R),
98 FT1211R_TIMER_1H(9, FT1211R),
99 FT1211R_TIMER_4H(10, FT1211R),
100 FT1211R_TIMER_8H(11, FT1211R),
102 LUCCI_AIR_DC_POWER(1, LUCCI_AIR_DC),
103 LUCCI_AIR_DC_UP(2, LUCCI_AIR_DC),
104 LUCCI_AIR_DC_DOWN(3, LUCCI_AIR_DC),
105 LUCCI_AIR_DC_LIGHT(4, LUCCI_AIR_DC),
106 LUCCI_AIR_DC_REVERSE(5, LUCCI_AIR_DC),
107 LUCCI_AIR_DC_NATURAL_FLOW(6, LUCCI_AIR_DC),
108 LUCCI_AIR_DC_PAIR(7, LUCCI_AIR_DC),
110 LUCCI_AIR_DC_II_POWER_OFF(1, 0, LUCCI_AIR_DC_II),
111 LUCCI_AIR_DC_II_SPEED_1(2, 1, LUCCI_AIR_DC_II),
112 LUCCI_AIR_DC_II_SPEED_2(3, 2, LUCCI_AIR_DC_II),
113 LUCCI_AIR_DC_II_SPEED_3(4, 3, LUCCI_AIR_DC_II),
114 LUCCI_AIR_DC_II_SPEED_4(5, 4, LUCCI_AIR_DC_II),
115 LUCCI_AIR_DC_II_SPEED_5(6, 5, LUCCI_AIR_DC_II),
116 LUCCI_AIR_DC_II_SPEED_6(7, 6, LUCCI_AIR_DC_II),
117 LUCCI_AIR_DC_II_LIGHT(8, LUCCI_AIR_DC_II),
118 LUCCI_AIR_DC_II_REVERSE(9, LUCCI_AIR_DC_II),
125 NOVY_RESET_FILTER(6, NOVY);
127 private final int command;
128 private final Integer speed;
129 private final List<SubType> supportedBySubTypes;
131 Commands(int command, SubType... supportedSubType) {
132 this(command, null, supportedSubType);
135 Commands(int command, Integer speed, SubType... supportedSubType) {
136 this.command = command;
138 this.supportedBySubTypes = Arrays.asList(supportedSubType);
142 public static Commands bySpeed(SubType subType, int speed) {
143 for (Commands value : values()) {
144 if (value.supportedBySubTypes.contains(subType) && value.speed == speed) {
152 public byte toByte() {
153 return (byte) command;
156 public Integer getSpeed() {
161 public List<SubType> supportedBySubTypes() {
162 return supportedBySubTypes;
166 private static final List<SubType> GENERIC_SUB_TYPES = Arrays.asList(WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN);
168 private static final List<Commands> LIGHT_ON_COMMANDS = Arrays.asList(LIGHT, LUCCI_AIR_DC_LIGHT,
169 LUCCI_AIR_DC_II_LIGHT, FALMEC_LIGHT_ON, NOVY_LIGHT);
170 private static final List<Commands> ON_COMMANDS = Arrays.asList(HI, MED, LOW, FALMEC_SPEED_1, FALMEC_SPEED_2,
171 FALMEC_SPEED_3, FALMEC_SPEED_4, LUCCI_AIR_DC_II_SPEED_1, LUCCI_AIR_DC_II_SPEED_2, LUCCI_AIR_DC_II_SPEED_3,
172 LUCCI_AIR_DC_II_SPEED_4, LUCCI_AIR_DC_II_SPEED_5, LUCCI_AIR_DC_II_SPEED_6);
173 private static final List<Commands> OFF_COMMANDS = Arrays.asList(OFF, FALMEC_POWER_OFF, LUCCI_AIR_DC_II_POWER_OFF);
175 private SubType subType;
176 private int sensorId;
177 private Commands command;
179 public RFXComFanMessage() {
180 super(PacketType.FAN);
183 public RFXComFanMessage(byte[] data) throws RFXComException {
188 public PacketType getPacketType() {
192 case WESTINGHOUSE_7226640:
193 return PacketType.FAN;
195 return PacketType.FAN_SF01;
197 return PacketType.FAN_ITHO;
199 return PacketType.FAN_SEAV;
201 return PacketType.FAN_LUCCI_DC;
203 return PacketType.FAN_FT1211R;
205 return PacketType.FAN_FALMEC;
206 case LUCCI_AIR_DC_II:
207 return PacketType.FAN_LUCCI_DC_II;
208 case ITHO_CVE_ECO_RFT:
209 return PacketType.FAN_ITHO_CVE_ECO_RFT;
211 return PacketType.FAN_NOVY;
213 return super.getPacketType();
217 public void encodeMessage(byte[] data) throws RFXComException {
218 super.encodeMessage(data);
220 subType = fromByte(SubType.class, super.subType);
221 sensorId = (data[4] & 0xFF) << 16 | (data[5] & 0xFF) << 8 | (data[6] & 0xFF);
222 command = fromByte(Commands.class, data[7], subType);
224 signalLevel = (byte) (data[8] & 0x0F);
228 public byte[] decodeMessage() {
229 byte[] data = new byte[9];
232 data[1] = PacketType.FAN.toByte();
233 data[2] = subType.toByte();
236 data[4] = (byte) ((sensorId >> 16) & 0xFF);
237 data[5] = (byte) ((sensorId >> 8) & 0xFF);
238 data[6] = (byte) (sensorId & 0xFF);
240 data[7] = command.toByte();
242 data[8] = (byte) (signalLevel & 0x0F);
248 public String getDeviceId() {
249 return String.valueOf(sensorId);
253 public void setSubType(SubType subType) {
254 this.subType = subType;
258 public void setDeviceId(String deviceId) {
259 sensorId = Integer.parseInt(deviceId);
263 public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
264 return ByteEnumUtil.convertSubType(SubType.class, subType);
268 public State convertToState(String channelId, RFXComDeviceConfiguration config, DeviceState deviceState)
269 throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
271 case CHANNEL_FAN_LIGHT:
272 return handleLightChannel();
274 case CHANNEL_FAN_SPEED:
275 return handleFanSpeedChannel();
277 case CHANNEL_COMMAND:
278 return handleCommandChannel();
280 case CHANNEL_COMMAND_STRING:
281 return handleCommandStringChannel();
284 return super.convertToState(channelId, config, deviceState);
288 private State handleCommandStringChannel() {
289 if (command == null) {
290 return UnDefType.UNDEF;
292 return StringType.valueOf(command.toString().replace(subType.name() + "_", ""));
295 private State handleLightChannel() {
296 if (LIGHT_ON_COMMANDS.contains(command)) {
298 } else if (command == Commands.FALMEC_LIGHT_OFF) {
299 return OnOffType.OFF;
301 return UnDefType.UNDEF;
305 private State handleFanSpeedChannel() {
311 return StringType.valueOf(command.toString());
313 case FALMEC_POWER_OFF:
319 case FT1211R_SPEED_1:
320 case FT1211R_SPEED_2:
321 case FT1211R_SPEED_3:
322 case FT1211R_SPEED_4:
323 case FT1211R_SPEED_5:
324 case LUCCI_AIR_DC_II_POWER_OFF:
325 case LUCCI_AIR_DC_II_SPEED_1:
326 case LUCCI_AIR_DC_II_SPEED_2:
327 case LUCCI_AIR_DC_II_SPEED_3:
328 case LUCCI_AIR_DC_II_SPEED_4:
329 case LUCCI_AIR_DC_II_SPEED_5:
330 case LUCCI_AIR_DC_II_SPEED_6:
331 return new DecimalType(command.getSpeed());
333 case LUCCI_AIR_DC_DOWN:
335 return UpDownType.DOWN;
337 case LUCCI_AIR_DC_UP:
339 return UpDownType.UP;
346 private State handleCommandChannel() {
347 if (ON_COMMANDS.contains(command)) {
349 } else if (OFF_COMMANDS.contains(command)) {
350 return OnOffType.OFF;
357 public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
359 case CHANNEL_COMMAND:
360 command = handleCommand(channelId, type);
363 case CHANNEL_FAN_SPEED:
364 command = handleFanSpeedCommand(channelId, type);
367 case CHANNEL_FAN_LIGHT:
368 command = handleFanLightCommand(channelId, type);
371 case CHANNEL_COMMAND_STRING:
372 command = handleCommandString(channelId, type);
376 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
380 private Commands handleCommandString(String channelId, Type type) throws RFXComUnsupportedChannelException {
381 if (type instanceof StringType) {
382 String stringCommand = type.toString();
383 switch (stringCommand) {
400 return Commands.valueOf(subType.name() + "_" + stringCommand);
403 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
406 private Commands handleCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
407 if (type instanceof OnOffType) {
408 if (GENERIC_SUB_TYPES.contains(subType)) {
409 return (type == OnOffType.ON ? Commands.MED : Commands.OFF);
410 } else if (subType == FALMEC) {
411 return (type == OnOffType.ON ? Commands.FALMEC_SPEED_2 : Commands.FALMEC_POWER_OFF);
412 } else if (subType == LUCCI_AIR_DC_II) {
413 return (type == OnOffType.ON ? LUCCI_AIR_DC_II_SPEED_3 : LUCCI_AIR_DC_II_POWER_OFF);
416 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
419 private Commands handleFanSpeedCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
420 if (type instanceof StringType) {
421 String stringCommand = type.toString();
422 switch (stringCommand) {
427 return Commands.valueOf(stringCommand);
429 } else if (type instanceof DecimalType) {
430 Commands speedCommand = Commands.bySpeed(subType, ((DecimalType) type).intValue());
431 if (speedCommand != null) {
434 } else if (type instanceof UpDownType && subType == LUCCI_AIR_DC) {
435 if (UpDownType.UP == type) {
436 return Commands.LUCCI_AIR_DC_UP;
438 return Commands.LUCCI_AIR_DC_DOWN;
440 } else if (type instanceof UpDownType && subType == NOVY) {
441 if (UpDownType.UP == type) {
442 return Commands.NOVY_UP;
444 return Commands.NOVY_DOWN;
447 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
450 private Commands handleFanLightCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
451 if (type == OnOffType.ON) {
455 case WESTINGHOUSE_7226640:
459 return FALMEC_LIGHT_ON;
461 case LUCCI_AIR_DC_II:
462 return LUCCI_AIR_DC_II_LIGHT;
464 } else if (type == OnOffType.OFF && subType == FALMEC) {
465 return Commands.FALMEC_LIGHT_OFF;
467 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);