]> git.basschouten.com Git - openhab-addons.git/blob
6096f88b7459a822ab0506fc325d88b2287b9056
[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.RFXComBaseMessage.PacketType.LIGHTING2;
18 import static org.openhab.binding.rfxcom.internal.messages.RFXComLighting2Message.Commands.*;
19
20 import java.math.BigDecimal;
21 import java.math.RoundingMode;
22
23 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
24 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
25 import org.openhab.binding.rfxcom.internal.exceptions.RFXComInvalidStateException;
26 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
27 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException;
28 import org.openhab.binding.rfxcom.internal.handler.DeviceState;
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.types.State;
34 import org.openhab.core.types.Type;
35
36 /**
37  * RFXCOM data class for lighting2 message.
38  *
39  * @author Pauli Anttila - Initial contribution
40  */
41 public class RFXComLighting2Message extends RFXComDeviceMessageImpl<RFXComLighting2Message.SubType> {
42     public enum SubType implements ByteEnumWrapper {
43         AC(0),
44         HOME_EASY_EU(1),
45         ANSLUT(2),
46         KAMBROOK(3);
47
48         private final int subType;
49
50         SubType(int subType) {
51             this.subType = subType;
52         }
53
54         @Override
55         public byte toByte() {
56             return (byte) subType;
57         }
58     }
59
60     public enum Commands implements ByteEnumWrapper {
61         OFF(0),
62         ON(1),
63         SET_LEVEL(2),
64         GROUP_OFF(3),
65         GROUP_ON(4),
66         SET_GROUP_LEVEL(5);
67
68         private final int command;
69
70         Commands(int command) {
71             this.command = command;
72         }
73
74         @Override
75         public byte toByte() {
76             return (byte) command;
77         }
78     }
79
80     public SubType subType;
81     public int sensorId;
82     public byte unitCode;
83     public Commands command;
84     public byte dimmingLevel;
85     public boolean group;
86
87     public RFXComLighting2Message() {
88         super(PacketType.LIGHTING2);
89     }
90
91     public RFXComLighting2Message(byte[] data) throws RFXComException {
92         encodeMessage(data);
93     }
94
95     @Override
96     public String toString() {
97         String str = "";
98
99         str += super.toString();
100         str += ", Sub type = " + subType;
101         str += ", Device Id = " + getDeviceId();
102         str += ", Command = " + command;
103         str += ", Dim level = " + dimmingLevel;
104         str += ", Signal level = " + signalLevel;
105
106         return str;
107     }
108
109     @Override
110     public void encodeMessage(byte[] data) throws RFXComException {
111         super.encodeMessage(data);
112
113         subType = fromByte(SubType.class, super.subType);
114         sensorId = (data[4] & 0xFF) << 24 | (data[5] & 0xFF) << 16 | (data[6] & 0xFF) << 8 | (data[7] & 0xFF);
115         command = fromByte(Commands.class, data[9]);
116
117         if ((command == Commands.GROUP_ON) || (command == Commands.GROUP_OFF)) {
118             unitCode = 0;
119         } else {
120             unitCode = data[8];
121         }
122
123         dimmingLevel = data[10];
124         signalLevel = (byte) ((data[11] & 0xF0) >> 4);
125     }
126
127     @Override
128     public byte[] decodeMessage() {
129         byte[] data = new byte[12];
130
131         data[0] = 0x0B;
132         data[1] = LIGHTING2.toByte();
133         data[2] = subType.toByte();
134         data[3] = seqNbr;
135         data[4] = (byte) ((sensorId >> 24) & 0xFF);
136         data[5] = (byte) ((sensorId >> 16) & 0xFF);
137         data[6] = (byte) ((sensorId >> 8) & 0xFF);
138         data[7] = (byte) (sensorId & 0xFF);
139
140         data[8] = unitCode;
141         data[9] = command.toByte();
142         data[10] = dimmingLevel;
143         data[11] = (byte) ((signalLevel & 0x0F) << 4);
144
145         return data;
146     }
147
148     @Override
149     public String getDeviceId() {
150         return sensorId + ID_DELIMITER + unitCode;
151     }
152
153     /**
154      * Convert a 0-15 scale value to a percent type.
155      *
156      * @param pt percent type to convert
157      * @return converted value 0-15
158      */
159     public static int getDimLevelFromPercentType(PercentType pt) {
160         return pt.toBigDecimal().multiply(BigDecimal.valueOf(15))
161                 .divide(PercentType.HUNDRED.toBigDecimal(), 0, RoundingMode.UP).intValue();
162     }
163
164     /**
165      * Convert a 0-15 scale value to a percent type.
166      *
167      * @param value percent type to convert
168      * @return converted value 0-15
169      */
170     public static PercentType getPercentTypeFromDimLevel(int value) {
171         value = Math.min(value, 15);
172
173         return new PercentType(BigDecimal.valueOf(value).multiply(BigDecimal.valueOf(100))
174                 .divide(BigDecimal.valueOf(15), 0, RoundingMode.UP).intValue());
175     }
176
177     @Override
178     public State convertToState(String channelId, RFXComDeviceConfiguration config, DeviceState deviceState)
179             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
180         switch (channelId) {
181             case CHANNEL_DIMMING_LEVEL:
182                 return RFXComLighting2Message.getPercentTypeFromDimLevel(dimmingLevel);
183
184             case CHANNEL_COMMAND:
185                 switch (command) {
186                     case OFF:
187                     case GROUP_OFF:
188                         return OnOffType.OFF;
189
190                     case ON:
191                     case GROUP_ON:
192                         return OnOffType.ON;
193
194                     case SET_GROUP_LEVEL:
195                     case SET_LEVEL:
196                     default:
197                         throw new RFXComUnsupportedChannelException("Can't convert " + command + " for " + channelId);
198                 }
199
200             case CHANNEL_CONTACT:
201                 switch (command) {
202                     case OFF:
203                     case GROUP_OFF:
204                         return OpenClosedType.CLOSED;
205
206                     case ON:
207                     case GROUP_ON:
208                         return OpenClosedType.OPEN;
209
210                     case SET_GROUP_LEVEL:
211                     case SET_LEVEL:
212                     default:
213                         throw new RFXComUnsupportedChannelException("Can't convert " + command + " for " + channelId);
214                 }
215
216             default:
217                 return super.convertToState(channelId, config, deviceState);
218         }
219     }
220
221     @Override
222     public void setSubType(SubType subType) {
223         this.subType = subType;
224     }
225
226     @Override
227     public void setDeviceId(String deviceId) throws RFXComException {
228         String[] ids = deviceId.split("\\" + ID_DELIMITER);
229         if (ids.length != 2) {
230             throw new RFXComException("Invalid device id '" + deviceId + "'");
231         }
232
233         sensorId = Integer.parseInt(ids[0]);
234
235         // Get unitcode, 0 means group
236         unitCode = Byte.parseByte(ids[1]);
237         if (unitCode == 0) {
238             unitCode = 1;
239             group = true;
240         }
241     }
242
243     @Override
244     public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
245         switch (channelId) {
246             case CHANNEL_COMMAND:
247                 if (type instanceof OnOffType) {
248                     if (group) {
249                         command = (type == OnOffType.ON ? GROUP_ON : GROUP_OFF);
250
251                     } else {
252                         command = (type == OnOffType.ON ? Commands.ON : Commands.OFF);
253                     }
254
255                     dimmingLevel = 0;
256                 } else {
257                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
258                 }
259                 break;
260
261             case CHANNEL_DIMMING_LEVEL:
262                 if (type instanceof OnOffType) {
263                     command = (type == OnOffType.ON ? Commands.ON : Commands.OFF);
264                     dimmingLevel = 0;
265
266                 } else if (type instanceof PercentType percentCommand) {
267                     command = Commands.SET_LEVEL;
268                     dimmingLevel = (byte) getDimLevelFromPercentType(percentCommand);
269
270                     if (dimmingLevel == 0) {
271                         command = Commands.OFF;
272                     }
273                 } else if (type instanceof IncreaseDecreaseType) {
274                     command = Commands.SET_LEVEL;
275                     // Evert: I do not know how to get previous object state...
276                     dimmingLevel = 5;
277
278                 } else {
279                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
280                 }
281                 break;
282
283             default:
284                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
285         }
286     }
287
288     @Override
289     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
290         return ByteEnumUtil.convertSubType(SubType.class, subType);
291     }
292 }