]> git.basschouten.com Git - openhab-addons.git/blob
06ddc9c9345a785d1c07ce6c8f2b843198c0ecdb
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.RFXComLighting5Message.SubType.*;
18
19 import java.math.BigDecimal;
20 import java.math.RoundingMode;
21 import java.util.Arrays;
22 import java.util.List;
23
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.IncreaseDecreaseType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.OpenClosedType;
34 import org.openhab.core.library.types.PercentType;
35 import org.openhab.core.library.types.StringType;
36 import org.openhab.core.types.State;
37 import org.openhab.core.types.Type;
38 import org.openhab.core.types.UnDefType;
39
40 /**
41  * RFXCOM data class for lighting5 message.
42  *
43  * @author Paul Hampson, Neil Renaud - Initial contribution
44  * @author Pauli Anttila
45  * @author Martin van Wingerden - added support for IT and some other subtypes
46  */
47 public class RFXComLighting5Message extends RFXComDeviceMessageImpl<RFXComLighting5Message.SubType> {
48
49     public enum SubType implements ByteEnumWrapper {
50         LIGHTWAVERF(0),
51         EMW100(1),
52         BBSB_NEW(2),
53         MDREMOTE(3),
54         CONRAD_RSL2(4),
55         LIVOLO(5),
56         RGB_TRC02(6),
57         AOKE(7),
58         RGB_TRC02_2(8),
59         EURODOMEST(9),
60         LIVOLO_APPLIANCE(10),
61         MDREMOTE_107(12),
62         AVANTEK(14),
63         IT(15),
64         MDREMOTE_108(16),
65         KANGTAI(17);
66
67         private final int subType;
68
69         SubType(int subType) {
70             this.subType = subType;
71         }
72
73         @Override
74         public byte toByte() {
75             return (byte) subType;
76         }
77     }
78
79     /**
80      * Note: for the lighting5 commands, some command are only supported for certain sub types and
81      * command-bytes might even have a different meaning for another sub type.
82      *
83      * If no sub types are specified for a command, its supported by all sub types.
84      * An example is the command OFF which is represented by the byte 0x00 for all subtypes.
85      *
86      * Otherwise the list of sub types after the command-bytes indicates the sub types
87      * which support this command with this byte.
88      * Example byte value 0x03 means GROUP_ON for IT and some others while it means MOOD1 for LIGHTWAVERF
89      */
90     public enum Commands implements ByteEnumWrapperWithSupportedSubTypes<SubType> {
91         OFF(0x00),
92         ON(0x01),
93         GROUP_OFF(0x02, LIGHTWAVERF, BBSB_NEW, CONRAD_RSL2, EURODOMEST, AVANTEK, IT, KANGTAI),
94         LEARN(0x02, EMW100),
95         GROUP_ON(0x03, BBSB_NEW, CONRAD_RSL2, EURODOMEST, AVANTEK, IT, KANGTAI),
96         MOOD1(0x03, LIGHTWAVERF),
97         MOOD2(0x04, LIGHTWAVERF),
98         MOOD3(0x05, LIGHTWAVERF),
99         MOOD4(0x06, LIGHTWAVERF),
100         MOOD5(0x07, LIGHTWAVERF),
101         RESERVED1(0x08, LIGHTWAVERF),
102         RESERVED2(0x09, LIGHTWAVERF),
103         UNLOCK(0x0A, LIGHTWAVERF),
104         LOCK(0x0B, LIGHTWAVERF),
105         ALL_LOCK(0x0C, LIGHTWAVERF),
106         CLOSE_RELAY(0x0D, LIGHTWAVERF),
107         STOP_RELAY(0x0E, LIGHTWAVERF),
108         OPEN_RELAY(0x0F, LIGHTWAVERF),
109         SET_LEVEL(0x10, LIGHTWAVERF, IT),
110         COLOUR_PALETTE(0x11, LIGHTWAVERF),
111         COLOUR_TONE(0x12, LIGHTWAVERF),
112         COLOUR_CYCLE(0x13, LIGHTWAVERF),
113         TOGGLE_1(0x01, LIVOLO_APPLIANCE),
114         TOGGLE_2(0x02, LIVOLO_APPLIANCE),
115         TOGGLE_3(0x03, LIVOLO_APPLIANCE),
116         TOGGLE_4(0x04, LIVOLO_APPLIANCE),
117         TOGGLE_5(0x07, LIVOLO_APPLIANCE),
118         TOGGLE_6(0x0B, LIVOLO_APPLIANCE),
119         TOGGLE_7(0x06, LIVOLO_APPLIANCE),
120         TOGGLE_8(0x0A, LIVOLO_APPLIANCE),
121         TOGGLE_9(0x05, LIVOLO_APPLIANCE);
122
123         private final int command;
124         private final List<SubType> supportedBySubTypes;
125
126         Commands(int command) {
127             this(command, SubType.values());
128         }
129
130         Commands(int command, SubType... supportedBySubTypes) {
131             this.command = command;
132             this.supportedBySubTypes = Arrays.asList(supportedBySubTypes);
133         }
134
135         @Override
136         public List<SubType> supportedBySubTypes() {
137             return supportedBySubTypes;
138         }
139
140         @Override
141         public byte toByte() {
142             return (byte) command;
143         }
144     }
145
146     public SubType subType;
147     public int sensorId;
148     public byte unitCode;
149     public Commands command;
150     public byte dimmingLevel;
151
152     public RFXComLighting5Message() {
153         super(PacketType.LIGHTING5);
154     }
155
156     public RFXComLighting5Message(byte[] data) throws RFXComException {
157         encodeMessage(data);
158     }
159
160     @Override
161     public String toString() {
162         String str = "";
163
164         str += super.toString();
165         str += ", Sub type = " + subType;
166         str += ", Device Id = " + getDeviceId();
167         str += ", Command = " + command;
168         str += ", Dim level = " + dimmingLevel;
169         str += ", Signal level = " + signalLevel;
170
171         return str;
172     }
173
174     @Override
175     public void encodeMessage(byte[] data) throws RFXComException {
176         super.encodeMessage(data);
177
178         subType = fromByte(SubType.class, super.subType);
179
180         sensorId = (data[4] & 0xFF) << 16 | (data[5] & 0xFF) << 8 | (data[6] & 0xFF);
181         unitCode = data[7];
182
183         command = fromByte(Commands.class, data[8], subType);
184
185         dimmingLevel = data[9];
186         signalLevel = (byte) ((data[10] & 0xF0) >> 4);
187     }
188
189     @Override
190     public byte[] decodeMessage() {
191         byte[] data = new byte[11];
192
193         data[0] = 0x0A;
194         data[1] = RFXComBaseMessage.PacketType.LIGHTING5.toByte();
195         data[2] = subType.toByte();
196         data[3] = seqNbr;
197         data[4] = (byte) ((sensorId >> 16) & 0xFF);
198         data[5] = (byte) ((sensorId >> 8) & 0xFF);
199         data[6] = (byte) (sensorId & 0xFF);
200
201         data[7] = unitCode;
202         data[8] = command.toByte();
203         data[9] = dimmingLevel;
204         data[10] = (byte) ((signalLevel & 0x0F) << 4);
205
206         return data;
207     }
208
209     @Override
210     public String getDeviceId() {
211         return sensorId + ID_DELIMITER + unitCode;
212     }
213
214     /**
215      * Convert a 0-31 scale value to a percent type.
216      *
217      * @param pt percent type to convert
218      * @return converted value 0-31
219      */
220     public static int getDimLevelFromPercentType(PercentType pt) {
221         return pt.toBigDecimal().multiply(BigDecimal.valueOf(31))
222                 .divide(PercentType.HUNDRED.toBigDecimal(), 0, RoundingMode.UP).intValue();
223     }
224
225     /**
226      * Convert a 0-31 scale value to a percent type.
227      *
228      * @param value percent type to convert
229      * @return converted value 0-31
230      */
231     public static PercentType getPercentTypeFromDimLevel(int value) {
232         value = Math.min(value, 31);
233
234         return new PercentType(BigDecimal.valueOf(value).multiply(BigDecimal.valueOf(100))
235                 .divide(BigDecimal.valueOf(31), 0, RoundingMode.UP).intValue());
236     }
237
238     @Override
239     public State convertToState(String channelId, RFXComDeviceConfiguration config, DeviceState deviceState)
240             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
241         switch (channelId) {
242             case CHANNEL_MOOD:
243                 switch (command) {
244                     case GROUP_OFF:
245                         return new DecimalType(0);
246                     case MOOD1:
247                         return new DecimalType(1);
248                     case MOOD2:
249                         return new DecimalType(2);
250                     case MOOD3:
251                         return new DecimalType(3);
252                     case MOOD4:
253                         return new DecimalType(4);
254                     case MOOD5:
255                         return new DecimalType(5);
256                     default:
257                         throw new RFXComUnsupportedChannelException(
258                                 "Unexpected mood command: " + command + " for " + channelId);
259                 }
260
261             case CHANNEL_DIMMING_LEVEL:
262                 return RFXComLighting5Message.getPercentTypeFromDimLevel(dimmingLevel);
263
264             case CHANNEL_COMMAND:
265                 switch (command) {
266                     case OFF:
267                     case GROUP_OFF:
268                         return OnOffType.OFF;
269
270                     case ON:
271                     case GROUP_ON:
272                         return OnOffType.ON;
273
274                     case SET_LEVEL:
275                     default:
276                         throw new RFXComUnsupportedChannelException("Can't convert " + command + " for " + channelId);
277                 }
278
279             case CHANNEL_COMMAND_STRING:
280                 return command == null ? UnDefType.UNDEF : StringType.valueOf(command.toString());
281
282             case CHANNEL_CONTACT:
283                 switch (command) {
284                     case OFF:
285                     case GROUP_OFF:
286                         return OpenClosedType.CLOSED;
287
288                     case ON:
289                     case GROUP_ON:
290                         return OpenClosedType.OPEN;
291
292                     case SET_LEVEL:
293                     default:
294                         throw new RFXComUnsupportedChannelException("Can't convert " + command + " for " + channelId);
295                 }
296
297             default:
298                 return super.convertToState(channelId, config, deviceState);
299         }
300     }
301
302     @Override
303     public void setSubType(SubType subType) {
304         this.subType = subType;
305     }
306
307     @Override
308     public void setDeviceId(String deviceId) throws RFXComException {
309         String[] ids = deviceId.split("\\" + ID_DELIMITER);
310         if (ids.length != 2) {
311             throw new RFXComException("Invalid device id '" + deviceId + "'");
312         }
313
314         sensorId = Integer.parseInt(ids[0]);
315         unitCode = Byte.parseByte(ids[1]);
316     }
317
318     @Override
319     public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
320         switch (channelId) {
321             case CHANNEL_COMMAND:
322                 if (type instanceof OnOffType) {
323                     command = (type == OnOffType.ON ? Commands.ON : Commands.OFF);
324                     dimmingLevel = 0;
325
326                 } else {
327                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
328                 }
329                 break;
330
331             case CHANNEL_COMMAND_STRING:
332                 command = Commands.valueOf(type.toString().toUpperCase());
333                 break;
334
335             case CHANNEL_DIMMING_LEVEL:
336                 if (type instanceof OnOffType) {
337                     command = (type == OnOffType.ON ? Commands.ON : Commands.OFF);
338                     dimmingLevel = 0;
339
340                 } else if (type instanceof PercentType percentCommand) {
341                     command = Commands.SET_LEVEL;
342                     dimmingLevel = (byte) getDimLevelFromPercentType(percentCommand);
343
344                     if (dimmingLevel == 0) {
345                         command = Commands.OFF;
346                     }
347
348                 } else if (type instanceof IncreaseDecreaseType) {
349                     command = Commands.SET_LEVEL;
350                     // Evert: I do not know how to get previous object state...
351                     dimmingLevel = 5;
352
353                 } else {
354                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
355                 }
356                 break;
357
358             default:
359                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
360         }
361     }
362
363     @Override
364     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
365         return ByteEnumUtil.convertSubType(SubType.class, subType);
366     }
367 }