]> git.basschouten.com Git - openhab-addons.git/blob
feecca8979920616acc51c72adb75c366c2e47b4
[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.rfxcom.internal.messages;
14
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.*;
19
20 import java.util.Arrays;
21 import java.util.List;
22
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;
35
36 /**
37  * RFXCOM data class for fan message.
38  *
39  * @author Martin van Wingerden - initial contribution
40  */
41 public class RFXComFanMessage extends RFXComDeviceMessageImpl<RFXComFanMessage.SubType> {
42
43     public enum SubType implements ByteEnumWrapper {
44         SF01(0x00),
45         CVE_RFT(0x01),
46         LUCCI_AIR_FAN(0x02),
47         SEAV_TXS4(0x03),
48         WESTINGHOUSE_7226640(0x04),
49         LUCCI_AIR_DC(0x05),
50         CASAFAN(0x06),
51         FT1211R(0x07),
52         FALMEC(0x08),
53         LUCCI_AIR_DC_II(0x09),
54         ITHO_CVE_ECO_RFT(0x0A),
55         NOVY(0x0B);
56
57         private final int subType;
58
59         SubType(int subType) {
60             this.subType = subType;
61         }
62
63         @Override
64         public byte toByte() {
65             return (byte) subType;
66         }
67     }
68
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),
75
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),
87
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),
99
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),
107
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),
117
118         NOVY_POWER(1, NOVY),
119         NOVY_UP(2, NOVY),
120         NOVY_DOWN(3, NOVY),
121         NOVY_LIGHT(4, NOVY),
122         NOVY_LEARN(5, NOVY),
123         NOVY_RESET_FILTER(6, NOVY);
124
125         private final int command;
126         private final Integer speed;
127         private final List<SubType> supportedBySubTypes;
128
129         Commands(int command, SubType... supportedSubType) {
130             this(command, null, supportedSubType);
131         }
132
133         Commands(int command, Integer speed, SubType... supportedSubType) {
134             this.command = command;
135             this.speed = speed;
136             this.supportedBySubTypes = Arrays.asList(supportedSubType);
137         }
138
139         @Nullable
140         public static Commands bySpeed(SubType subType, int speed) {
141             for (Commands value : values()) {
142                 if (value.supportedBySubTypes.contains(subType) && value.speed == speed) {
143                     return value;
144                 }
145             }
146             return null;
147         }
148
149         @Override
150         public byte toByte() {
151             return (byte) command;
152         }
153
154         public Integer getSpeed() {
155             return speed;
156         }
157
158         @Override
159         public List<SubType> supportedBySubTypes() {
160             return supportedBySubTypes;
161         }
162     }
163
164     private static final List<SubType> GENERIC_SUB_TYPES = Arrays.asList(WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN);
165
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);
172
173     private SubType subType;
174     private int sensorId;
175     private Commands command;
176
177     public RFXComFanMessage() {
178         super(PacketType.FAN);
179     }
180
181     public RFXComFanMessage(byte[] data) throws RFXComException {
182         encodeMessage(data);
183     }
184
185     @Override
186     public PacketType getPacketType() {
187         switch (subType) {
188             case LUCCI_AIR_FAN:
189             case CASAFAN:
190             case WESTINGHOUSE_7226640:
191                 return PacketType.FAN;
192             case SF01:
193                 return PacketType.FAN_SF01;
194             case CVE_RFT:
195                 return PacketType.FAN_ITHO;
196             case SEAV_TXS4:
197                 return PacketType.FAN_SEAV;
198             case LUCCI_AIR_DC:
199                 return PacketType.FAN_LUCCI_DC;
200             case FT1211R:
201                 return PacketType.FAN_FT1211R;
202             case FALMEC:
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;
208             case NOVY:
209                 return PacketType.FAN_NOVY;
210         }
211         return super.getPacketType();
212     }
213
214     @Override
215     public void encodeMessage(byte[] data) throws RFXComException {
216         super.encodeMessage(data);
217
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);
221
222         signalLevel = (byte) (data[8] & 0x0F);
223     }
224
225     @Override
226     public byte[] decodeMessage() {
227         byte[] data = new byte[9];
228
229         data[0] = 0x08;
230         data[1] = PacketType.FAN.toByte();
231         data[2] = subType.toByte();
232         data[3] = seqNbr;
233
234         data[4] = (byte) ((sensorId >> 16) & 0xFF);
235         data[5] = (byte) ((sensorId >> 8) & 0xFF);
236         data[6] = (byte) (sensorId & 0xFF);
237
238         data[7] = command.toByte();
239
240         data[8] = (byte) (signalLevel & 0x0F);
241
242         return data;
243     }
244
245     @Override
246     public String getDeviceId() {
247         return String.valueOf(sensorId);
248     }
249
250     @Override
251     public void setSubType(SubType subType) {
252         this.subType = subType;
253     }
254
255     @Override
256     public void setDeviceId(String deviceId) {
257         sensorId = Integer.parseInt(deviceId);
258     }
259
260     @Override
261     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
262         return ByteEnumUtil.convertSubType(SubType.class, subType);
263     }
264
265     @Override
266     public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException {
267         switch (channelId) {
268             case CHANNEL_FAN_LIGHT:
269                 return handleLightChannel();
270
271             case CHANNEL_FAN_SPEED:
272                 return handleFanSpeedChannel();
273
274             case CHANNEL_COMMAND:
275                 return handleCommandChannel();
276
277             case CHANNEL_COMMAND_STRING:
278                 return handleCommandStringChannel();
279
280             default:
281                 return super.convertToState(channelId, deviceState);
282         }
283     }
284
285     private State handleCommandStringChannel() {
286         if (command == null) {
287             return UnDefType.UNDEF;
288         }
289         return StringType.valueOf(command.toString().replace(subType.name() + "_", ""));
290     }
291
292     private State handleLightChannel() {
293         if (LIGHT_ON_COMMANDS.contains(command)) {
294             return OnOffType.ON;
295         } else if (command == Commands.FALMEC_LIGHT_OFF) {
296             return OnOffType.OFF;
297         } else {
298             return UnDefType.UNDEF;
299         }
300     }
301
302     private State handleFanSpeedChannel() {
303         switch (command) {
304             case HI:
305             case MED:
306             case LOW:
307             case OFF:
308                 return StringType.valueOf(command.toString());
309
310             case FALMEC_POWER_OFF:
311             case FALMEC_SPEED_1:
312             case FALMEC_SPEED_2:
313             case FALMEC_SPEED_3:
314             case FALMEC_SPEED_4:
315             case FT1211R_POWER:
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());
329
330             case LUCCI_AIR_DC_DOWN:
331             case NOVY_DOWN:
332                 return UpDownType.DOWN;
333
334             case LUCCI_AIR_DC_UP:
335             case NOVY_UP:
336                 return UpDownType.UP;
337
338             default:
339                 return null;
340         }
341     }
342
343     private State handleCommandChannel() {
344         if (ON_COMMANDS.contains(command)) {
345             return OnOffType.ON;
346         } else if (OFF_COMMANDS.contains(command)) {
347             return OnOffType.OFF;
348         } else {
349             return null;
350         }
351     }
352
353     @Override
354     public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
355         switch (channelId) {
356             case CHANNEL_COMMAND:
357                 command = handleCommand(channelId, type);
358                 break;
359
360             case CHANNEL_FAN_SPEED:
361                 command = handleFanSpeedCommand(channelId, type);
362                 break;
363
364             case CHANNEL_FAN_LIGHT:
365                 command = handleFanLightCommand(channelId, type);
366                 break;
367
368             case CHANNEL_COMMAND_STRING:
369                 command = handleCommandString(channelId, type);
370                 break;
371
372             default:
373                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
374         }
375     }
376
377     private Commands handleCommandString(String channelId, Type type) throws RFXComUnsupportedChannelException {
378         if (type instanceof StringType) {
379             String stringCommand = type.toString();
380             switch (stringCommand) {
381                 case "POWER":
382                 case "POWER_OFF":
383                 case "UP":
384                 case "DOWN":
385                 case "LEARN":
386                 case "LIGHT":
387                 case "REVERSE":
388                 case "NATURAL_FLOW":
389                 case "PAIR":
390                 case "RESET_FILTER":
391                 case "SPEED_1":
392                 case "SPEED_2":
393                 case "SPEED_3":
394                 case "SPEED_4":
395                 case "SPEED_5":
396                 case "SPEED_6":
397                     return Commands.valueOf(subType.name() + "_" + stringCommand);
398             }
399         }
400         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
401     }
402
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);
411             }
412         }
413         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
414     }
415
416     private Commands handleFanSpeedCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
417         if (type instanceof StringType) {
418             String stringCommand = type.toString();
419             switch (stringCommand) {
420                 case "HI":
421                 case "MED":
422                 case "LOW":
423                 case "OFF":
424                     return Commands.valueOf(stringCommand);
425             }
426         } else if (type instanceof DecimalType) {
427             Commands speedCommand = Commands.bySpeed(subType, ((DecimalType) type).intValue());
428             if (speedCommand != null) {
429                 return speedCommand;
430             }
431         } else if (type instanceof UpDownType && subType == LUCCI_AIR_DC) {
432             if (UpDownType.UP == type) {
433                 return Commands.LUCCI_AIR_DC_UP;
434             } else {
435                 return Commands.LUCCI_AIR_DC_DOWN;
436             }
437         } else if (type instanceof UpDownType && subType == NOVY) {
438             if (UpDownType.UP == type) {
439                 return Commands.NOVY_UP;
440             } else {
441                 return Commands.NOVY_DOWN;
442             }
443         }
444         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
445     }
446
447     private Commands handleFanLightCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
448         if (type == OnOffType.ON) {
449             switch (subType) {
450                 case LUCCI_AIR_FAN:
451                 case CASAFAN:
452                 case WESTINGHOUSE_7226640:
453                     return LIGHT;
454
455                 case FALMEC:
456                     return FALMEC_LIGHT_ON;
457
458                 case LUCCI_AIR_DC_II:
459                     return LUCCI_AIR_DC_II_LIGHT;
460             }
461         } else if (type == OnOffType.OFF && subType == FALMEC) {
462             return Commands.FALMEC_LIGHT_OFF;
463         }
464         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
465     }
466 }