2 * Copyright (c) 2010-2023 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.RFXComLighting4DeviceConfiguration.PULSE_LABEL;
18 import java.util.HashSet;
20 import java.util.stream.Collectors;
21 import java.util.stream.Stream;
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;
38 * RFXCOM data class for lighting4 message.
40 * a Lighting4 Base command is composed of 24 bit DATA plus PULSE information
46 * S1- S24 = <0000 0001 0100 0101 0101> <0100>
47 * first 20 are DeviceID last 4 are for Command
53 * Tested on a PT2262 remote PlugIn module
60 * Switch TESTout "TestOut" (All) {rfxcom=">83205.350:LIGHTING4.PT2262:Command"}
61 * (SendCommand DeviceID(int).Pulse(int):LIGHTING4.Subtype:Command )
63 * Switch TESTin "TestIn" (All) {rfxcom="<83205:Command"}
64 * (ReceiveCommand ON/OFF Command )
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
74 public class RFXComLighting4Message extends RFXComDeviceMessageImpl<RFXComLighting4Message.SubType> {
75 public enum SubType implements ByteEnumWrapper {
78 private final int subType;
80 SubType(int subType) {
81 this.subType = subType;
85 public byte toByte() {
86 return (byte) subType;
90 // These are historical behaviour, are deprecated, and will be removed in a future openHAB release.
92 private static final byte DEFAULT_OFF_COMMAND_ID = 4;
94 private static final byte DEFAULT_ON_COMMAND_ID = 1;
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));
99 private SubType subType;
100 private int sensorId;
102 private int commandId;
104 private RFXComLighting4DeviceConfiguration config;
106 public RFXComLighting4Message() {
107 super(PacketType.LIGHTING4);
110 public RFXComLighting4Message(byte[] data) throws RFXComException {
115 public String toString() {
118 str += super.toString();
119 str += ", Sub type = " + subType;
120 str += ", Device Id = " + getDeviceId();
121 str += ", Command Id = " + commandId;
122 str += ", Pulse = " + pulse;
128 public void encodeMessage(byte[] data) throws RFXComException {
129 super.encodeMessage(data);
131 subType = ByteEnumUtil.fromByte(SubType.class, super.subType);
132 sensorId = (data[4] & 0xFF) << 12 | (data[5] & 0xFF) << 4 | (data[6] & 0xF0) >> 4;
134 commandId = (data[6] & 0x0F);
136 pulse = (data[7] & 0xFF) << 8 | (data[8] & 0xFF);
138 signalLevel = (byte) ((data[9] & 0xF0) >> 4);
142 public byte[] decodeMessage() {
143 byte[] data = new byte[10];
146 data[1] = PacketType.LIGHTING4.toByte();
147 data[2] = subType.toByte();
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));
156 data[7] = (byte) (pulse >> 8 & 0xFF);
157 data[8] = (byte) (pulse & 0xFF);
160 data[9] = (byte) ((signalLevel & 0x0F) << 4);
166 public String getDeviceId() {
167 return String.valueOf(sensorId);
171 public State convertToState(String channelId, RFXComDeviceConfiguration configuration, DeviceState deviceState)
172 throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
173 RFXComLighting4DeviceConfiguration config = (RFXComLighting4DeviceConfiguration) configuration;
176 case CHANNEL_COMMAND:
178 if (config.onCommandId != null && commandId == config.onCommandId) {
181 if (config.offCommandId != null && commandId == config.offCommandId) {
182 return OnOffType.OFF;
184 // Deprecated if statement - to be removed in a future release
185 if (config.onCommandId == null && config.offCommandId == null) {
186 return ON_COMMAND_IDS.contains(commandId) ? OnOffType.ON : OnOffType.OFF;
188 throw new RFXComInvalidStateException(channelId, Integer.toString(commandId),
189 "Device not configured for received commandId");
191 case CHANNEL_CONTACT:
192 if (config.openCommandId != null && commandId == config.openCommandId) {
193 return OpenClosedType.OPEN;
195 if (config.closedCommandId != null && commandId == config.closedCommandId) {
196 return OpenClosedType.CLOSED;
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;
202 throw new RFXComInvalidStateException(channelId, Integer.toString(commandId),
203 "Device not configured for received commandId");
205 case CHANNEL_COMMAND_ID:
206 return new DecimalType(commandId);
209 return super.convertToState(channelId, config, deviceState);
214 public void setSubType(SubType subType) {
215 this.subType = subType;
219 public void setDeviceId(String deviceId) {
220 sensorId = Integer.parseInt(deviceId);
224 public void convertFromState(String channelId, Type type)
225 throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
227 case CHANNEL_COMMAND:
228 if (type instanceof OnOffType) {
229 if (type == OnOffType.ON) {
230 if (config.onCommandId != null) {
231 commandId = config.onCommandId;
233 // Deprecated - to throw RFXComInvalidStateException in a future release, see contact
235 commandId = DEFAULT_ON_COMMAND_ID;
238 if (type == OnOffType.OFF) {
239 if (config.offCommandId != null) {
240 commandId = config.offCommandId;
242 // Deprecated - to throw RFXComInvalidStateException in a future release, see contact
244 commandId = DEFAULT_OFF_COMMAND_ID;
248 throw new RFXComInvalidStateException(channelId, type.toString(),
249 "Channel only supports OnOffType");
253 case CHANNEL_CONTACT:
254 if (type instanceof OpenClosedType) {
255 if (type == OpenClosedType.OPEN) {
256 if (config.openCommandId != null) {
257 commandId = config.openCommandId;
259 throw new RFXComInvalidStateException(channelId, type.toString(),
260 "openCommandId not configured for this device");
263 if (type == OpenClosedType.CLOSED) {
264 if (config.closedCommandId != null) {
265 commandId = config.closedCommandId;
267 throw new RFXComInvalidStateException(channelId, type.toString(),
268 "closedCommandId not configured for this device");
272 throw new RFXComInvalidStateException(channelId, type.toString(),
273 "Channel only supports OpenClosedType");
277 case CHANNEL_COMMAND_ID:
278 if (type instanceof DecimalType decimalCommand) {
279 commandId = (byte) decimalCommand.intValue();
281 throw new RFXComInvalidStateException(channelId, type.toString(),
282 "Channel only supports DecimalType");
287 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not supported by Lighting4");
292 public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
293 return ByteEnumUtil.convertSubType(SubType.class, subType);
297 public void addDevicePropertiesTo(DiscoveryResultBuilder discoveryResultBuilder) throws RFXComException {
298 super.addDevicePropertiesTo(discoveryResultBuilder);
299 discoveryResultBuilder.withProperty(PULSE_LABEL, pulse);
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;