]> git.basschouten.com Git - openhab-addons.git/blob
cda2175a1b583951eaf7955f2271f8071cd3bb89
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.RFXComDeviceConfiguration.*;
17 import static org.openhab.binding.rfxcom.internal.messages.ByteEnumUtil.fromByte;
18
19 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
20 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
21 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
22 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException;
23 import org.openhab.binding.rfxcom.internal.handler.DeviceState;
24 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.OpenClosedType;
28 import org.openhab.core.types.State;
29 import org.openhab.core.types.Type;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * RFXCOM data class for lighting4 message.
35  *
36  * a Lighting4 Base command is composed of 24 bit DATA plus PULSE information
37  *
38  * DATA:
39  * Code = 014554
40  * S1- S24 = <0000 0001 0100 0101 0101> <0100>
41  * first 20 are DeviceID last 4 are for Command
42  *
43  * PULSE:
44  * default 350
45  *
46  * Tested on a PT2262 remote PlugIn module
47  *
48  * Example:
49  *
50  * Switch TESTout "TestOut" (All) {rfxcom=">83205.350:LIGHTING4.PT2262:Command"}
51  * (SendCommand DeviceID(int).Pulse(int):LIGHTING4.Subtype:Command )
52  *
53  * Switch TESTin "TestIn" (All) {rfxcom="<83205:Command"}
54  * (ReceiveCommand ON/OFF Command )
55  *
56  * @author Alessandro Ballini (ITA) - Initial contribution
57  * @author Pauli Anttila - Migrated to OH2
58  * @author Martin van Wingerden - Extended support for more complex PT2262 devices
59  */
60 public class RFXComLighting4Message extends RFXComDeviceMessageImpl<RFXComLighting4Message.SubType> {
61     // this logger is used from a static context, so is static as well
62     private static final Logger LOGGER = LoggerFactory.getLogger(RFXComLighting4Message.class);
63
64     private static final byte DEFAULT_OFF_COMMAND_ID = Commands.OFF_4.toByte();
65     private static final byte DEFAULT_ON_COMMAND_ID = Commands.ON_1.toByte();
66
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     public enum Commands implements ByteEnumWrapper {
83         OFF_0(0, false),
84         ON_1(1, true),
85         OFF_2(2, false),
86         ON_3(3, true),
87         OFF_4(4, false),
88         ON_5(5, true),
89         ON_6(6, true),
90         ON_7(7, true),
91         ON_8(8, true),
92         ON_9(9, true),
93         ON_10(10, true),
94         ON_11(11, true),
95         ON_12(12, true),
96         OFF_14(14, false),
97         ON_15(15, true),
98         UNKNOWN(-1, false);
99
100         private final int command;
101         private final boolean on;
102
103         Commands(int command, boolean on) {
104             this.command = command;
105             this.on = on;
106         }
107
108         @Override
109         public byte toByte() {
110             return (byte) command;
111         }
112
113         public boolean isOn() {
114             return on;
115         }
116
117         public static Commands fromByte(int input) {
118             for (Commands c : Commands.values()) {
119                 if (c.command == input) {
120                     return c;
121                 }
122             }
123             LOGGER.info(
124                     "A not completely supported command with value {} was received, we can send it but please report "
125                             + "it as an issue including what the command means, this helps to extend the binding with better support.",
126                     input);
127             return UNKNOWN;
128         }
129     }
130
131     private SubType subType;
132     private int sensorId;
133     private int pulse;
134     private Commands command;
135     private int commandId;
136     private int offCommandId;
137     private int onCommandId;
138
139     public RFXComLighting4Message() {
140         super(PacketType.LIGHTING4);
141     }
142
143     public RFXComLighting4Message(byte[] data) throws RFXComException {
144         encodeMessage(data);
145     }
146
147     @Override
148     public String toString() {
149         String str = "";
150
151         str += super.toString();
152         str += ", Sub type = " + subType;
153         str += ", Device Id = " + getDeviceId();
154         str += ", Command = " + command + "(" + commandId + ")";
155         str += ", Pulse = " + pulse;
156
157         return str;
158     }
159
160     @Override
161     public void encodeMessage(byte[] data) throws RFXComException {
162         super.encodeMessage(data);
163
164         subType = fromByte(SubType.class, super.subType);
165         sensorId = (data[4] & 0xFF) << 12 | (data[5] & 0xFF) << 4 | (data[6] & 0xF0) >> 4;
166
167         commandId = (data[6] & 0x0F);
168         command = Commands.fromByte(commandId);
169         onCommandId = command.isOn() ? commandId : DEFAULT_ON_COMMAND_ID;
170         offCommandId = command.isOn() ? DEFAULT_OFF_COMMAND_ID : commandId;
171
172         pulse = (data[7] & 0xFF) << 8 | (data[8] & 0xFF);
173
174         signalLevel = (byte) ((data[9] & 0xF0) >> 4);
175     }
176
177     @Override
178     public byte[] decodeMessage() {
179         byte[] data = new byte[10];
180
181         data[0] = 0x09;
182         data[1] = PacketType.LIGHTING4.toByte();
183         data[2] = subType.toByte();
184         data[3] = seqNbr;
185
186         // SENSOR_ID + COMMAND
187         data[4] = (byte) ((sensorId >> 12) & 0xFF);
188         data[5] = (byte) ((sensorId >> 4) & 0xFF);
189         data[6] = (byte) ((sensorId << 4 & 0xF0) | (commandId & 0x0F));
190
191         // PULSE
192         data[7] = (byte) (pulse >> 8 & 0xFF);
193         data[8] = (byte) (pulse & 0xFF);
194
195         // SIGNAL
196         data[9] = (byte) ((signalLevel & 0x0F) << 4);
197
198         return data;
199     }
200
201     @Override
202     public String getDeviceId() {
203         return String.valueOf(sensorId);
204     }
205
206     @Override
207     public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException {
208         switch (channelId) {
209             case CHANNEL_COMMAND:
210             case CHANNEL_MOTION:
211                 return command.isOn() ? OnOffType.ON : OnOffType.OFF;
212
213             case CHANNEL_CONTACT:
214                 return command.isOn() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
215
216             case CHANNEL_COMMAND_ID:
217                 return new DecimalType(commandId);
218
219             default:
220                 return super.convertToState(channelId, deviceState);
221         }
222     }
223
224     @Override
225     public void setSubType(SubType subType) {
226         this.subType = subType;
227     }
228
229     @Override
230     public void setDeviceId(String deviceId) {
231         sensorId = Integer.parseInt(deviceId);
232     }
233
234     @Override
235     public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
236         switch (channelId) {
237             case CHANNEL_COMMAND:
238                 if (type instanceof OnOffType) {
239                     command = Commands.fromByte(type == OnOffType.ON ? onCommandId : offCommandId);
240                     commandId = command.toByte();
241
242                 } else {
243                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
244                 }
245                 break;
246
247             case CHANNEL_COMMAND_ID:
248                 if (type instanceof DecimalType) {
249                     commandId = ((DecimalType) type).toBigDecimal().byteValue();
250                     command = Commands.fromByte(commandId);
251
252                 } else {
253                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
254                 }
255                 break;
256
257             default:
258                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
259         }
260     }
261
262     @Override
263     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
264         return ByteEnumUtil.convertSubType(SubType.class, subType);
265     }
266
267     @Override
268     public void addDevicePropertiesTo(DiscoveryResultBuilder discoveryResultBuilder) throws RFXComException {
269         super.addDevicePropertiesTo(discoveryResultBuilder);
270         discoveryResultBuilder.withProperty(PULSE_LABEL, pulse);
271         discoveryResultBuilder.withProperty(ON_COMMAND_ID_LABEL, onCommandId);
272         discoveryResultBuilder.withProperty(OFF_COMMAND_ID_LABEL, offCommandId);
273     }
274
275     @Override
276     public void setConfig(RFXComDeviceConfiguration config) throws RFXComException {
277         super.setConfig(config);
278         this.pulse = config.pulse != null ? config.pulse : 350;
279         this.onCommandId = valueOrDefault(config.onCommandId, DEFAULT_ON_COMMAND_ID);
280         this.offCommandId = valueOrDefault(config.offCommandId, DEFAULT_OFF_COMMAND_ID);
281     }
282
283     private int valueOrDefault(Integer commandId, byte defaultValue) {
284         if (commandId != null) {
285             return commandId;
286         }
287         return defaultValue;
288     }
289 }