]> git.basschouten.com Git - openhab-addons.git/blob
d7d7dc0a04cf467c6e1054c5275bc330271ba259
[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.config.RFXComLighting4DeviceConfiguration.PULSE_LABEL;
17
18 import java.util.HashSet;
19 import java.util.Set;
20 import java.util.stream.Collectors;
21 import java.util.stream.Stream;
22
23 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
24 import org.openhab.binding.rfxcom.internal.config.RFXComLighting4DeviceConfiguration;
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.config.discovery.DiscoveryResultBuilder;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.OpenClosedType;
34 import org.openhab.core.types.State;
35 import org.openhab.core.types.Type;
36
37 /**
38  * RFXCOM data class for lighting4 message.
39  *
40  * a Lighting4 Base command is composed of 24 bit DATA plus PULSE information
41  *
42  * <p>
43  * {@code
44  * DATA:
45  * Code = 014554
46  * S1- S24 = <0000 0001 0100 0101 0101> <0100>
47  * first 20 are DeviceID last 4 are for Command
48  *
49  * PULSE:
50  * default 350
51  * }
52  * 
53  * Tested on a PT2262 remote PlugIn module
54  *
55  * <p>
56  * Example:
57  *
58  * <pre>
59  * {@code
60  * Switch TESTout "TestOut" (All) {rfxcom=">83205.350:LIGHTING4.PT2262:Command"}
61  * (SendCommand DeviceID(int).Pulse(int):LIGHTING4.Subtype:Command )
62  *
63  * Switch TESTin "TestIn" (All) {rfxcom="<83205:Command"}
64  * (ReceiveCommand ON/OFF Command )
65  * }
66  * </pre>
67  *
68  * @author Alessandro Ballini (ITA) - Initial contribution
69  * @author Pauli Anttila - Migrated to OH2
70  * @author Martin van Wingerden - Extended support for more complex PT2262 devices
71  * @author James Hewitt - Use the thing config to identify what incoming commandIds map to
72  * @author James Hewitt - Deprecate using previously discovered commandIds because they are unreliable
73  */
74 public class RFXComLighting4Message extends RFXComDeviceMessageImpl<RFXComLighting4Message.SubType> {
75     public enum SubType implements ByteEnumWrapper {
76         PT2262(0);
77
78         private final int subType;
79
80         SubType(int subType) {
81             this.subType = subType;
82         }
83
84         @Override
85         public byte toByte() {
86             return (byte) subType;
87         }
88     }
89
90     // These are historical behaviour, are deprecated, and will be removed in a future openHAB release.
91     @Deprecated
92     private static final byte DEFAULT_OFF_COMMAND_ID = 4;
93     @Deprecated
94     private static final byte DEFAULT_ON_COMMAND_ID = 1;
95     @Deprecated
96     private Set<Integer> ON_COMMAND_IDS = Stream.of(1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15)
97             .collect(Collectors.toCollection(HashSet::new));
98
99     private SubType subType;
100     private int sensorId;
101     private int pulse;
102     private int commandId;
103
104     private RFXComLighting4DeviceConfiguration config;
105
106     public RFXComLighting4Message() {
107         super(PacketType.LIGHTING4);
108     }
109
110     public RFXComLighting4Message(byte[] data) throws RFXComException {
111         encodeMessage(data);
112     }
113
114     @Override
115     public String toString() {
116         String str = "";
117
118         str += super.toString();
119         str += ", Sub type = " + subType;
120         str += ", Device Id = " + getDeviceId();
121         str += ", Command Id = " + commandId;
122         str += ", Pulse = " + pulse;
123
124         return str;
125     }
126
127     @Override
128     public void encodeMessage(byte[] data) throws RFXComException {
129         super.encodeMessage(data);
130
131         subType = ByteEnumUtil.fromByte(SubType.class, super.subType);
132         sensorId = (data[4] & 0xFF) << 12 | (data[5] & 0xFF) << 4 | (data[6] & 0xF0) >> 4;
133
134         commandId = (data[6] & 0x0F);
135
136         pulse = (data[7] & 0xFF) << 8 | (data[8] & 0xFF);
137
138         signalLevel = (byte) ((data[9] & 0xF0) >> 4);
139     }
140
141     @Override
142     public byte[] decodeMessage() {
143         byte[] data = new byte[10];
144
145         data[0] = 0x09;
146         data[1] = PacketType.LIGHTING4.toByte();
147         data[2] = subType.toByte();
148         data[3] = seqNbr;
149
150         // SENSOR_ID + COMMAND
151         data[4] = (byte) ((sensorId >> 12) & 0xFF);
152         data[5] = (byte) ((sensorId >> 4) & 0xFF);
153         data[6] = (byte) ((sensorId << 4 & 0xF0) | (commandId & 0x0F));
154
155         // PULSE
156         data[7] = (byte) (pulse >> 8 & 0xFF);
157         data[8] = (byte) (pulse & 0xFF);
158
159         // SIGNAL
160         data[9] = (byte) ((signalLevel & 0x0F) << 4);
161
162         return data;
163     }
164
165     @Override
166     public String getDeviceId() {
167         return String.valueOf(sensorId);
168     }
169
170     @Override
171     public State convertToState(String channelId, RFXComDeviceConfiguration configuration, DeviceState deviceState)
172             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
173         RFXComLighting4DeviceConfiguration config = (RFXComLighting4DeviceConfiguration) configuration;
174
175         switch (channelId) {
176             case CHANNEL_COMMAND:
177             case CHANNEL_MOTION:
178                 if (config.onCommandId != null && commandId == config.onCommandId) {
179                     return OnOffType.ON;
180                 }
181                 if (config.offCommandId != null && commandId == config.offCommandId) {
182                     return OnOffType.OFF;
183                 }
184                 // Deprecated if statement - to be removed in a future release
185                 if (config.onCommandId == null && config.offCommandId == null) {
186                     return OnOffType.from(ON_COMMAND_IDS.contains(commandId));
187                 }
188                 throw new RFXComInvalidStateException(channelId, Integer.toString(commandId),
189                         "Device not configured for received commandId");
190
191             case CHANNEL_CONTACT:
192                 if (config.openCommandId != null && commandId == config.openCommandId) {
193                     return OpenClosedType.OPEN;
194                 }
195                 if (config.closedCommandId != null && commandId == config.closedCommandId) {
196                     return OpenClosedType.CLOSED;
197                 }
198                 // Deprecated if statement - to be removed in a future release
199                 if (config.onCommandId == null && config.offCommandId == null) {
200                     return ON_COMMAND_IDS.contains(commandId) ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
201                 }
202                 throw new RFXComInvalidStateException(channelId, Integer.toString(commandId),
203                         "Device not configured for received commandId");
204
205             case CHANNEL_COMMAND_ID:
206                 return new DecimalType(commandId);
207
208             default:
209                 return super.convertToState(channelId, config, deviceState);
210         }
211     }
212
213     @Override
214     public void setSubType(SubType subType) {
215         this.subType = subType;
216     }
217
218     @Override
219     public void setDeviceId(String deviceId) {
220         sensorId = Integer.parseInt(deviceId);
221     }
222
223     @Override
224     public void convertFromState(String channelId, Type type)
225             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
226         switch (channelId) {
227             case CHANNEL_COMMAND:
228                 if (type instanceof OnOffType) {
229                     if (type == OnOffType.ON) {
230                         if (config.onCommandId != null) {
231                             commandId = config.onCommandId;
232                         } else {
233                             // Deprecated - to throw RFXComInvalidStateException in a future release, see contact
234                             // channel
235                             commandId = DEFAULT_ON_COMMAND_ID;
236                         }
237                     }
238                     if (type == OnOffType.OFF) {
239                         if (config.offCommandId != null) {
240                             commandId = config.offCommandId;
241                         } else {
242                             // Deprecated - to throw RFXComInvalidStateException in a future release, see contact
243                             // channel
244                             commandId = DEFAULT_OFF_COMMAND_ID;
245                         }
246                     }
247                 } else {
248                     throw new RFXComInvalidStateException(channelId, type.toString(),
249                             "Channel only supports OnOffType");
250                 }
251                 break;
252
253             case CHANNEL_CONTACT:
254                 if (type instanceof OpenClosedType) {
255                     if (type == OpenClosedType.OPEN) {
256                         if (config.openCommandId != null) {
257                             commandId = config.openCommandId;
258                         } else {
259                             throw new RFXComInvalidStateException(channelId, type.toString(),
260                                     "openCommandId not configured for this device");
261                         }
262                     }
263                     if (type == OpenClosedType.CLOSED) {
264                         if (config.closedCommandId != null) {
265                             commandId = config.closedCommandId;
266                         } else {
267                             throw new RFXComInvalidStateException(channelId, type.toString(),
268                                     "closedCommandId not configured for this device");
269                         }
270                     }
271                 } else {
272                     throw new RFXComInvalidStateException(channelId, type.toString(),
273                             "Channel only supports OpenClosedType");
274                 }
275                 break;
276
277             case CHANNEL_COMMAND_ID:
278                 if (type instanceof DecimalType decimalCommand) {
279                     commandId = (byte) decimalCommand.intValue();
280                 } else {
281                     throw new RFXComInvalidStateException(channelId, type.toString(),
282                             "Channel only supports DecimalType");
283                 }
284                 break;
285
286             default:
287                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not supported by Lighting4");
288         }
289     }
290
291     @Override
292     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
293         return ByteEnumUtil.convertSubType(SubType.class, subType);
294     }
295
296     @Override
297     public void addDevicePropertiesTo(DiscoveryResultBuilder discoveryResultBuilder) throws RFXComException {
298         super.addDevicePropertiesTo(discoveryResultBuilder);
299         discoveryResultBuilder.withProperty(PULSE_LABEL, pulse);
300     }
301
302     @Override
303     public void setConfig(RFXComDeviceConfiguration config) throws RFXComException {
304         super.setConfig(config);
305         this.config = (RFXComLighting4DeviceConfiguration) config;
306         this.pulse = this.config.pulse != null ? this.config.pulse : 350;
307     }
308 }