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