2 * Copyright (c) 2010-2020 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(4),
55 private final int subType;
57 SubType(int subType) {
58 this.subType = subType;
62 public byte toByte() {
63 return (byte) subType;
67 public enum Commands implements ByteEnumWrapperWithSupportedSubTypes<SubType> {
68 HI(1, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
69 MED(2, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
70 LOW(3, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
71 OFF(4, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
72 LIGHT(5, WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN),
74 FALMEC_POWER_OFF(1, 0, FALMEC),
75 FALMEC_SPEED_1(2, 1, FALMEC),
76 FALMEC_SPEED_2(3, 2, FALMEC),
77 FALMEC_SPEED_3(4, 3, FALMEC),
78 FALMEC_SPEED_4(5, 4, FALMEC),
79 FALMEC_TIMER_1(6, FALMEC),
80 FALMEC_TIMER_2(7, FALMEC),
81 FALMEC_TIMER_3(8, FALMEC),
82 FALMEC_TIMER_4(9, FALMEC),
83 FALMEC_LIGHT_ON(10, FALMEC),
84 FALMEC_LIGHT_OFF(11, FALMEC),
86 FT1211R_POWER(1, 0, FT1211R),
87 FT1211R_LIGHT(2, FT1211R),
88 FT1211R_SPEED_1(3, 1, FT1211R),
89 FT1211R_SPEED_2(4, 2, FT1211R),
90 FT1211R_SPEED_3(5, 3, FT1211R),
91 FT1211R_SPEED_4(6, 4, FT1211R),
92 FT1211R_SPEED_5(7, 5, FT1211R),
93 FT1211R_FORWARD_REVERSE(8, FT1211R),
94 FT1211R_TIMER_1H(9, FT1211R),
95 FT1211R_TIMER_4H(10, FT1211R),
96 FT1211R_TIMER_8H(11, FT1211R),
98 LUCCI_AIR_DC_POWER(1, LUCCI_AIR_DC),
99 LUCCI_AIR_DC_UP(2, LUCCI_AIR_DC),
100 LUCCI_AIR_DC_DOWN(3, LUCCI_AIR_DC),
101 LUCCI_AIR_DC_LIGHT(4, LUCCI_AIR_DC),
102 LUCCI_AIR_DC_REVERSE(5, LUCCI_AIR_DC),
103 LUCCI_AIR_DC_NATURAL_FLOW(6, LUCCI_AIR_DC),
104 LUCCI_AIR_DC_PAIR(7, LUCCI_AIR_DC),
106 LUCCI_AIR_DC_II_POWER_OFF(1, 0, LUCCI_AIR_DC_II),
107 LUCCI_AIR_DC_II_SPEED_1(2, 1, LUCCI_AIR_DC_II),
108 LUCCI_AIR_DC_II_SPEED_2(3, 2, LUCCI_AIR_DC_II),
109 LUCCI_AIR_DC_II_SPEED_3(4, 3, LUCCI_AIR_DC_II),
110 LUCCI_AIR_DC_II_SPEED_4(5, 4, LUCCI_AIR_DC_II),
111 LUCCI_AIR_DC_II_SPEED_5(6, 5, LUCCI_AIR_DC_II),
112 LUCCI_AIR_DC_II_SPEED_6(7, 6, LUCCI_AIR_DC_II),
113 LUCCI_AIR_DC_II_LIGHT(8, LUCCI_AIR_DC_II),
114 LUCCI_AIR_DC_II_REVERSE(9, LUCCI_AIR_DC_II);
116 private final int command;
117 private final Integer speed;
118 private final List<SubType> supportedBySubTypes;
120 Commands(int command, SubType... supportedSubType) {
121 this(command, null, supportedSubType);
124 Commands(int command, Integer speed, SubType... supportedSubType) {
125 this.command = command;
127 this.supportedBySubTypes = Arrays.asList(supportedSubType);
131 public static Commands bySpeed(SubType subType, int speed) {
132 for (Commands value : values()) {
133 if (value.supportedBySubTypes.contains(subType) && value.speed == speed) {
141 public byte toByte() {
142 return (byte) command;
145 public Integer getSpeed() {
150 public List<SubType> supportedBySubTypes() {
151 return supportedBySubTypes;
155 private static final List<SubType> GENERIC_SUB_TYPES = Arrays.asList(WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN);
157 private static final List<Commands> LIGHT_ON_COMMANDS = Arrays.asList(LIGHT, LUCCI_AIR_DC_LIGHT,
158 LUCCI_AIR_DC_II_LIGHT, FALMEC_LIGHT_ON);
159 private static final List<Commands> ON_COMMANDS = Arrays.asList(Commands.HI, MED, LOW, FALMEC_SPEED_1,
160 FALMEC_SPEED_2, FALMEC_SPEED_3, FALMEC_SPEED_4, LUCCI_AIR_DC_II_SPEED_1, LUCCI_AIR_DC_II_SPEED_2,
161 LUCCI_AIR_DC_II_SPEED_3, LUCCI_AIR_DC_II_SPEED_4, LUCCI_AIR_DC_II_SPEED_5, LUCCI_AIR_DC_II_SPEED_6);
162 private static final List<Commands> OFF_COMMANDS = Arrays.asList(OFF, FALMEC_POWER_OFF, LUCCI_AIR_DC_II_POWER_OFF);
164 private SubType subType;
165 private int sensorId;
166 private Commands command;
168 public RFXComFanMessage() {
169 super(PacketType.FAN);
172 public RFXComFanMessage(byte[] data) throws RFXComException {
177 public PacketType getPacketType() {
181 case WESTINGHOUSE_7226640:
182 return PacketType.FAN;
184 return PacketType.FAN_SF01;
186 return PacketType.FAN_ITHO;
188 return PacketType.FAN_SEAV;
190 return PacketType.FAN_LUCCI_DC;
192 return PacketType.FAN_FT1211R;
194 return PacketType.FAN_FALMEC;
195 case LUCCI_AIR_DC_II:
196 return PacketType.FAN_LUCCI_DC_II;
198 return super.getPacketType();
202 public void encodeMessage(byte[] data) throws RFXComException {
203 super.encodeMessage(data);
205 subType = fromByte(SubType.class, super.subType);
206 sensorId = (data[4] & 0xFF) << 16 | (data[5] & 0xFF) << 8 | (data[6] & 0xFF);
207 command = fromByte(Commands.class, data[7], subType);
209 signalLevel = (byte) (data[8] & 0x0F);
213 public byte[] decodeMessage() {
214 byte[] data = new byte[9];
217 data[1] = PacketType.FAN.toByte();
218 data[2] = subType.toByte();
221 data[4] = (byte) ((sensorId >> 16) & 0xFF);
222 data[5] = (byte) ((sensorId >> 8) & 0xFF);
223 data[6] = (byte) (sensorId & 0xFF);
225 data[7] = command.toByte();
227 data[8] = (byte) (signalLevel & 0x0F);
233 public String getDeviceId() {
234 return String.valueOf(sensorId);
238 public void setSubType(SubType subType) {
239 this.subType = subType;
243 public void setDeviceId(String deviceId) {
244 sensorId = Integer.parseInt(deviceId);
248 public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
249 return ByteEnumUtil.convertSubType(SubType.class, subType);
253 public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException {
255 case CHANNEL_FAN_LIGHT:
256 return handleLightChannel();
258 case CHANNEL_FAN_SPEED:
259 return handleFanSpeedChannel();
261 case CHANNEL_COMMAND:
262 return handleCommandChannel();
264 case CHANNEL_COMMAND_STRING:
265 return handleCommandStringChannel();
268 return super.convertToState(channelId, deviceState);
272 private State handleCommandStringChannel() {
273 if (command == null) {
274 return UnDefType.UNDEF;
276 return StringType.valueOf(command.toString().replace(subType.name() + "_", ""));
279 private State handleLightChannel() {
280 if (LIGHT_ON_COMMANDS.contains(command)) {
282 } else if (command == Commands.FALMEC_LIGHT_OFF) {
283 return OnOffType.OFF;
285 return UnDefType.UNDEF;
289 private State handleFanSpeedChannel() {
295 return StringType.valueOf(command.toString());
297 case FALMEC_POWER_OFF:
303 case FT1211R_SPEED_1:
304 case FT1211R_SPEED_2:
305 case FT1211R_SPEED_3:
306 case FT1211R_SPEED_4:
307 case FT1211R_SPEED_5:
308 case LUCCI_AIR_DC_II_POWER_OFF:
309 case LUCCI_AIR_DC_II_SPEED_1:
310 case LUCCI_AIR_DC_II_SPEED_2:
311 case LUCCI_AIR_DC_II_SPEED_3:
312 case LUCCI_AIR_DC_II_SPEED_4:
313 case LUCCI_AIR_DC_II_SPEED_5:
314 case LUCCI_AIR_DC_II_SPEED_6:
315 return new DecimalType(command.getSpeed());
317 case LUCCI_AIR_DC_DOWN:
318 return UpDownType.DOWN;
320 case LUCCI_AIR_DC_UP:
321 return UpDownType.UP;
328 private State handleCommandChannel() {
329 if (ON_COMMANDS.contains(command)) {
331 } else if (OFF_COMMANDS.contains(command)) {
332 return OnOffType.OFF;
339 public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
341 case CHANNEL_COMMAND:
342 command = handleCommand(channelId, type);
345 case CHANNEL_FAN_SPEED:
346 command = handleFanSpeedCommand(channelId, type);
349 case CHANNEL_FAN_LIGHT:
350 command = handleFanLightCommand(channelId, type);
353 case CHANNEL_COMMAND_STRING:
354 command = handleCommandString(channelId, type);
358 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
362 private Commands handleCommandString(String channelId, Type type) throws RFXComUnsupportedChannelException {
363 if (type instanceof StringType) {
364 String stringCommand = type.toString();
365 switch (stringCommand) {
380 return Commands.valueOf(subType.name() + "_" + stringCommand);
383 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
386 private Commands handleCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
387 if (type instanceof OnOffType) {
388 if (GENERIC_SUB_TYPES.contains(subType)) {
389 return (type == OnOffType.ON ? Commands.MED : Commands.OFF);
390 } else if (subType == FALMEC) {
391 return (type == OnOffType.ON ? Commands.FALMEC_SPEED_2 : Commands.FALMEC_POWER_OFF);
392 } else if (subType == LUCCI_AIR_DC_II) {
393 return (type == OnOffType.ON ? LUCCI_AIR_DC_II_SPEED_3 : LUCCI_AIR_DC_II_POWER_OFF);
396 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
399 private Commands handleFanSpeedCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
400 if (type instanceof StringType) {
401 String stringCommand = type.toString();
402 switch (stringCommand) {
407 return Commands.valueOf(stringCommand);
409 } else if (type instanceof DecimalType) {
410 Commands speedCommand = Commands.bySpeed(subType, ((DecimalType) type).intValue());
411 if (speedCommand != null) {
414 } else if (type instanceof UpDownType && subType == LUCCI_AIR_DC) {
415 if (UpDownType.UP == type) {
416 return Commands.LUCCI_AIR_DC_UP;
418 return Commands.LUCCI_AIR_DC_DOWN;
421 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
424 private Commands handleFanLightCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
425 if (type == OnOffType.ON) {
429 case WESTINGHOUSE_7226640:
434 return FALMEC_LIGHT_ON;
436 case LUCCI_AIR_DC_II:
437 return LUCCI_AIR_DC_II_LIGHT;
439 } else if (type == OnOffType.OFF && subType == FALMEC) {
440 return Commands.FALMEC_LIGHT_OFF;
442 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);