diff --git a/bundles/org.openhab.binding.rfxcom/README.md b/bundles/org.openhab.binding.rfxcom/README.md index 62f89fe0d3db5..b82ae0dc82c91 100644 --- a/bundles/org.openhab.binding.rfxcom/README.md +++ b/bundles/org.openhab.binding.rfxcom/README.md @@ -174,7 +174,7 @@ This binding currently supports following channel types: | rainrate | Number | Rain fall rate in millimeters per hour. | | raintotal | Number | Total rain in millimeters. | | rawmessage | String | Hexadecimal representation of the raw RFXCOM msg incl. header and payload | -| rawpayload | String | Hexadecimal representation of payload RFXCOM messages | +| rawpayload | String | Hexadecimal representation of the payload of RFXCOM messages | | setpoint | Number | Requested temperature. | | shutter | Rollershutter | Shutter/blind channel. | | status | String | Status channel. | @@ -229,6 +229,7 @@ This binding currently supports the following things / message types: * [lighting5 - RFXCOM Lighting5 Actuator](#lighting5---rfxcom-lighting5-actuator) * [lighting6 - RFXCOM Lighting6 Actuator](#lighting6---rfxcom-lighting6-actuator) * [rain - RFXCOM Rain Sensor](#rain---rfxcom-rain-sensor) +* [raw - RFXCOM Raw Messages](#raw---rfxcom-raw-messages) * [rfxsensor - RFXCOM rfxsensor](#rfxsensor) * [rfy - RFXCOM Rfy Actuator](#rfy---rfxcom-rfy-actuator) * [security1 - RFXCOM Security1 Sensor](#security1---rfxcom-security1-sensor) @@ -884,6 +885,31 @@ A Rain device * RAIN6 - La Crosse TX5 * RAIN9 - TFA 30.3233.1 +### raw - RFXCOM Raw Messages + +Raw messages. + +#### Channels + +| Name | Channel Type | Item Type | Remarks | +|------------|---------------------------|-----------|-------------| +| rawMessage | [rawmessage](#channels) | String | | +| rawPayload | [rawpayload](#channels) | String | | + +#### Configuration Options + +* deviceId - Device Id + * Raw items cannot provide a device ID, so this value is always RAW. + +* subType - Sub Type + * Specifies message sub type. + + * RAW_PACKET1 + * RAW_PACKET2 + * RAW_PACKET3 + * RAW_PACKET4 + + ### rfxsensor - RFXCOM RFXSensor A RFXSensor sensor diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java index c558cdc223f25..d300abab5a258 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java @@ -147,6 +147,7 @@ public class RFXComBindingConstants { private static final ThingTypeUID THING_TYPE_POWER = new ThingTypeUID(BINDING_ID, "power"); private static final ThingTypeUID THING_TYPE_RADIATOR1 = new ThingTypeUID(BINDING_ID, "radiator1"); private static final ThingTypeUID THING_TYPE_RAIN = new ThingTypeUID(BINDING_ID, "rain"); + private static final ThingTypeUID THING_TYPE_RAW = new ThingTypeUID(BINDING_ID, "raw"); private static final ThingTypeUID THING_TYPE_REMOTE_CONTROL = new ThingTypeUID(BINDING_ID, "remotecontrol"); private static final ThingTypeUID THING_TYPE_RFX_METER = new ThingTypeUID(BINDING_ID, "rfxmeter"); private static final ThingTypeUID THING_TYPE_RFX_SENSOR = new ThingTypeUID(BINDING_ID, "rfxsensor"); @@ -171,19 +172,20 @@ public class RFXComBindingConstants { /** * Presents all supported Thing types by RFXCOM binding. */ - public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream.of( - THING_TYPE_BAROMETRIC, THING_TYPE_BBQ_TEMPERATURE, THING_TYPE_BLINDS1, THING_TYPE_CAMERA1, THING_TYPE_CHIME, - THING_TYPE_CURRENT, THING_TYPE_CURRENT_ENERGY, THING_TYPE_CURTAIN1, THING_TYPE_DATE_TIME, THING_TYPE_ENERGY, - THING_TYPE_FAN, THING_TYPE_FAN_SF01, THING_TYPE_FAN_ITHO, THING_TYPE_FAN_SEAV, THING_TYPE_FAN_LUCCI_DC, - THING_TYPE_FAN_FT1211R, THING_TYPE_FAN_FALMEC, THING_TYPE_FAN_LUCCI_DC_II, THING_TYPE_GAS_USAGE, - THING_TYPE_HOME_CONFORT, THING_TYPE_HUMIDITY, THING_TYPE_IO_LINES, THING_TYPE_LIGHTNING1, - THING_TYPE_LIGHTNING2, THING_TYPE_LIGHTNING3, THING_TYPE_LIGHTNING4, THING_TYPE_LIGHTNING5, - THING_TYPE_LIGHTNING6, THING_TYPE_POWER, THING_TYPE_RADIATOR1, THING_TYPE_RAIN, THING_TYPE_REMOTE_CONTROL, - THING_TYPE_RFX_METER, THING_TYPE_RFX_SENSOR, THING_TYPE_RFY, THING_TYPE_SECURITY1, THING_TYPE_SECURITY2, - THING_TYPE_TEMPERATURE, THING_TYPE_TEMPERATURE_HUMIDITY, THING_TYPE_TEMPERATURE_HUMIDITY_BAROMETRIC, - THING_TYPE_TEMPERATURE_RAIN, THING_TYPE_THERMOSTAT1, THING_TYPE_THERMOSTAT2, THING_TYPE_THERMOSTAT3, - THING_TYPE_UNDECODED, THING_TYPE_UV, THING_TYPE_WATER_USAGE, THING_TYPE_WEIGHTING_SCALE, THING_TYPE_WIND) - .collect(Collectors.toSet())); + public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(THING_TYPE_BAROMETRIC, THING_TYPE_BBQ_TEMPERATURE, THING_TYPE_BLINDS1, + THING_TYPE_CAMERA1, THING_TYPE_CHIME, THING_TYPE_CURRENT, THING_TYPE_CURRENT_ENERGY, + THING_TYPE_CURTAIN1, THING_TYPE_DATE_TIME, THING_TYPE_ENERGY, THING_TYPE_FAN, THING_TYPE_FAN_SF01, + THING_TYPE_FAN_ITHO, THING_TYPE_FAN_SEAV, THING_TYPE_FAN_LUCCI_DC, THING_TYPE_FAN_FT1211R, + THING_TYPE_FAN_FALMEC, THING_TYPE_FAN_LUCCI_DC_II, THING_TYPE_GAS_USAGE, THING_TYPE_HOME_CONFORT, + THING_TYPE_HUMIDITY, THING_TYPE_IO_LINES, THING_TYPE_LIGHTNING1, THING_TYPE_LIGHTNING2, + THING_TYPE_LIGHTNING3, THING_TYPE_LIGHTNING4, THING_TYPE_LIGHTNING5, THING_TYPE_LIGHTNING6, + THING_TYPE_POWER, THING_TYPE_RADIATOR1, THING_TYPE_RAIN, THING_TYPE_RAW, THING_TYPE_REMOTE_CONTROL, + THING_TYPE_RFX_METER, THING_TYPE_RFX_SENSOR, THING_TYPE_RFY, THING_TYPE_SECURITY1, + THING_TYPE_SECURITY2, THING_TYPE_TEMPERATURE, THING_TYPE_TEMPERATURE_HUMIDITY, + THING_TYPE_TEMPERATURE_HUMIDITY_BAROMETRIC, THING_TYPE_TEMPERATURE_RAIN, THING_TYPE_THERMOSTAT1, + THING_TYPE_THERMOSTAT2, THING_TYPE_THERMOSTAT3, THING_TYPE_UNDECODED, THING_TYPE_UV, + THING_TYPE_WATER_USAGE, THING_TYPE_WEIGHTING_SCALE, THING_TYPE_WIND).collect(Collectors.toSet())); /** * Map RFXCOM packet types to RFXCOM Thing types and vice versa. @@ -223,6 +225,7 @@ public class RFXComBindingConstants { put(PacketType.POWER, RFXComBindingConstants.THING_TYPE_POWER); put(PacketType.RADIATOR1, RFXComBindingConstants.THING_TYPE_RADIATOR1); put(PacketType.RAIN, RFXComBindingConstants.THING_TYPE_RAIN); + put(PacketType.RAW, RFXComBindingConstants.THING_TYPE_RAW); put(PacketType.REMOTE_CONTROL, RFXComBindingConstants.THING_TYPE_REMOTE_CONTROL); put(PacketType.RFXMETER, RFXComBindingConstants.THING_TYPE_RFX_METER); put(PacketType.RFXSENSOR, RFXComBindingConstants.THING_TYPE_RFX_SENSOR); diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComBaseMessage.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComBaseMessage.java index dd54535c552a5..1f49b97f339b9 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComBaseMessage.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComBaseMessage.java @@ -83,6 +83,7 @@ public enum PacketType implements ByteEnumWrapper { RFXSENSOR(112), RFXMETER(113), FS20(114), + RAW(127), IO_LINES(128); private final int packetType; @@ -163,7 +164,7 @@ public String toString() { } str += ", Packet type = " + packetType; - str += ", Seq number = " + (short) (seqNbr & 0xFF); + str += ", Seq number = " + Byte.toUnsignedInt(seqNbr); return str; } diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessageFactory.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessageFactory.java index 4c759a17166e8..0a3ec7d47bea6 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessageFactory.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessageFactory.java @@ -84,6 +84,7 @@ public class RFXComMessageFactory { put(PacketType.RFXSENSOR, RFXComRFXSensorMessage.class); // put(PacketType.RFXMETER, RFXComRFXMeterMessage.class); // put(PacketType.FS20, RFXComFS20Message.class); + put(PacketType.RAW, RFXComRawMessage.class); // put(PacketType.IO_LINES, RFXComIOLinesMessage.class); } }); diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComRawMessage.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComRawMessage.java new file mode 100644 index 0000000000000..c4f1ed56129c1 --- /dev/null +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComRawMessage.java @@ -0,0 +1,184 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.rfxcom.internal.messages; + +import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*; +import static org.openhab.binding.rfxcom.internal.messages.RFXComBaseMessage.PacketType.RAW; + +import java.nio.ByteBuffer; + +import org.openhab.binding.rfxcom.internal.exceptions.RFXComException; +import org.openhab.binding.rfxcom.internal.exceptions.RFXComMessageTooLongException; +import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException; +import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException; +import org.openhab.binding.rfxcom.internal.handler.DeviceState; +import org.openhab.core.library.types.StringType; +import org.openhab.core.types.State; +import org.openhab.core.types.Type; +import org.openhab.core.util.HexUtils; + +/** + * RFXCOM data class for raw messages. + * + * @author James Hewitt-Thomas - New addition to the PRO RFXCom firmware + */ +public class RFXComRawMessage extends RFXComDeviceMessageImpl { + + public enum SubType implements ByteEnumWrapper { + RAW_PACKET1(0x00), + RAW_PACKET2(0x01), + RAW_PACKET3(0x02), + RAW_PACKET4(0x03), + + UNKNOWN(0xFF); + + private final int subType; + + SubType(int subType) { + this.subType = subType; + } + + @Override + public byte toByte() { + return (byte) subType; + } + + public static SubType fromByte(int input) { + for (SubType c : SubType.values()) { + if (c.subType == input) { + return c; + } + } + + return SubType.UNKNOWN; + } + } + + public SubType subType; + public byte repeat; + public short[] pulses; + + public RFXComRawMessage() { + super(RAW); + pulses = new short[0]; + } + + public RFXComRawMessage(byte[] message) throws RFXComException { + encodeMessage(message); + } + + @Override + public String toString() { + String str = super.toString(); + + str += ", Sub type = " + subType; + + return str; + } + + @Override + public void encodeMessage(byte[] message) throws RFXComException { + super.encodeMessage(message); + + final int pulsesByteLen = rawMessage.length - 5; + if (pulsesByteLen % 4 != 0) { + throw new RFXComException("Incorrect byte length for pulses - must be divisible by 4"); + } + + subType = SubType.fromByte(super.subType); + repeat = rawMessage[4]; + pulses = new short[pulsesByteLen / 2]; + ByteBuffer.wrap(rawMessage, 5, rawMessage.length - 5).asShortBuffer().get(pulses); + } + + @Override + public byte[] decodeMessage() throws RFXComException { + if (pulses.length > 124) { + throw new RFXComMessageTooLongException("Longest payload according to RFXtrx SDK is 124 shorts."); + } + + final int pulsesByteLen = pulses.length * 2; + byte[] data = new byte[5 + pulsesByteLen]; + + data[0] = (byte) (data.length - 1); + data[1] = RAW.toByte(); + data[2] = subType.toByte(); + data[3] = seqNbr; + data[4] = repeat; + + ByteBuffer.wrap(data, 5, pulsesByteLen).asShortBuffer().put(pulses); + + return data; + } + + @Override + public String getDeviceId() { + return "RAW"; + } + + @Override + public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException { + switch (channelId) { + case CHANNEL_RAW_MESSAGE: + return new StringType(HexUtils.bytesToHex(rawMessage)); + + case CHANNEL_RAW_PAYLOAD: + byte[] payload = new byte[pulses.length * 2]; + ByteBuffer.wrap(payload).asShortBuffer().put(pulses); + return new StringType(HexUtils.bytesToHex(payload)); + + default: + throw new RFXComUnsupportedChannelException("Nothing relevant for " + channelId); + } + } + + @Override + public void setSubType(SubType subType) { + throw new UnsupportedOperationException(); + } + + @Override + public void setDeviceId(String deviceId) { + throw new UnsupportedOperationException(); + } + + @Override + public void convertFromState(String channelId, Type type) throws RFXComUnsupportedChannelException { + switch (channelId) { + case CHANNEL_RAW_MESSAGE: + if (type instanceof StringType) { + // TODO: Check the raw message for validity (length, no more than 124 shorts, multiple of 4 bytes in + // payload) + throw new RFXComUnsupportedChannelException("Channel " + channelId + " inot yet implemented"); + } else { + throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type); + } + + case CHANNEL_RAW_PAYLOAD: + if (type instanceof StringType) { + // TODO: Check the payload for validity (no more than 124 shorts, multiple of 4 bytes + throw new RFXComUnsupportedChannelException("Channel " + channelId + " not yet implemented"); + } else { + throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type); + } + + default: + throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here"); + } + } + + @Override + public SubType convertSubType(String subType) throws RFXComUnsupportedValueException { + return ByteEnumUtil.convertSubType(SubType.class, subType); + } +} diff --git a/bundles/org.openhab.binding.rfxcom/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.rfxcom/src/main/resources/OH-INF/thing/channels.xml index 30af6f83af301..3707b8d7f5079 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.rfxcom/src/main/resources/OH-INF/thing/channels.xml @@ -9,13 +9,13 @@ String - Hexadecimal representation of undecoded RFXCOM messages including header and payload + Hexadecimal representation of RFXCOM messages including header and payload String - Hexadecimal representation of payload of undecoded RFXCOM messages + Hexadecimal representation of payload of raw and undecoded RFXCOM messages diff --git a/bundles/org.openhab.binding.rfxcom/src/main/resources/OH-INF/thing/raw.xml b/bundles/org.openhab.binding.rfxcom/src/main/resources/OH-INF/thing/raw.xml new file mode 100644 index 0000000000000..52618f3d11529 --- /dev/null +++ b/bundles/org.openhab.binding.rfxcom/src/main/resources/OH-INF/thing/raw.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + Raw messages. + + + + + + + + + + Raw items cannot provide a device ID, so this value is always RAW. + + + + Specifies device sub type. + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComRawMessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComRawMessageTest.java new file mode 100644 index 0000000000000..f3baebb59bda8 --- /dev/null +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComRawMessageTest.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.rfxcom.internal.messages; + +import static org.junit.jupiter.api.Assertions.*; + +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.rfxcom.internal.exceptions.RFXComException; +import org.openhab.binding.rfxcom.internal.exceptions.RFXComMessageTooLongException; +import org.openhab.binding.rfxcom.internal.messages.RFXComBaseMessage.PacketType; +import org.openhab.core.util.HexUtils; + +/** + * Test for RFXCom-binding + * + * @author James Hewitt-Thomas - New addition to the PRO RFXCom firmware + */ +@NonNullByDefault +public class RFXComRawMessageTest { + + private void testMessage(String hexMsg, RFXComRawMessage.SubType subType, int seqNbr, int repeat, String pulses) + throws RFXComException { + final RFXComRawMessage msg = (RFXComRawMessage) RFXComMessageFactory.createMessage(HexUtils.hexToBytes(hexMsg)); + assertEquals(subType, msg.subType, "SubType"); + assertEquals(seqNbr, (short) (msg.seqNbr & 0xFF), "Seq Number"); + assertEquals("RAW", msg.getDeviceId(), "Device Id"); + assertEquals(repeat, msg.repeat, "Repeat"); + byte[] payload = new byte[msg.pulses.length * 2]; + ByteBuffer.wrap(payload).asShortBuffer().put(msg.pulses); + assertEquals(pulses, HexUtils.bytesToHex(payload), "Pulses"); + + byte[] decoded = msg.decodeMessage(); + + assertEquals(hexMsg, HexUtils.bytesToHex(decoded), "Message converted back"); + } + + @Test + public void testSomeMessages() throws RFXComException { + testMessage("087F0027051356ECC0", RFXComRawMessage.SubType.RAW_PACKET1, 0x27, 5, "1356ECC0"); + } + + @Test + public void testLongMessage() throws RFXComException { + RFXComRawMessage msg = (RFXComRawMessage) RFXComMessageFactory.createMessage(PacketType.RAW); + msg.subType = RFXComRawMessage.SubType.RAW_PACKET1; + msg.seqNbr = 1; + msg.repeat = 5; + msg.pulses = new short[125]; + + assertThrows(RFXComMessageTooLongException.class, () -> msg.decodeMessage()); + } +}