2 * Copyright (c) 2010-2024 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.messages.RFXComBaseMessage.PacketType.RAW;
18 import java.nio.ByteBuffer;
19 import java.util.stream.Collectors;
20 import java.util.stream.IntStream;
22 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
23 import org.openhab.binding.rfxcom.internal.config.RFXComRawDeviceConfiguration;
24 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
25 import org.openhab.binding.rfxcom.internal.exceptions.RFXComInvalidStateException;
26 import org.openhab.binding.rfxcom.internal.exceptions.RFXComMessageTooLongException;
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.library.types.OnOffType;
31 import org.openhab.core.library.types.OpenClosedType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.types.State;
34 import org.openhab.core.types.Type;
35 import org.openhab.core.util.HexUtils;
38 * RFXCOM data class for raw messages.
40 * @author James Hewitt-Thomas - New addition to the PRO RFXCom firmware
42 public class RFXComRawMessage extends RFXComDeviceMessageImpl<RFXComRawMessage.SubType> {
44 public enum SubType implements ByteEnumWrapper {
52 private final int subType;
54 SubType(int subType) {
55 this.subType = subType;
59 public byte toByte() {
60 return (byte) subType;
63 public static SubType fromByte(int input) {
64 for (SubType c : SubType.values()) {
65 if (c.subType == input) {
70 return SubType.UNKNOWN;
74 public SubType subType;
76 public short[] pulses;
78 private RFXComRawDeviceConfiguration config;
80 public RFXComRawMessage() {
82 pulses = new short[0];
85 public RFXComRawMessage(byte[] message) throws RFXComException {
86 encodeMessage(message);
90 public String toString() {
91 String str = super.toString();
93 str += ", Sub type = " + subType;
99 public void encodeMessage(byte[] message) throws RFXComException {
100 super.encodeMessage(message);
102 final int pulsesByteLen = rawMessage.length - 5;
103 if (pulsesByteLen % 4 != 0) {
104 throw new RFXComException("Incorrect byte length for pulses - must be divisible by 4");
107 subType = SubType.fromByte(super.subType);
108 repeat = rawMessage[4];
109 pulses = new short[pulsesByteLen / 2];
110 ByteBuffer.wrap(rawMessage, 5, rawMessage.length - 5).asShortBuffer().get(pulses);
114 public byte[] decodeMessage() throws RFXComException {
115 if (pulses.length > 124) {
116 throw new RFXComMessageTooLongException("Longest payload according to RFXtrx SDK is 124 shorts.");
119 final int pulsesByteLen = pulses.length * 2;
120 byte[] data = new byte[5 + pulsesByteLen];
122 data[0] = (byte) (data.length - 1);
123 data[1] = RAW.toByte();
124 data[2] = subType.toByte();
126 data[4] = (byte) config.repeat;
128 ByteBuffer.wrap(data, 5, pulsesByteLen).asShortBuffer().put(pulses);
134 public String getDeviceId() {
139 public void setConfig(RFXComDeviceConfiguration config) throws RFXComException {
140 super.setConfig(config);
141 this.config = (RFXComRawDeviceConfiguration) config;
145 public State convertToState(String channelId, RFXComDeviceConfiguration config, DeviceState deviceState)
146 throws RFXComUnsupportedChannelException {
148 case CHANNEL_RAW_MESSAGE:
149 return new StringType(HexUtils.bytesToHex(rawMessage));
151 case CHANNEL_RAW_PAYLOAD:
152 byte[] payload = new byte[pulses.length * 2];
153 ByteBuffer.wrap(payload).asShortBuffer().put(pulses);
154 return new StringType(HexUtils.bytesToHex(payload));
157 return new StringType(IntStream.range(0, pulses.length)
158 .mapToObj(s -> Integer.toString(Short.toUnsignedInt(pulses[s])))
159 .collect(Collectors.joining(" ")));
162 throw new RFXComUnsupportedChannelException("Nothing relevant for " + channelId);
167 public void setSubType(SubType subType) {
168 this.subType = subType;
172 public void setDeviceId(String deviceId) {
173 // Nothing to do here
177 public void convertFromState(String channelId, Type type)
178 throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
180 case CHANNEL_RAW_MESSAGE:
181 case CHANNEL_RAW_PAYLOAD:
183 throw new RFXComUnsupportedChannelException("Cannot send on channel " + channelId);
185 case CHANNEL_COMMAND:
186 if (type instanceof OnOffType) {
187 if (type == OnOffType.ON) {
188 this.pulses = config.onPulsesArray;
190 this.pulses = config.offPulsesArray;
192 } else if (type instanceof OpenClosedType) {
193 if (type == OpenClosedType.OPEN) {
194 this.pulses = config.openPulsesArray;
196 this.pulses = config.closedPulsesArray;
199 throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
202 if (this.pulses == null) {
203 throw new RFXComInvalidStateException(channelId, null,
204 "No pulses provided in the device configuration for command" + type);
210 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
215 public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
216 return ByteEnumUtil.convertSubType(SubType.class, subType);