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