2 * Copyright (c) 2010-2021 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.exceptions.RFXComException;
25 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
26 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException;
27 import org.openhab.binding.rfxcom.internal.handler.DeviceState;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.StringType;
31 import org.openhab.core.library.types.UpDownType;
32 import org.openhab.core.types.State;
33 import org.openhab.core.types.Type;
34 import org.openhab.core.types.UnDefType;
37 * RFXCOM data class for fan message.
39 * @author Martin van Wingerden - initial contribution
41 public class RFXComFanMessage extends RFXComDeviceMessageImpl<RFXComFanMessage.SubType> {
43 public enum SubType implements ByteEnumWrapper {
48 WESTINGHOUSE_7226640(0x04),
53 LUCCI_AIR_DC_II(0x09),
54 ITHO_CVE_ECO_RFT(0x0A),
57 private final int subType;
59 SubType(int subType) {
60 this.subType = subType;
64 public byte toByte() {
65 return (byte) subType;
69 public enum Commands implements ByteEnumWrapperWithSupportedSubTypes<SubType> {
70 HI(1, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
71 MED(2, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
72 LOW(3, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
73 OFF(4, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
74 LIGHT(5, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
76 FALMEC_POWER_OFF(1, 0, FALMEC),
77 FALMEC_SPEED_1(2, 1, FALMEC),
78 FALMEC_SPEED_2(3, 2, FALMEC),
79 FALMEC_SPEED_3(4, 3, FALMEC),
80 FALMEC_SPEED_4(5, 4, FALMEC),
81 FALMEC_TIMER_1(6, FALMEC),
82 FALMEC_TIMER_2(7, FALMEC),
83 FALMEC_TIMER_3(8, FALMEC),
84 FALMEC_TIMER_4(9, FALMEC),
85 FALMEC_LIGHT_ON(10, FALMEC),
86 FALMEC_LIGHT_OFF(11, FALMEC),
88 FT1211R_POWER(1, 0, FT1211R),
89 FT1211R_LIGHT(2, FT1211R),
90 FT1211R_SPEED_1(3, 1, FT1211R),
91 FT1211R_SPEED_2(4, 2, FT1211R),
92 FT1211R_SPEED_3(5, 3, FT1211R),
93 FT1211R_SPEED_4(6, 4, FT1211R),
94 FT1211R_SPEED_5(7, 5, FT1211R),
95 FT1211R_FORWARD_REVERSE(8, FT1211R),
96 FT1211R_TIMER_1H(9, FT1211R),
97 FT1211R_TIMER_4H(10, FT1211R),
98 FT1211R_TIMER_8H(11, FT1211R),
100 LUCCI_AIR_DC_POWER(1, LUCCI_AIR_DC),
101 LUCCI_AIR_DC_UP(2, LUCCI_AIR_DC),
102 LUCCI_AIR_DC_DOWN(3, LUCCI_AIR_DC),
103 LUCCI_AIR_DC_LIGHT(4, LUCCI_AIR_DC),
104 LUCCI_AIR_DC_REVERSE(5, LUCCI_AIR_DC),
105 LUCCI_AIR_DC_NATURAL_FLOW(6, LUCCI_AIR_DC),
106 LUCCI_AIR_DC_PAIR(7, LUCCI_AIR_DC),
108 LUCCI_AIR_DC_II_POWER_OFF(1, 0, LUCCI_AIR_DC_II),
109 LUCCI_AIR_DC_II_SPEED_1(2, 1, LUCCI_AIR_DC_II),
110 LUCCI_AIR_DC_II_SPEED_2(3, 2, LUCCI_AIR_DC_II),
111 LUCCI_AIR_DC_II_SPEED_3(4, 3, LUCCI_AIR_DC_II),
112 LUCCI_AIR_DC_II_SPEED_4(5, 4, LUCCI_AIR_DC_II),
113 LUCCI_AIR_DC_II_SPEED_5(6, 5, LUCCI_AIR_DC_II),
114 LUCCI_AIR_DC_II_SPEED_6(7, 6, LUCCI_AIR_DC_II),
115 LUCCI_AIR_DC_II_LIGHT(8, LUCCI_AIR_DC_II),
116 LUCCI_AIR_DC_II_REVERSE(9, LUCCI_AIR_DC_II),
123 NOVY_RESET_FILTER(6, NOVY);
125 private final int command;
126 private final Integer speed;
127 private final List<SubType> supportedBySubTypes;
129 Commands(int command, SubType... supportedSubType) {
130 this(command, null, supportedSubType);
133 Commands(int command, Integer speed, SubType... supportedSubType) {
134 this.command = command;
136 this.supportedBySubTypes = Arrays.asList(supportedSubType);
140 public static Commands bySpeed(SubType subType, int speed) {
141 for (Commands value : values()) {
142 if (value.supportedBySubTypes.contains(subType) && value.speed == speed) {
150 public byte toByte() {
151 return (byte) command;
154 public Integer getSpeed() {
159 public List<SubType> supportedBySubTypes() {
160 return supportedBySubTypes;
164 private static final List<SubType> GENERIC_SUB_TYPES = Arrays.asList(WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN);
166 private static final List<Commands> LIGHT_ON_COMMANDS = Arrays.asList(LIGHT, LUCCI_AIR_DC_LIGHT,
167 LUCCI_AIR_DC_II_LIGHT, FALMEC_LIGHT_ON, NOVY_LIGHT);
168 private static final List<Commands> ON_COMMANDS = Arrays.asList(HI, MED, LOW, FALMEC_SPEED_1, FALMEC_SPEED_2,
169 FALMEC_SPEED_3, FALMEC_SPEED_4, LUCCI_AIR_DC_II_SPEED_1, LUCCI_AIR_DC_II_SPEED_2, LUCCI_AIR_DC_II_SPEED_3,
170 LUCCI_AIR_DC_II_SPEED_4, LUCCI_AIR_DC_II_SPEED_5, LUCCI_AIR_DC_II_SPEED_6);
171 private static final List<Commands> OFF_COMMANDS = Arrays.asList(OFF, FALMEC_POWER_OFF, LUCCI_AIR_DC_II_POWER_OFF);
173 private SubType subType;
174 private int sensorId;
175 private Commands command;
177 public RFXComFanMessage() {
178 super(PacketType.FAN);
181 public RFXComFanMessage(byte[] data) throws RFXComException {
186 public PacketType getPacketType() {
190 case WESTINGHOUSE_7226640:
191 return PacketType.FAN;
193 return PacketType.FAN_SF01;
195 return PacketType.FAN_ITHO;
197 return PacketType.FAN_SEAV;
199 return PacketType.FAN_LUCCI_DC;
201 return PacketType.FAN_FT1211R;
203 return PacketType.FAN_FALMEC;
204 case LUCCI_AIR_DC_II:
205 return PacketType.FAN_LUCCI_DC_II;
206 case ITHO_CVE_ECO_RFT:
207 return PacketType.FAN_ITHO_CVE_ECO_RFT;
209 return PacketType.FAN_NOVY;
211 return super.getPacketType();
215 public void encodeMessage(byte[] data) throws RFXComException {
216 super.encodeMessage(data);
218 subType = fromByte(SubType.class, super.subType);
219 sensorId = (data[4] & 0xFF) << 16 | (data[5] & 0xFF) << 8 | (data[6] & 0xFF);
220 command = fromByte(Commands.class, data[7], subType);
222 signalLevel = (byte) (data[8] & 0x0F);
226 public byte[] decodeMessage() {
227 byte[] data = new byte[9];
230 data[1] = PacketType.FAN.toByte();
231 data[2] = subType.toByte();
234 data[4] = (byte) ((sensorId >> 16) & 0xFF);
235 data[5] = (byte) ((sensorId >> 8) & 0xFF);
236 data[6] = (byte) (sensorId & 0xFF);
238 data[7] = command.toByte();
240 data[8] = (byte) (signalLevel & 0x0F);
246 public String getDeviceId() {
247 return String.valueOf(sensorId);
251 public void setSubType(SubType subType) {
252 this.subType = subType;
256 public void setDeviceId(String deviceId) {
257 sensorId = Integer.parseInt(deviceId);
261 public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
262 return ByteEnumUtil.convertSubType(SubType.class, subType);
266 public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException {
268 case CHANNEL_FAN_LIGHT:
269 return handleLightChannel();
271 case CHANNEL_FAN_SPEED:
272 return handleFanSpeedChannel();
274 case CHANNEL_COMMAND:
275 return handleCommandChannel();
277 case CHANNEL_COMMAND_STRING:
278 return handleCommandStringChannel();
281 return super.convertToState(channelId, deviceState);
285 private State handleCommandStringChannel() {
286 if (command == null) {
287 return UnDefType.UNDEF;
289 return StringType.valueOf(command.toString().replace(subType.name() + "_", ""));
292 private State handleLightChannel() {
293 if (LIGHT_ON_COMMANDS.contains(command)) {
295 } else if (command == Commands.FALMEC_LIGHT_OFF) {
296 return OnOffType.OFF;
298 return UnDefType.UNDEF;
302 private State handleFanSpeedChannel() {
308 return StringType.valueOf(command.toString());
310 case FALMEC_POWER_OFF:
316 case FT1211R_SPEED_1:
317 case FT1211R_SPEED_2:
318 case FT1211R_SPEED_3:
319 case FT1211R_SPEED_4:
320 case FT1211R_SPEED_5:
321 case LUCCI_AIR_DC_II_POWER_OFF:
322 case LUCCI_AIR_DC_II_SPEED_1:
323 case LUCCI_AIR_DC_II_SPEED_2:
324 case LUCCI_AIR_DC_II_SPEED_3:
325 case LUCCI_AIR_DC_II_SPEED_4:
326 case LUCCI_AIR_DC_II_SPEED_5:
327 case LUCCI_AIR_DC_II_SPEED_6:
328 return new DecimalType(command.getSpeed());
330 case LUCCI_AIR_DC_DOWN:
332 return UpDownType.DOWN;
334 case LUCCI_AIR_DC_UP:
336 return UpDownType.UP;
343 private State handleCommandChannel() {
344 if (ON_COMMANDS.contains(command)) {
346 } else if (OFF_COMMANDS.contains(command)) {
347 return OnOffType.OFF;
354 public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
356 case CHANNEL_COMMAND:
357 command = handleCommand(channelId, type);
360 case CHANNEL_FAN_SPEED:
361 command = handleFanSpeedCommand(channelId, type);
364 case CHANNEL_FAN_LIGHT:
365 command = handleFanLightCommand(channelId, type);
368 case CHANNEL_COMMAND_STRING:
369 command = handleCommandString(channelId, type);
373 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
377 private Commands handleCommandString(String channelId, Type type) throws RFXComUnsupportedChannelException {
378 if (type instanceof StringType) {
379 String stringCommand = type.toString();
380 switch (stringCommand) {
397 return Commands.valueOf(subType.name() + "_" + stringCommand);
400 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
403 private Commands handleCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
404 if (type instanceof OnOffType) {
405 if (GENERIC_SUB_TYPES.contains(subType)) {
406 return (type == OnOffType.ON ? Commands.MED : Commands.OFF);
407 } else if (subType == FALMEC) {
408 return (type == OnOffType.ON ? Commands.FALMEC_SPEED_2 : Commands.FALMEC_POWER_OFF);
409 } else if (subType == LUCCI_AIR_DC_II) {
410 return (type == OnOffType.ON ? LUCCI_AIR_DC_II_SPEED_3 : LUCCI_AIR_DC_II_POWER_OFF);
413 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
416 private Commands handleFanSpeedCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
417 if (type instanceof StringType) {
418 String stringCommand = type.toString();
419 switch (stringCommand) {
424 return Commands.valueOf(stringCommand);
426 } else if (type instanceof DecimalType) {
427 Commands speedCommand = Commands.bySpeed(subType, ((DecimalType) type).intValue());
428 if (speedCommand != null) {
431 } else if (type instanceof UpDownType && subType == LUCCI_AIR_DC) {
432 if (UpDownType.UP == type) {
433 return Commands.LUCCI_AIR_DC_UP;
435 return Commands.LUCCI_AIR_DC_DOWN;
437 } else if (type instanceof UpDownType && subType == NOVY) {
438 if (UpDownType.UP == type) {
439 return Commands.NOVY_UP;
441 return Commands.NOVY_DOWN;
444 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
447 private Commands handleFanLightCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
448 if (type == OnOffType.ON) {
452 case WESTINGHOUSE_7226640:
456 return FALMEC_LIGHT_ON;
458 case LUCCI_AIR_DC_II:
459 return LUCCI_AIR_DC_II_LIGHT;
461 } else if (type == OnOffType.OFF && subType == FALMEC) {
462 return Commands.FALMEC_LIGHT_OFF;
464 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);