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