]> git.basschouten.com Git - openhab-addons.git/blob
fcde1589c8372a7aea21fac53c8b86d609354a28
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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         LUCCI_AIR_DC_SPEED_1(8, 1, LUCCI_AIR_DC),
110         LUCCI_AIR_DC_SPEED_2(9, 2, LUCCI_AIR_DC),
111         LUCCI_AIR_DC_SPEED_3(10, 3, LUCCI_AIR_DC),
112         LUCCI_AIR_DC_SPEED_4(11, 4, LUCCI_AIR_DC),
113         LUCCI_AIR_DC_SPEED_5(12, 5, LUCCI_AIR_DC),
114         LUCCI_AIR_DC_SPEED_6(13, 6, LUCCI_AIR_DC),
115
116         LUCCI_AIR_DC_II_POWER_OFF(1, 0, LUCCI_AIR_DC_II),
117         LUCCI_AIR_DC_II_SPEED_1(2, 1, LUCCI_AIR_DC_II),
118         LUCCI_AIR_DC_II_SPEED_2(3, 2, LUCCI_AIR_DC_II),
119         LUCCI_AIR_DC_II_SPEED_3(4, 3, LUCCI_AIR_DC_II),
120         LUCCI_AIR_DC_II_SPEED_4(5, 4, LUCCI_AIR_DC_II),
121         LUCCI_AIR_DC_II_SPEED_5(6, 5, LUCCI_AIR_DC_II),
122         LUCCI_AIR_DC_II_SPEED_6(7, 6, LUCCI_AIR_DC_II),
123         LUCCI_AIR_DC_II_LIGHT(8, LUCCI_AIR_DC_II),
124         LUCCI_AIR_DC_II_REVERSE(9, LUCCI_AIR_DC_II),
125
126         NOVY_POWER(1, NOVY),
127         NOVY_UP(2, NOVY),
128         NOVY_DOWN(3, NOVY),
129         NOVY_LIGHT(4, NOVY),
130         NOVY_LEARN(5, NOVY),
131         NOVY_RESET_FILTER(6, NOVY),
132         NOVY_MOOD_LIGHT(7, NOVY);
133
134         private final int command;
135         private final Integer speed;
136         private final List<SubType> supportedBySubTypes;
137
138         Commands(int command, SubType... supportedSubType) {
139             this(command, null, supportedSubType);
140         }
141
142         Commands(int command, Integer speed, SubType... supportedSubType) {
143             this.command = command;
144             this.speed = speed;
145             this.supportedBySubTypes = Arrays.asList(supportedSubType);
146         }
147
148         @Nullable
149         public static Commands bySpeed(SubType subType, int speed) {
150             for (Commands value : values()) {
151                 if (value.supportedBySubTypes.contains(subType) && value.speed != null && value.speed == speed) {
152                     return value;
153                 }
154             }
155             return null;
156         }
157
158         @Override
159         public byte toByte() {
160             return (byte) command;
161         }
162
163         public Integer getSpeed() {
164             return speed;
165         }
166
167         @Override
168         public List<SubType> supportedBySubTypes() {
169             return supportedBySubTypes;
170         }
171     }
172
173     private static final List<SubType> GENERIC_SUB_TYPES = Arrays.asList(WESTINGHOUSE_7226640, CASAFAN, LUCCI_AIR_FAN);
174
175     private static final List<Commands> LIGHT_ON_COMMANDS = Arrays.asList(LIGHT, LUCCI_AIR_DC_LIGHT,
176             LUCCI_AIR_DC_II_LIGHT, FALMEC_LIGHT_ON, NOVY_LIGHT);
177     private static final List<Commands> ON_COMMANDS = Arrays.asList(HI, MED, LOW, FALMEC_SPEED_1, FALMEC_SPEED_2,
178             FALMEC_SPEED_3, FALMEC_SPEED_4, LUCCI_AIR_DC_II_SPEED_1, LUCCI_AIR_DC_II_SPEED_2, LUCCI_AIR_DC_II_SPEED_3,
179             LUCCI_AIR_DC_II_SPEED_4, LUCCI_AIR_DC_II_SPEED_5, LUCCI_AIR_DC_II_SPEED_6, LUCCI_AIR_DC_SPEED_1,
180             LUCCI_AIR_DC_SPEED_2, LUCCI_AIR_DC_SPEED_3, LUCCI_AIR_DC_SPEED_4, LUCCI_AIR_DC_SPEED_5,
181             LUCCI_AIR_DC_SPEED_6);
182     private static final List<Commands> OFF_COMMANDS = Arrays.asList(OFF, FALMEC_POWER_OFF, LUCCI_AIR_DC_II_POWER_OFF);
183
184     private SubType subType;
185     private int sensorId;
186     private Commands command;
187
188     public RFXComFanMessage() {
189         super(PacketType.FAN);
190     }
191
192     public RFXComFanMessage(byte[] data) throws RFXComException {
193         encodeMessage(data);
194     }
195
196     @Override
197     public PacketType getPacketType() {
198         switch (subType) {
199             case LUCCI_AIR_FAN:
200             case CASAFAN:
201             case WESTINGHOUSE_7226640:
202                 return PacketType.FAN;
203             case SF01:
204                 return PacketType.FAN_SF01;
205             case CVE_RFT:
206                 return PacketType.FAN_ITHO;
207             case SEAV_TXS4:
208                 return PacketType.FAN_SEAV;
209             case LUCCI_AIR_DC:
210                 return PacketType.FAN_LUCCI_DC;
211             case FT1211R:
212                 return PacketType.FAN_FT1211R;
213             case FALMEC:
214                 return PacketType.FAN_FALMEC;
215             case LUCCI_AIR_DC_II:
216                 return PacketType.FAN_LUCCI_DC_II;
217             case ITHO_CVE_ECO_RFT:
218                 return PacketType.FAN_ITHO_CVE_ECO_RFT;
219             case NOVY:
220                 return PacketType.FAN_NOVY;
221         }
222         return super.getPacketType();
223     }
224
225     @Override
226     public void encodeMessage(byte[] data) throws RFXComException {
227         super.encodeMessage(data);
228
229         subType = fromByte(SubType.class, super.subType);
230         sensorId = (data[4] & 0xFF) << 16 | (data[5] & 0xFF) << 8 | (data[6] & 0xFF);
231         command = fromByte(Commands.class, data[7], subType);
232
233         signalLevel = (byte) (data[8] & 0x0F);
234     }
235
236     @Override
237     public byte[] decodeMessage() {
238         byte[] data = new byte[9];
239
240         data[0] = 0x08;
241         data[1] = PacketType.FAN.toByte();
242         data[2] = subType.toByte();
243         data[3] = seqNbr;
244
245         data[4] = (byte) ((sensorId >> 16) & 0xFF);
246         data[5] = (byte) ((sensorId >> 8) & 0xFF);
247         data[6] = (byte) (sensorId & 0xFF);
248
249         data[7] = command.toByte();
250
251         data[8] = (byte) (signalLevel & 0x0F);
252
253         return data;
254     }
255
256     @Override
257     public String getDeviceId() {
258         return String.valueOf(sensorId);
259     }
260
261     @Override
262     public void setSubType(SubType subType) {
263         this.subType = subType;
264     }
265
266     @Override
267     public void setDeviceId(String deviceId) {
268         sensorId = Integer.parseInt(deviceId);
269     }
270
271     @Override
272     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
273         return ByteEnumUtil.convertSubType(SubType.class, subType);
274     }
275
276     @Override
277     public State convertToState(String channelId, RFXComDeviceConfiguration config, DeviceState deviceState)
278             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
279         switch (channelId) {
280             case CHANNEL_FAN_LIGHT:
281                 return handleLightChannel();
282
283             case CHANNEL_FAN_SPEED:
284                 return handleFanSpeedChannel();
285
286             case CHANNEL_COMMAND:
287                 return handleCommandChannel();
288
289             case CHANNEL_COMMAND_STRING:
290                 return handleCommandStringChannel();
291
292             default:
293                 return super.convertToState(channelId, config, deviceState);
294         }
295     }
296
297     private State handleCommandStringChannel() {
298         if (command == null) {
299             return UnDefType.UNDEF;
300         }
301         return StringType.valueOf(command.toString().replace(subType.name() + "_", ""));
302     }
303
304     private State handleLightChannel() {
305         if (LIGHT_ON_COMMANDS.contains(command)) {
306             return OnOffType.ON;
307         } else if (command == Commands.FALMEC_LIGHT_OFF) {
308             return OnOffType.OFF;
309         } else {
310             return UnDefType.UNDEF;
311         }
312     }
313
314     private State handleFanSpeedChannel() {
315         switch (command) {
316             case HI:
317             case MED:
318             case LOW:
319             case OFF:
320                 return StringType.valueOf(command.toString());
321
322             case FALMEC_POWER_OFF:
323             case FALMEC_SPEED_1:
324             case FALMEC_SPEED_2:
325             case FALMEC_SPEED_3:
326             case FALMEC_SPEED_4:
327             case FT1211R_POWER:
328             case FT1211R_SPEED_1:
329             case FT1211R_SPEED_2:
330             case FT1211R_SPEED_3:
331             case FT1211R_SPEED_4:
332             case FT1211R_SPEED_5:
333             case LUCCI_AIR_DC_SPEED_1:
334             case LUCCI_AIR_DC_SPEED_2:
335             case LUCCI_AIR_DC_SPEED_3:
336             case LUCCI_AIR_DC_SPEED_4:
337             case LUCCI_AIR_DC_SPEED_5:
338             case LUCCI_AIR_DC_SPEED_6:
339             case LUCCI_AIR_DC_II_POWER_OFF:
340             case LUCCI_AIR_DC_II_SPEED_1:
341             case LUCCI_AIR_DC_II_SPEED_2:
342             case LUCCI_AIR_DC_II_SPEED_3:
343             case LUCCI_AIR_DC_II_SPEED_4:
344             case LUCCI_AIR_DC_II_SPEED_5:
345             case LUCCI_AIR_DC_II_SPEED_6:
346                 return new DecimalType(command.getSpeed());
347
348             case LUCCI_AIR_DC_DOWN:
349             case NOVY_DOWN:
350                 return UpDownType.DOWN;
351
352             case LUCCI_AIR_DC_UP:
353             case NOVY_UP:
354                 return UpDownType.UP;
355
356             default:
357                 return null;
358         }
359     }
360
361     private State handleCommandChannel() {
362         if (ON_COMMANDS.contains(command)) {
363             return OnOffType.ON;
364         } else if (OFF_COMMANDS.contains(command)) {
365             return OnOffType.OFF;
366         } else {
367             return null;
368         }
369     }
370
371     @Override
372     public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
373         switch (channelId) {
374             case CHANNEL_COMMAND:
375                 command = handleCommand(channelId, type);
376                 break;
377
378             case CHANNEL_FAN_SPEED:
379                 command = handleFanSpeedCommand(channelId, type);
380                 break;
381
382             case CHANNEL_FAN_LIGHT:
383                 command = handleFanLightCommand(channelId, type);
384                 break;
385
386             case CHANNEL_COMMAND_STRING:
387                 command = handleCommandString(channelId, type);
388                 break;
389
390             default:
391                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
392         }
393     }
394
395     private Commands handleCommandString(String channelId, Type type) throws RFXComUnsupportedChannelException {
396         if (type instanceof StringType) {
397             String stringCommand = type.toString();
398             switch (stringCommand) {
399                 case "POWER":
400                 case "POWER_OFF":
401                 case "UP":
402                 case "DOWN":
403                 case "LEARN":
404                 case "LIGHT":
405                 case "REVERSE":
406                 case "NATURAL_FLOW":
407                 case "PAIR":
408                 case "RESET_FILTER":
409                 case "MOOD_LIGHT":
410                 case "SPEED_1":
411                 case "SPEED_2":
412                 case "SPEED_3":
413                 case "SPEED_4":
414                 case "SPEED_5":
415                 case "SPEED_6":
416                     return Commands.valueOf(subType.name() + "_" + stringCommand);
417             }
418         }
419         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
420     }
421
422     private Commands handleCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
423         if (type instanceof OnOffType) {
424             if (GENERIC_SUB_TYPES.contains(subType)) {
425                 return (type == OnOffType.ON ? Commands.MED : Commands.OFF);
426             } else if (subType == FALMEC) {
427                 return (type == OnOffType.ON ? Commands.FALMEC_SPEED_2 : Commands.FALMEC_POWER_OFF);
428             } else if (subType == LUCCI_AIR_DC_II) {
429                 return (type == OnOffType.ON ? LUCCI_AIR_DC_II_SPEED_3 : LUCCI_AIR_DC_II_POWER_OFF);
430             }
431         }
432         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
433     }
434
435     private Commands handleFanSpeedCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
436         if (type instanceof StringType) {
437             String stringCommand = type.toString();
438             switch (stringCommand) {
439                 case "HI":
440                 case "MED":
441                 case "LOW":
442                 case "OFF":
443                     return Commands.valueOf(stringCommand);
444             }
445         } else if (type instanceof DecimalType) {
446             Commands speedCommand = Commands.bySpeed(subType, ((DecimalType) type).intValue());
447             if (speedCommand != null) {
448                 return speedCommand;
449             }
450         } else if (type instanceof UpDownType && subType == LUCCI_AIR_DC) {
451             if (UpDownType.UP == type) {
452                 return Commands.LUCCI_AIR_DC_UP;
453             } else {
454                 return Commands.LUCCI_AIR_DC_DOWN;
455             }
456         } else if (type instanceof UpDownType && subType == NOVY) {
457             if (UpDownType.UP == type) {
458                 return Commands.NOVY_UP;
459             } else {
460                 return Commands.NOVY_DOWN;
461             }
462         }
463         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
464     }
465
466     private Commands handleFanLightCommand(String channelId, Type type) throws RFXComUnsupportedChannelException {
467         if (type == OnOffType.ON) {
468             switch (subType) {
469                 case LUCCI_AIR_FAN:
470                 case CASAFAN:
471                 case WESTINGHOUSE_7226640:
472                     return LIGHT;
473
474                 case FALMEC:
475                     return FALMEC_LIGHT_ON;
476
477                 case LUCCI_AIR_DC_II:
478                     return LUCCI_AIR_DC_II_LIGHT;
479
480                 default:
481                     // return exception
482             }
483         } else if (type == OnOffType.OFF && subType == FALMEC) {
484             return Commands.FALMEC_LIGHT_OFF;
485         }
486         throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
487     }
488 }