2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.rfxcom.internal.messages;
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;
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;
34 * RFXCOM data class for lighting4 message.
36 * a Lighting4 Base command is composed of 24 bit DATA plus PULSE information
40 * S1- S24 = <0000 0001 0100 0101 0101> <0100>
41 * first 20 are DeviceID last 4 are for Command
46 * Tested on a PT2262 remote PlugIn module
50 * Switch TESTout "TestOut" (All) {rfxcom=">83205.350:LIGHTING4.PT2262:Command"}
51 * (SendCommand DeviceID(int).Pulse(int):LIGHTING4.Subtype:Command )
53 * Switch TESTin "TestIn" (All) {rfxcom="<83205:Command"}
54 * (ReceiveCommand ON/OFF Command )
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
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);
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();
67 public enum SubType implements ByteEnumWrapper {
70 private final int subType;
72 SubType(int subType) {
73 this.subType = subType;
77 public byte toByte() {
78 return (byte) subType;
82 public enum Commands implements ByteEnumWrapper {
100 private final int command;
101 private final boolean on;
103 Commands(int command, boolean on) {
104 this.command = command;
109 public byte toByte() {
110 return (byte) command;
113 public boolean isOn() {
117 public static Commands fromByte(int input) {
118 for (Commands c : Commands.values()) {
119 if (c.command == input) {
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.",
131 private SubType subType;
132 private int sensorId;
134 private Commands command;
135 private int commandId;
136 private int offCommandId;
137 private int onCommandId;
139 public RFXComLighting4Message() {
140 super(PacketType.LIGHTING4);
143 public RFXComLighting4Message(byte[] data) throws RFXComException {
148 public String toString() {
151 str += super.toString();
152 str += ", Sub type = " + subType;
153 str += ", Device Id = " + getDeviceId();
154 str += ", Command = " + command + "(" + commandId + ")";
155 str += ", Pulse = " + pulse;
161 public void encodeMessage(byte[] data) throws RFXComException {
162 super.encodeMessage(data);
164 subType = fromByte(SubType.class, super.subType);
165 sensorId = (data[4] & 0xFF) << 12 | (data[5] & 0xFF) << 4 | (data[6] & 0xF0) >> 4;
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;
172 pulse = (data[7] & 0xFF) << 8 | (data[8] & 0xFF);
174 signalLevel = (byte) ((data[9] & 0xF0) >> 4);
178 public byte[] decodeMessage() {
179 byte[] data = new byte[10];
182 data[1] = PacketType.LIGHTING4.toByte();
183 data[2] = subType.toByte();
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));
192 data[7] = (byte) (pulse >> 8 & 0xFF);
193 data[8] = (byte) (pulse & 0xFF);
196 data[9] = (byte) ((signalLevel & 0x0F) << 4);
202 public String getDeviceId() {
203 return String.valueOf(sensorId);
207 public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException {
209 case CHANNEL_COMMAND:
211 return command.isOn() ? OnOffType.ON : OnOffType.OFF;
213 case CHANNEL_CONTACT:
214 return command.isOn() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
216 case CHANNEL_COMMAND_ID:
217 return new DecimalType(commandId);
220 return super.convertToState(channelId, deviceState);
225 public void setSubType(SubType subType) {
226 this.subType = subType;
230 public void setDeviceId(String deviceId) {
231 sensorId = Integer.parseInt(deviceId);
235 public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException {
237 case CHANNEL_COMMAND:
238 if (type instanceof OnOffType) {
239 command = Commands.fromByte(type == OnOffType.ON ? onCommandId : offCommandId);
240 commandId = command.toByte();
243 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
247 case CHANNEL_COMMAND_ID:
248 if (type instanceof DecimalType) {
249 commandId = ((DecimalType) type).toBigDecimal().byteValue();
250 command = Commands.fromByte(commandId);
253 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
258 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
263 public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
264 return ByteEnumUtil.convertSubType(SubType.class, subType);
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);
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);
283 private int valueOrDefault(Integer commandId, byte defaultValue) {
284 if (commandId != null) {