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