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