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