diff --git a/bundles/org.openhab.binding.enocean/README.md b/bundles/org.openhab.binding.enocean/README.md index 1d98a619c3eef..31f3e2ea41060 100644 --- a/bundles/org.openhab.binding.enocean/README.md +++ b/bundles/org.openhab.binding.enocean/README.md @@ -13,9 +13,15 @@ This binding can enable the repeater function (level 1 or 2) of these gateways a First of all you have to configure an EnOcean transceiver (gateway). A directly connected USB300 can be auto discovered, an EnOceanPi has to be added manually to openHAB. Both gateways are represented by an _EnOcean gateway_ in openHAB. -If you want to place the gateway for better reception apart from your openHAB server, you can forward its serial messages over TCP/IP (_ser2net_). In this case you have to define the path to the gateway like this rfc2217://x.x.x.x:3001. +If you want to place the gateway for better reception apart from your openHAB server, you can forward its serial messages over TCP/IP (_ser2net_). +In this case you have to define the path to the gateway like this rfc2217://x.x.x.x:3001. If everything is running fine you should see the _base id_ of your gateway in the properties of your bridge. +Another way to improve sending and reception reliability is to setup a wired connection. +In this case you directly connect to your RS485 EnOcean bus (use USB connection of an Eltako FAM14 e.g.). +However communication on RS485 bus is mostly done with an older ESP2 protocol which does not support advanced telegrams like VLD. +Furthermore you have to be aware that not all radio telegrams are published on the bus. + The vast majority of EnOcean messages are sent as broadcast messages without an explicit receiver address. However each EnOcean device is identified by an unique id, called EnOceanId, which is used as the sender address in these messages. To receive messages from an EnOcean device you have to determine its EnOceanId and add an appropriate thing to openHAB. @@ -92,7 +98,7 @@ Hence if your device supports one of the following EEPs the chances are good tha Furthermore following supporting EEP family is available too: A5-11, types 0x03 (rollershutter position status) and 0x04 (extended light status). A `rockerSwitch` is used to receive messages from a physical EnOcean Rocker Switch. -A `classicDevice` is used to for older EnOcean devices which react only on rocker switch messages (like Opus GN-A-R12V-SR-4). +A `classicDevice` is used for older EnOcean devices which react only on rocker switch messages (like Opus GN-A-R12V-SR-4). As these devices do not send their current status, you have to add additional listener channels for each physical Rocker Switch to your thing. In this way you can still sync your item status with the physical status of your device whenever it gets modified by a physical rocker switch. The classic device simulates a physical Rocker Switch. @@ -147,6 +153,9 @@ If you change the SenderId of your thing, you have to pair again the thing with |---------------------------------|-------------------|-----------------------------|---| | bridge | path | Path to the EnOcean Gateway | COM3, /dev/ttyAMA0, rfc2217://x.x.x.x:3001 | | | nextSenderId | Set SenderId of next created thing.
If omitted, the next unused SenderId is taken | 1-127 | +| | espVersion | ESP Version of gateway | ESP3, ESP2 | +| | rs485 | If gateway is directly connected to a RS485 bus the BaseId is set to 0x00 | true, false +| | rs485BaseId | Override BaseId 0x00 if your bus contains a telegram duplicator (FTD14 for ex) | 4 byte hex value | | pushButton | receivingEEPId | EEP used for receiving msg | F6_01_01, D2_03_0A | | | enoceanId | EnOceanId of device this thing belongs to | hex value as string | | rockerSwitch | receivingEEPId | | F6_02_01, F6_02_02 | diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java index 2766e55e99b23..25d409d68c7da 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java @@ -384,4 +384,5 @@ public class EnOceanBindingConstants { public static final String EMPTYENOCEANID = "00000000"; + public static final byte ZERO = (byte) 0; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/Helper.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/Helper.java new file mode 100644 index 0000000000000..87f06db03c951 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/Helper.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2010-2020 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.enocean.internal; + +import java.util.Arrays; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class Helper { + + public static byte[] concatAll(byte[] a, byte[]... rest) { + if(rest == null) { + return a; + } + + int totalLength = a.length; + for (byte[] b : rest) { + if (b != null) { + totalLength += b.length; + } + } + + byte[] result = Arrays.copyOf(a, totalLength); + int offset = a.length; + for (byte[] array : rest) { + if (array != null) { + System.arraycopy(array, 0, result, offset, array.length); + offset += array.length; + } + } + return result; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBridgeConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBridgeConfig.java new file mode 100644 index 0000000000000..698e10459d62d --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBridgeConfig.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2020 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.enocean.internal.config; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class EnOceanBridgeConfig { + + public enum ESPVersion { + UNKNOWN("unknown"), + ESP3("ESP3"), + ESP2("ESP2"); + + private String version; + + ESPVersion(String version) { + this.version = version; + } + + public static ESPVersion getESPVersion(String espVersion) { + for (ESPVersion version : values()) { + if (version.version.equalsIgnoreCase (espVersion)) { + return version; + } + } + + return ESPVersion.UNKNOWN; + } + } + + public String path; + + public String espVersion; + public boolean rs485; + public String rs485BaseId; + + public int nextSenderId = 0; + + public EnOceanBridgeConfig() { + espVersion = "ESP3"; + } + + public ESPVersion getESPVersion() { + return ESPVersion.getESPVersion(espVersion); + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java index 8157ea80f0d22..5b153011a0bd5 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java @@ -27,10 +27,10 @@ import org.openhab.binding.enocean.internal.eep.Base.UTEResponse; import org.openhab.binding.enocean.internal.eep.Base._4BSMessage; import org.openhab.binding.enocean.internal.handler.EnOceanBridgeHandler; +import org.openhab.binding.enocean.internal.messages.BasePacket; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; -import org.openhab.binding.enocean.internal.transceiver.ESP3PacketListener; +import org.openhab.binding.enocean.internal.transceiver.PacketListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +40,7 @@ * @author Daniel Weber - Initial contribution */ -public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService implements ESP3PacketListener { +public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService implements PacketListener { private final Logger logger = LoggerFactory.getLogger(EnOceanDeviceDiscoveryService.class); private EnOceanBridgeHandler bridgeHandler; @@ -89,7 +89,7 @@ public synchronized void stopScan() { } @Override - public void espPacketReceived(ESP3Packet packet) { + public void packetReceived(BasePacket packet) { ERP1Message msg = (ERP1Message) packet; logger.info("EnOcean Package discovered, RORG {}, payload {}, additional {}", msg.getRORG().name(), @@ -97,6 +97,7 @@ public void espPacketReceived(ESP3Packet packet) { EEP eep = EEPFactory.buildEEPFromTeachInERP1(msg); if (eep == null) { + logger.debug("Could not build EEP for received package"); return; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java index d24920c3293da..cf20bfdd7b0ba 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java @@ -67,9 +67,9 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co switch (channelId) { case CHANNEL_ROLLERSHUTTER: - byte db0 = Zero | SEND_NEW_STATE | TeachInBit; - byte db1 = Zero; - byte db2 = Zero; + byte db0 = ZERO | SEND_NEW_STATE | TeachInBit; + byte db1 = ZERO; + byte db2 = ZERO; byte position; byte angle = 0; // for now, no angle configuration supported @@ -80,7 +80,7 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co } else if (outputCommand instanceof OnOffType) { position = (byte) (((OnOffType) outputCommand == OnOffType.ON) ? 0 : 100); } else if (outputCommand instanceof StopMoveType) { - position = Zero; + position = ZERO; doStop = true; } else if (outputCommand instanceof UpDownType) { position = (byte) (((UpDownType) outputCommand == UpDownType.UP) ? 0 : 100); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java index fbd6e1702492c..22e5af7e3da27 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java @@ -13,6 +13,7 @@ package org.openhab.binding.enocean.internal.eep.A5_38; import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.CHANNEL_DIMMER; +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.ZERO; import java.util.function.Function; @@ -62,20 +63,20 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co if (outputCommand instanceof DecimalType) { dimmValue = ((DecimalType) outputCommand).byteValue(); } else if (outputCommand instanceof OnOffType) { - dimmValue = ((OnOffType) outputCommand == OnOffType.ON) ? Switch100Percent : Zero; + dimmValue = ((OnOffType) outputCommand == OnOffType.ON) ? Switch100Percent : ZERO; } else if (outputCommand instanceof IncreaseDecreaseType) { dimmValue = ((IncreaseDecreaseType) outputCommand == IncreaseDecreaseType.INCREASE) ? Switch100Percent - : Zero; + : ZERO; } else if (outputCommand instanceof UpDownType) { - dimmValue = ((UpDownType) outputCommand == UpDownType.UP) ? Switch100Percent : Zero; + dimmValue = ((UpDownType) outputCommand == UpDownType.UP) ? Switch100Percent : ZERO; } else { throw new IllegalArgumentException(outputCommand.toFullString() + " is no valid dimming command."); } EnOceanChannelDimmerConfig c = config.as(EnOceanChannelDimmerConfig.class); - byte storeByte = 0x00; // "Store final value" (standard) vs. "block value" (Eltako) + byte storeByte = ZERO; // "Store final value" (standard) vs. "block value" (Eltako) if (!c.eltakoDimmer) { dimmValue *= 2.55; // 0-100% = 0-255 @@ -90,7 +91,7 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co } byte rampingTime = Integer.valueOf(c.rampingTime).byteValue(); - byte switchingCommand = (dimmValue == Zero) ? SwitchOff : SwitchOn; + byte switchingCommand = (dimmValue == ZERO) ? SwitchOff : SwitchOn; setData(CommandId, dimmValue, rampingTime, (byte) (TeachInBit | storeByte | switchingCommand)); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java index 85c0bdfb2ba9f..041014f08cbf9 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java @@ -13,6 +13,7 @@ package org.openhab.binding.enocean.internal.eep.A5_38; import java.util.function.Function; +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.ZERO; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -44,9 +45,9 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co Function getCurrentStateFunc, Configuration config) { if ((OnOffType) outputCommand == OnOffType.ON) { - setData(CommandId, Zero, Zero, (byte) (TeachInBit | SwitchOn)); + setData(CommandId, ZERO, ZERO, (byte) (TeachInBit | SwitchOn)); } else { - setData(CommandId, Zero, Zero, (byte) (TeachInBit | SwitchOff)); + setData(CommandId, ZERO, ZERO, (byte) (TeachInBit | SwitchOff)); } } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java index 91e18f121da3a..201fc8749d244 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java @@ -13,6 +13,7 @@ package org.openhab.binding.enocean.internal.eep.A5_3F; import java.util.function.Function; +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.ZERO; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.PercentType; @@ -60,9 +61,9 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co PercentType target = (PercentType) command; if (target.intValue() == PercentType.ZERO.intValue()) { - setData(Zero, (byte) shutTime, MoveUp, TeachInBit); // => move completely up + setData(ZERO, (byte) shutTime, MoveUp, TeachInBit); // => move completely up } else if (target.intValue() == PercentType.HUNDRED.intValue()) { - setData(Zero, (byte) shutTime, MoveDown, TeachInBit); // => move completely down + setData(ZERO, (byte) shutTime, MoveDown, TeachInBit); // => move completely down } else if (channelState != null) { PercentType current = channelState.as(PercentType.class); if (config != null && current != null) { @@ -72,20 +73,20 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co (Math.abs(current.intValue() - target.intValue()) * shutTime) / PercentType.HUNDRED.intValue()); - setData(Zero, duration, direction, TeachInBit); + setData(ZERO, duration, direction, TeachInBit); } } } } else if (command instanceof UpDownType) { if ((UpDownType) command == UpDownType.UP) { - setData(Zero, (byte) shutTime, MoveUp, TeachInBit); // => 0 percent + setData(ZERO, (byte) shutTime, MoveUp, TeachInBit); // => 0 percent } else if ((UpDownType) command == UpDownType.DOWN) { - setData(Zero, (byte) shutTime, MoveDown, TeachInBit); // => 100 percent + setData(ZERO, (byte) shutTime, MoveDown, TeachInBit); // => 100 percent } } else if (command instanceof StopMoveType) { if ((StopMoveType) command == StopMoveType.STOP) { - setData(Zero, (byte) 0xFF, Stop, TeachInBit); + setData(ZERO, (byte) 0xFF, Stop, TeachInBit); } } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java index 3c0549c4141c2..b5250bd0fd19a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.eep.Base; +import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; + import org.openhab.binding.enocean.internal.messages.ERP1Message; /** @@ -27,9 +29,9 @@ public class UTEResponse extends _VLDMessage { public UTEResponse(ERP1Message packet) { - int dataLength = packet.getPayload().length - SenderIdLength - RORGLength - StatusLength; + int dataLength = packet.getPayload().length - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH - ESP3_STATUS_LENGTH; - setData(packet.getPayload(RORGLength, dataLength)); + setData(packet.getPayload(ESP3_RORG_LENGTH, dataLength)); bytes[0] = (byte) 0x91; // bidirectional communication, teach in accepted, teach in response setStatus((byte) 0x80); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java index 5d949b30cd7b4..248bbbcfb0c59 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.eep.Base; +import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; + import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; @@ -22,7 +24,7 @@ public class _4BSTeachInVariation3Response extends _4BSMessage { public _4BSTeachInVariation3Response(ERP1Message packet) { - byte[] payload = packet.getPayload(RORGLength, RORG._4BS.getDataLength()); + byte[] payload = packet.getPayload(ESP3_RORG_LENGTH, RORG._4BS.getDataLength()); payload[3] = (byte) 0xF0; // telegram with EEP number and Manufacturer ID, // EEP supported, Sender ID stored, Response diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java index 1d6cbfa90ee2e..4e5d275d59732 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.eep.Base; +import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; + import org.openhab.binding.enocean.internal.eep.EEP; import org.openhab.binding.enocean.internal.messages.ERP1Message; @@ -32,7 +34,7 @@ public _VLDMessage(ERP1Message packet) { @Override protected int getDataLength() { if (packet != null) { - return packet.getPayload().length - SenderIdLength - RORGLength - StatusLength; + return packet.getPayload().length - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH - ESP3_STATUS_LENGTH; } else { return bytes.length; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_03/D2_03_0A.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_03/D2_03_0A.java index 8e94d6c3c9e81..cdc7e119d3ec9 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_03/D2_03_0A.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_03/D2_03_0A.java @@ -17,7 +17,6 @@ import java.util.function.Function; import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.eclipse.smarthome.core.thing.CommonTriggerEvents; diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java index b74d8fbdaa345..022a559901dc6 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java @@ -13,6 +13,7 @@ package org.openhab.binding.enocean.internal.eep; import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; import java.util.Arrays; import java.util.function.Function; @@ -26,9 +27,9 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.enocean.internal.Helper; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; -import org.openhab.binding.enocean.internal.transceiver.Helper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,11 +39,6 @@ */ public abstract class EEP { - public static final byte Zero = 0x00; - protected static final int SenderIdLength = 4; - protected static final int StatusLength = 1; - protected static final int RORGLength = 1; - protected RORG newRORG = RORG.Unknown; protected byte[] bytes; protected byte[] optionalData; @@ -71,9 +67,9 @@ public EEP(ERP1Message packet) { // Todo validation?? this.packet = packet; - setData(packet.getPayload(RORGLength, getDataLength())); - setSenderId(packet.getPayload(RORGLength + getDataLength(), SenderIdLength)); - setStatus(packet.getPayload(RORGLength + getDataLength() + SenderIdLength, 1)[0]); + setData(packet.getPayload(ESP3_RORG_LENGTH, getDataLength())); + setSenderId(packet.getPayload(ESP3_RORG_LENGTH + getDataLength(), ESP3_SENDERID_LENGTH)); + setStatus(packet.getPayload(ESP3_RORG_LENGTH + getDataLength() + ESP3_SENDERID_LENGTH, 1)[0]); setOptionalData(packet.getOptionalPayload()); } @@ -154,7 +150,7 @@ public EEP setOptionalData(byte... bytes) { } public EEP setSenderId(byte[] senderId) { - if (senderId == null || senderId.length != SenderIdLength) { + if (senderId == null || senderId.length != ESP3_SENDERID_LENGTH) { throw new IllegalArgumentException(); } @@ -180,25 +176,20 @@ public final ERP1Message getERP1Message() { optionalDataLength = optionalData.length; } - byte[] payLoad = new byte[RORGLength + getDataLength() + SenderIdLength + StatusLength + byte[] payLoad = new byte[ESP3_RORG_LENGTH + getDataLength() + ESP3_SENDERID_LENGTH + ESP3_STATUS_LENGTH + optionalDataLength]; - Arrays.fill(payLoad, Zero); + Arrays.fill(payLoad, ZERO); byte rorgValue = getEEPType().getRORG().getValue(); if (newRORG != RORG.Unknown) { rorgValue = newRORG.getValue(); } - payLoad[0] = rorgValue; - ERP1Message message = new ERP1Message(payLoad.length - optionalDataLength, optionalDataLength, payLoad); - - message.setPayload(Helper.concatAll(new byte[] { rorgValue }, bytes, senderId, - new byte[] { (byte) (status | (suppressRepeating ? 0b1111 : 0)) })); // set repeater count to max if - // suppressRepeating - - message.setOptionalPayload(optionalData); + // set repeater count to max if suppressRepeating + payLoad = Helper.concatAll(new byte[] { rorgValue }, bytes, senderId, + new byte[] { (byte) (status | (suppressRepeating ? 0b1111 : 0)) }, optionalData); - return message; + return new ERP1Message(payLoad.length - optionalDataLength, optionalDataLength, payLoad); } else { logger.warn("ERP1Message for EEP {} is not valid!", this.getClass().getName()); } @@ -211,7 +202,7 @@ protected boolean validateData(byte[] bytes) { } public boolean isValid() { - return validateData(bytes) && senderId != null && senderId.length == SenderIdLength; + return validateData(bytes) && senderId != null && senderId.length == ESP3_SENDERID_LENGTH; } protected EEPType getEEPType() { diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java index ec59cdba10f7c..62b99dc53d872 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.eep; +import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; + import java.lang.reflect.InvocationTargetException; import org.eclipse.smarthome.core.util.HexUtils; @@ -147,14 +149,15 @@ public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { case UTE: { byte[] payload = msg.getPayload(); - byte rorg = payload[payload.length - 1 - EEP.StatusLength - EEP.SenderIdLength]; - byte func = payload[payload.length - 1 - EEP.StatusLength - EEP.SenderIdLength - EEP.RORGLength]; - byte type = payload[payload.length - 1 - EEP.StatusLength - EEP.SenderIdLength - EEP.RORGLength - 1]; + byte rorg = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH]; + byte func = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH]; + byte type = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH + - 1]; - byte manufIdMSB = payload[payload.length - 1 - EEP.StatusLength - EEP.SenderIdLength - EEP.RORGLength - - 2]; - byte manufIdLSB = payload[payload.length - 1 - EEP.StatusLength - EEP.SenderIdLength - EEP.RORGLength - - 3]; + byte manufIdMSB = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH + - ESP3_RORG_LENGTH - 2]; + byte manufIdLSB = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH + - ESP3_RORG_LENGTH - 3]; int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff); EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java index 293cda34c8cf9..28d25686aee84 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java @@ -13,6 +13,7 @@ package org.openhab.binding.enocean.internal.eep.Generic; import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.PARAMETER_EEPID; +import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*; import java.lang.reflect.InvocationTargetException; import java.util.Collections; @@ -149,7 +150,7 @@ protected State convertToStateImpl(String channelId, String channelTypeId, Funct @Override protected int getDataLength() { if (packet != null) { - return packet.getPayload().length - SenderIdLength - RORGLength - StatusLength; + return packet.getPayload().length - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH - ESP3_STATUS_LENGTH; } else { return bytes.length; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java index f594a06f3a4e2..72d214ad48da5 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java @@ -39,7 +39,7 @@ import org.openhab.binding.enocean.internal.eep.EEP; import org.openhab.binding.enocean.internal.eep.EEPFactory; import org.openhab.binding.enocean.internal.eep.EEPType; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; +import org.openhab.binding.enocean.internal.messages.BasePacket; /** * @@ -249,7 +249,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { EEP eep = EEPFactory.createEEP(sendingEEPType); if (eep.convertFromCommand(channelId, channelTypeId, command, id -> getCurrentState(id), channelConfig) .hasData()) { - ESP3Packet msg = eep.setSenderId(senderId) + BasePacket msg = eep.setSenderId(senderId) .setDestinationId(destinationId) .setSuppressRepeating(getConfiguration().suppressRepeating) .getERP1Message(); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java index 8ebd36466f763..a2ff4af0b7354 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java @@ -39,17 +39,17 @@ import org.openhab.binding.enocean.internal.eep.EEP; import org.openhab.binding.enocean.internal.eep.EEPFactory; import org.openhab.binding.enocean.internal.eep.EEPType; +import org.openhab.binding.enocean.internal.messages.BasePacket; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; -import org.openhab.binding.enocean.internal.transceiver.ESP3PacketListener; +import org.openhab.binding.enocean.internal.transceiver.PacketListener; /** * * @author Daniel Weber - Initial contribution * This class defines base functionality for receiving eep messages. */ -public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements ESP3PacketListener { +public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements PacketListener { // List of all thing types which support receiving of eep messages public final static Set SUPPORTED_THING_TYPES = new HashSet<>( @@ -151,7 +151,7 @@ protected Predicate channelFilter(EEPType eepType, byte[] senderId) { } @Override - public void espPacketReceived(ESP3Packet packet) { + public void packetReceived(BasePacket packet) { if (receivingEEPTypes == null) { return; diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java index e16ad208f770c..8c8581c93eb13 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java @@ -41,17 +41,19 @@ import org.eclipse.smarthome.io.transport.serial.SerialPortManager; import org.openhab.binding.enocean.internal.EnOceanConfigStatusMessage; import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig; +import org.openhab.binding.enocean.internal.config.EnOceanBridgeConfig; +import org.openhab.binding.enocean.internal.messages.BasePacket; import org.openhab.binding.enocean.internal.messages.BaseResponse; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; import org.openhab.binding.enocean.internal.messages.ESP3PacketFactory; import org.openhab.binding.enocean.internal.messages.RDBaseIdResponse; import org.openhab.binding.enocean.internal.messages.RDRepeaterResponse; import org.openhab.binding.enocean.internal.messages.RDVersionResponse; import org.openhab.binding.enocean.internal.messages.Response; import org.openhab.binding.enocean.internal.messages.Response.ResponseType; -import org.openhab.binding.enocean.internal.transceiver.ESP3PacketListener; -import org.openhab.binding.enocean.internal.transceiver.EnOceanSerialTransceiver; +import org.openhab.binding.enocean.internal.transceiver.EnOceanESP2Transceiver; +import org.openhab.binding.enocean.internal.transceiver.EnOceanESP3Transceiver; import org.openhab.binding.enocean.internal.transceiver.EnOceanTransceiver; +import org.openhab.binding.enocean.internal.transceiver.PacketListener; import org.openhab.binding.enocean.internal.transceiver.ResponseListener; import org.openhab.binding.enocean.internal.transceiver.ResponseListenerIgnoringTimeouts; import org.openhab.binding.enocean.internal.transceiver.TransceiverErrorListener; @@ -193,12 +195,22 @@ private synchronized void initTransceiver() { try { - Configuration c = getThing().getConfiguration(); + EnOceanBridgeConfig c = getThing().getConfiguration().as(EnOceanBridgeConfig.class); if (transceiver != null) { transceiver.ShutDown(); } - transceiver = new EnOceanSerialTransceiver((String) c.get(PATH), this, scheduler, serialPortManager); + switch (c.getESPVersion()) { + case ESP2: + transceiver = new EnOceanESP2Transceiver(c.path, this, scheduler, serialPortManager); + break; + case ESP3: + transceiver = new EnOceanESP3Transceiver(c.path, this, scheduler, serialPortManager); + break; + default: + break; + + } updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "opening serial port..."); transceiver.Initialize(); @@ -206,21 +218,31 @@ private synchronized void initTransceiver() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "starting rx thread..."); transceiver.StartReceiving(scheduler); - if ((boolean) c.get(RS485)) { - baseId = new byte[4]; + if (c.rs485) { + if (c.rs485BaseId != null && !c.rs485BaseId.isEmpty()) { + baseId = HexUtils.hexToBytes(c.rs485BaseId); + if (baseId.length != 4) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "RS485 BaseId has the wrong format. It is expected to be an 8 digit hex code, for example 01000000"); + } + } else { + baseId = new byte[4]; + } + + updateProperty(PROPERTY_BASE_ID, HexUtils.bytesToHex(baseId)); updateStatus(ThingStatus.ONLINE); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "trying to get bridge base id..."); + logger.debug("request base id"); - transceiver.sendESP3Packet(ESP3PacketFactory.CO_RD_IDBASE, + transceiver.sendBasePacket(ESP3PacketFactory.CO_RD_IDBASE, new ResponseListenerIgnoringTimeouts() { @Override public void responseReceived(RDBaseIdResponse response) { logger.debug("received response for base id"); - if (response.isValid() && response.isOK()) { baseId = response.getBaseId().clone(); updateProperty(PROPERTY_BASE_ID, HexUtils.bytesToHex(response.getBaseId())); @@ -237,7 +259,8 @@ public void responseReceived(RDBaseIdResponse response) { }); } - transceiver.sendESP3Packet(ESP3PacketFactory.CO_RD_VERSION, + logger.debug("request version info"); + transceiver.sendBasePacket(ESP3PacketFactory.CO_RD_VERSION, new ResponseListenerIgnoringTimeouts() { @Override @@ -251,7 +274,6 @@ public void responseReceived(RDVersionResponse response) { } } }); - } catch (IOException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Port could not be found"); } catch (PortInUseException e) { @@ -335,35 +357,35 @@ public void removeSender(int id) { sendingThings[id] = null; } - public void sendMessage(ESP3Packet message, ResponseListener responseListener) { + public void sendMessage(BasePacket message, ResponseListener responseListener) { try { - transceiver.sendESP3Packet(message, responseListener); + transceiver.sendBasePacket(message, responseListener); } catch (IOException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } - public void addPacketListener(ESP3PacketListener listener) { + public void addPacketListener(PacketListener listener) { addPacketListener(listener, listener.getSenderIdToListenTo()); } - public void addPacketListener(ESP3PacketListener listener, long senderIdToListenTo) { + public void addPacketListener(PacketListener listener, long senderIdToListenTo) { if (transceiver != null) { transceiver.addPacketListener(listener, senderIdToListenTo); } } - public void removePacketListener(ESP3PacketListener listener) { + public void removePacketListener(PacketListener listener) { removePacketListener(listener, listener.getSenderIdToListenTo()); } - public void removePacketListener(ESP3PacketListener listener, long senderIdToListenTo) { + public void removePacketListener(PacketListener listener, long senderIdToListenTo) { if (transceiver != null) { transceiver.removePacketListener(listener, senderIdToListenTo); } } - public void startDiscovery(ESP3PacketListener teachInListener) { + public void startDiscovery(PacketListener teachInListener) { transceiver.startDiscovery(teachInListener); } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java index 2e0b0ac7727d0..b89748b745aa0 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java @@ -45,7 +45,7 @@ import org.openhab.binding.enocean.internal.eep.EEP; import org.openhab.binding.enocean.internal.eep.EEPFactory; import org.openhab.binding.enocean.internal.eep.EEPType; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; +import org.openhab.binding.enocean.internal.messages.BasePacket; /** * @@ -237,7 +237,7 @@ public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command comma EEP eep = EEPFactory.createEEP(sendingEEPType); if (eep.setSenderId(senderId).setDestinationId(destinationId).convertFromCommand(channelId, channelTypeId, result, id -> this.getCurrentState(id), channel.getConfiguration()).hasData()) { - ESP3Packet press = eep.setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); + BasePacket press = eep.setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); getBridgeHandler().sendMessage(press, null); @@ -245,7 +245,7 @@ public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command comma releaseFuture = scheduler.schedule(() -> { if (eep.convertFromCommand(channelId, channelTypeId, convertToReleasedCommand(lastTriggerEvent), id -> this.getCurrentState(id), channel.getConfiguration()).hasData()) { - ESP3Packet release = eep.getERP1Message(); + BasePacket release = eep.getERP1Message(); getBridgeHandler().sendMessage(release, null); } }, channelConfig.duration, TimeUnit.MILLISECONDS); diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BasePacket.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BasePacket.java new file mode 100644 index 0000000000000..16b1b4cf5e590 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BasePacket.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2010-2020 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.enocean.internal.messages; + +import java.security.InvalidParameterException; +import java.util.Arrays; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public abstract class BasePacket { + + public enum ESPPacketType { + RADIO_ERP1((byte) 0x01), + RESPONSE((byte) 0x02), + RADIO_SUB_TEL((byte) 0x03), + EVENT((byte) 0x04), + COMMON_COMMAND((byte) 0x05), + SMART_ACK_COMMAND((byte) 0x06), + REMOTE_MAN_COMMAND((byte) 0x07), + RADIO_MESSAGE((byte) 0x09), + RADIO_ERP2((byte) 0x0A); + + private byte value; + + private ESPPacketType(byte value) { + this.value = value; + } + + public byte getValue() { + return value; + } + + public static boolean hasValue(byte value) { + for (ESPPacketType p : ESPPacketType.values()) { + if (p.value == value) { + return true; + } + } + + return false; + } + + public static ESPPacketType getPacketType(byte packetType) { + for (ESPPacketType p : ESPPacketType.values()) { + if (p.value == packetType) { + return p; + } + } + + throw new InvalidParameterException("Unknown packetType value"); + } + + } + + protected ESPPacketType packetType; + protected byte[] data; + protected byte[] optionalData; + + public BasePacket(int dataLength, int optionalDataLength, ESPPacketType packetType, byte[] payload) { + this(dataLength, optionalDataLength, packetType.value, payload); + } + + public BasePacket(int dataLength, int optionalDataLength, byte packetType, byte[] payload) { + + if (!ESPPacketType.hasValue(packetType)) { + throw new InvalidParameterException("Packet type is unknown"); + } + + if (dataLength + optionalDataLength > payload.length) { + throw new InvalidParameterException("data length does not match provided lengths"); + } + + this.packetType = ESPPacketType.getPacketType(packetType); + + this.data = new byte[dataLength]; + System.arraycopy(payload, 0, this.data, 0, dataLength); + + if (optionalDataLength > 0) { + this.optionalData = new byte[optionalDataLength]; + System.arraycopy(payload, dataLength, optionalData, 0, optionalDataLength); + } else { + this.optionalData = new byte[0]; + } + } + + public ESPPacketType getPacketType() { + return this.packetType; + } + + public byte[] getPayload(int offset, int length) { + return Arrays.copyOfRange(data, offset, offset + length); + } + + public byte[] getPayload() { + return data; + } + + public byte[] getOptionalPayload(int offset, int length) { + byte[] result = new byte[length]; + System.arraycopy(optionalData, offset, result, 0, length); + return result; + } + + public byte[] getOptionalPayload() { + return optionalData; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java index d6d63e3158218..8c82af85e2aa7 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.enocean.internal.messages; -import org.openhab.binding.enocean.internal.transceiver.Helper; +import org.openhab.binding.enocean.internal.Helper; /** * diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java index 6981d244f05fa..d506fac0276d1 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java @@ -16,10 +16,48 @@ * * @author Daniel Weber - Initial contribution */ -public class CCMessage extends ESP3Packet { +class CCMessage extends BasePacket { - public CCMessage(int dataLength, int optionalDataLength, byte[] payload) { - super(dataLength, optionalDataLength, ESPPacketType.COMMON_COMMAND, payload); + public enum CCMessageType { + CO_RD_VERSION((byte) 3, 1), + CO_WR_IDBASE((byte) 7, 5), + CO_RD_IDBASE((byte) 8, 1), + CO_WR_REPEATER((byte) 9, 3), + CO_RD_REPEATER((byte) 10, 1), + CO_RD_SECUREDEVICE_PSK((byte) 0x22, 1), + CO_RD_DUTYCYCLE_LIMIT((byte) 0x23, 1), + CO_GET_FREQUENCY_INFO((byte) 0x25, 1); + private byte value; + private int dataLength; + + CCMessageType(byte value, int dataLength) { + this.value = value; + this.dataLength = dataLength; + } + + public byte getValue() { + return this.value; + } + + public int getDataLength() { + return dataLength; + } + } + + private CCMessageType type; + + public CCMessage(CCMessageType type) { + this(type, new byte[] { type.getValue() }); + } + + public CCMessage(CCMessageType type, byte[] payload) { + super(type.getDataLength(), 0, ESPPacketType.COMMON_COMMAND, payload); + + this.type = type; + } + + public CCMessageType getCCMessageType() { + return type; } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java index 76f4c60dc1fdc..966d171fed843 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java @@ -23,8 +23,9 @@ * * @author Daniel Weber - Initial contribution */ -public class ERP1Message extends ESP3Packet { +public class ERP1Message extends BasePacket { + // these are just ESP3 RORGs, ESP2 ORGs are converted by ESP2PacketConverter public enum RORG { Unknown((byte) 0x00, 0), RPS((byte) 0xF6, 1), @@ -62,15 +63,11 @@ public static RORG getRORG(byte value) { } } - protected RORG rorg; + RORG rorg; byte[] senderId; boolean teachIn; - public ERP1Message() { - super.setPacketType(ESPPacketType.RADIO_ERP1); - } - public ERP1Message(int dataLength, int optionalDataLength, byte[] payload) { super(dataLength, optionalDataLength, ESPPacketType.RADIO_ERP1, payload); @@ -123,7 +120,7 @@ public RORG getRORG() { return rorg; } - public final byte[] getSenderId() { + public byte[] getSenderId() { return senderId; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP2Packet.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP2Packet.java new file mode 100644 index 0000000000000..8572c77b411d7 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP2Packet.java @@ -0,0 +1,263 @@ +/** + * Copyright (c) 2010-2020 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.enocean.internal.messages; + +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.ZERO; + +import java.security.InvalidParameterException; +import java.util.Arrays; + +import org.openhab.binding.enocean.internal.EnOceanException; +import org.openhab.binding.enocean.internal.messages.BasePacket.ESPPacketType; +import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class ESP2Packet { + + public static final byte ENOCEAN_ESP2_FIRSTSYNC_BYTE = (byte) 0xA5; + public static final byte ENOCEAN_ESP2_SECONDSYNC_BYTE = 0x5A; + + private static final int ESP2_SYNC_BYTE_LENGTH = 2; + private static final int ESP2_HEADER_LENGTH = 1; + private static final int ESP2_DATA_LENGTH = 4; + private static final int ESP2_SENDERID_LENGTH = 4; + private static final int ESP2_STATUS_LENGTH = 1; + private static final int ESP2_CHECKSUM_LENGTH = 1; + + public static final int ESP2_ORG_LENGTH = 1; + + public static final int ESP_PACKET_LENGTH = ESP2_ORG_LENGTH + ESP2_DATA_LENGTH + ESP2_SENDERID_LENGTH + + ESP2_STATUS_LENGTH + ESP2_CHECKSUM_LENGTH; + + public enum ORG { + UNKOWN((byte) 0x00), + _RPS((byte) 0x05), + _1BS((byte) 0x06), + _4BS((byte) 0x07); + + private byte value; + + private ORG(byte value) { + this.value = value; + } + + public static ORG getORG(byte org) { + for (ORG o : ORG.values()) { + if (o.value == org) { + return o; + } + } + + return UNKOWN; + } + } + + public enum ESP2Response { + + UNKOWN((byte) 0), + OK((byte) 0x58), + ERR((byte) 0x19), + INF_IDBase((byte) 0x98), + INF_RX_SENSITIVITY((byte) 0x88), + INF_MODEM_STATUS((byte) 0xA8), + INF_SW_VERSION((byte) 0x8C), + ERR_MODEM_NOTWANTEDACK((byte) 0x28), + ERR_MODEM_NOTACK((byte) 0x29), + ERR_MODEM_DUP_ID((byte) 0x0C), + ERR_SYNTAX_HSEQ((byte) 0x08), + ERR_SYNTAX_LENGTH((byte) 0x09), + ERR_SYNTAX_CHECKSUM((byte) 0x0A), + ERR_SYNTAX_ORG((byte) 0x0B), + ERR_TX_IDRANGE((byte) 0x22), + ERR_IDRANGE((byte) 0x1A); + + private byte value; + + private ESP2Response(byte value) { + this.value = value; + } + + public static ESP2Response getResponse(byte response) { + for (ESP2Response p : ESP2Response.values()) { + if (p.value == response) { + return p; + } + } + + return UNKOWN; + } + } + + public enum ESP2PacketType { + Receive_Radio_Telegram((byte) 0x00), + Transmit_Radio_Telegram((byte) 0x03), + Receive_Message_Telegram((byte) 0x04), + Transmit_Command_Telegram((byte) 0x05); + + private byte value; + + private ESP2PacketType(byte value) { + this.value = value; + } + + public static ESP2PacketType getPacketType(byte packetType) { + for (ESP2PacketType p : ESP2PacketType.values()) { + if (p.value == packetType) { + return p; + } + } + + throw new InvalidParameterException("Unknown ESP2 PacketType value"); + } + } + + protected BasePacket basePacket; + + public ESP2Packet(BasePacket basePacket) { + this.basePacket = basePacket; + } + + private ESP2PacketType convertToESP2PacketType(ESPPacketType espPacketType) { + switch (espPacketType) { + case COMMON_COMMAND: + return ESP2PacketType.Transmit_Command_Telegram; + case RADIO_ERP1: + case RADIO_ERP2: + return ESP2PacketType.Transmit_Radio_Telegram; + case RESPONSE: // Response is not intended for outbound data (at least for ESP2) + default: + throw new IllegalArgumentException("ESPPacketType not supported"); + } + } + + private byte convertToESP2ORG(RORG esp3RORG) { + switch (esp3RORG) { + case RPS: + return ORG._RPS.value; + case _1BS: + return ORG._1BS.value; + case _4BS: + return ORG._4BS.value; + default: + throw new InvalidParameterException("RORG is not supported by ESP2"); + } + } + + private byte[] getOrgAndDataBytes(BasePacket basePacket) { + byte[] data = new byte[ESP2_ORG_LENGTH + ESP2_DATA_LENGTH]; + Arrays.fill(data, ZERO); + + if (basePacket.getPacketType() == ESPPacketType.RADIO_ERP1) { + ERP1Message message = (ERP1Message) basePacket; + data[0] = convertToESP2ORG(message.getRORG()); + System.arraycopy(message.getPayload(ESP2_ORG_LENGTH, message.getRORG().getDataLength()), 0, data, + ESP2_ORG_LENGTH, message.getRORG().getDataLength()); + } else if (basePacket.getPacketType() == ESPPacketType.COMMON_COMMAND) { + CCMessage message = (CCMessage) basePacket; + switch (message.getCCMessageType()) { + case CO_RD_IDBASE: + data[0] = 0x58; + break; + case CO_RD_VERSION: + data[0] = 0x4B; + break; + case CO_WR_IDBASE: + data[0] = 0x18; + System.arraycopy(message.getPayload(ESP2_ORG_LENGTH, 4), 0, data, ESP2_ORG_LENGTH, 4); + break; + default: + throw new InvalidParameterException("CCMessage is not supported by ESP2"); + } + } + + return data; + } + + private byte[] getSenderId(BasePacket basePacket) { + byte[] data = new byte[4]; + Arrays.fill(data, ZERO); + + if (basePacket.getPacketType() == ESPPacketType.RADIO_ERP1) { + ERP1Message message = (ERP1Message) basePacket; + data = message.getSenderId(); + } + + return data; + } + + private byte getStatus(BasePacket basePacket) { + + if (basePacket.getPacketType() == ESPPacketType.RADIO_ERP1) { + ERP1Message message = (ERP1Message) basePacket; + return message.getPayload(ESP2_ORG_LENGTH + message.getRORG().getDataLength() + ESP2_SENDERID_LENGTH, + ESP2_STATUS_LENGTH)[0]; + } + + return ZERO; + } + + private byte calcCheckSum(byte data[], int offset, int length) { + + int checkSum = 0; + for (int i = 0; i < length; i++) { + checkSum += (data[offset + i] & 0xff); + } + + return (byte) (checkSum & 0xff); + } + + public byte[] serialize() throws EnOceanException { + try { + + byte[] result = new byte[ESP2_SYNC_BYTE_LENGTH + ESP2_HEADER_LENGTH + ESP2_ORG_LENGTH + ESP2_DATA_LENGTH + + ESP2_SENDERID_LENGTH + ESP2_STATUS_LENGTH + ESP2_CHECKSUM_LENGTH]; + Arrays.fill(result, ZERO); + + result[0] = ENOCEAN_ESP2_FIRSTSYNC_BYTE; + result[1] = ENOCEAN_ESP2_SECONDSYNC_BYTE; + result[2] = (byte) (((convertToESP2PacketType(basePacket.getPacketType()).value << 5) + (ESP_PACKET_LENGTH)) + & 0xff); + + System.arraycopy(getOrgAndDataBytes(basePacket), 0, result, ESP2_SYNC_BYTE_LENGTH + ESP2_HEADER_LENGTH, + ESP2_ORG_LENGTH + ESP2_DATA_LENGTH); + + System.arraycopy(getSenderId(basePacket), 0, result, + ESP2_SYNC_BYTE_LENGTH + ESP2_HEADER_LENGTH + ESP2_ORG_LENGTH + ESP2_DATA_LENGTH, + ESP2_SENDERID_LENGTH); + + result[ESP2_SYNC_BYTE_LENGTH + ESP2_HEADER_LENGTH + ESP2_ORG_LENGTH + ESP2_DATA_LENGTH + + ESP2_SENDERID_LENGTH] = getStatus(basePacket); + + result[ESP2_SYNC_BYTE_LENGTH + ESP2_HEADER_LENGTH + ESP2_ORG_LENGTH + ESP2_DATA_LENGTH + + ESP2_SENDERID_LENGTH + ESP2_STATUS_LENGTH] = calcCheckSum(result, ESP2_SYNC_BYTE_LENGTH, + ESP2_HEADER_LENGTH + ESP2_ORG_LENGTH + ESP2_DATA_LENGTH + ESP2_SENDERID_LENGTH + + ESP2_STATUS_LENGTH); + + return result; + } catch (Exception e) { + throw new EnOceanException(e.getMessage()); + } + } + + public static boolean validateCheckSum(byte data[], int length, byte checkSum) { + int sum = 0; + for (int i = 0; i < length; i++) { + sum += (data[i] & 0xff); + } + + return (sum & 0xff) == (checkSum & 0xff); + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP2PacketConverter.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP2PacketConverter.java new file mode 100644 index 0000000000000..390a08921c489 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP2PacketConverter.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2010-2020 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.enocean.internal.messages; + +import java.nio.charset.Charset; +import java.util.Arrays; + +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.enocean.internal.messages.BasePacket.ESPPacketType; +import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; +import org.openhab.binding.enocean.internal.messages.ESP2Packet.ESP2PacketType; +import org.openhab.binding.enocean.internal.messages.Response.ResponseType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class ESP2PacketConverter { + + protected static Logger logger = LoggerFactory.getLogger(ESP2PacketConverter.class); + + private static final int ESP3PACKET_BASE_LENGTH = ESP3Packet.ESP3_RORG_LENGTH + ESP3Packet.ESP3_SENDERID_LENGTH + + ESP3Packet.ESP3_STATUS_LENGTH; + + private static BasePacket handleRadioTelegram(int dataLength, byte packetType, byte[] payload) { + switch (ESP2Packet.ORG.getORG(payload[1])) { + case _RPS: + return ESP3PacketFactory.BuildPacket(ESP3PACKET_BASE_LENGTH + RORG.RPS.getDataLength(), 0, + ESPPacketType.RADIO_ERP1.getValue(), new byte[] { RORG.RPS.getValue(), payload[2], payload[6], + payload[7], payload[8], payload[9], payload[10] }); + case _1BS: + return ESP3PacketFactory.BuildPacket(ESP3PACKET_BASE_LENGTH + RORG._1BS.getDataLength(), 0, + ESPPacketType.RADIO_ERP1.getValue(), new byte[] { RORG._1BS.getValue(), payload[2], payload[6], + payload[7], payload[8], payload[9], payload[10] }); + + case _4BS: + return ESP3PacketFactory.BuildPacket(ESP3PACKET_BASE_LENGTH + RORG._4BS.getDataLength(), 0, + ESPPacketType.RADIO_ERP1.getValue(), new byte[] { RORG._4BS.getValue(), payload[2], payload[3], + payload[4], payload[5], payload[6], payload[7], payload[8], payload[9], payload[10] }); + default: + logger.debug("Received unsupported ORG: {}", payload[1]); + return null; + } + } + + private static BasePacket handleMessageTelegram(int dataLength, byte packetType, byte[] payload) { + switch (ESP2Packet.ESP2Response.getResponse(payload[1])) { + case OK: + return ESP3PacketFactory.BuildPacket(1, 0, ESPPacketType.RESPONSE.getValue(), + new byte[] { ResponseType.RET_OK.getValue() }); + case ERR: + return ESP3PacketFactory.BuildPacket(1, 0, ESPPacketType.RESPONSE.getValue(), + new byte[] { ResponseType.RET_ERROR.getValue() }); + case INF_SW_VERSION: { + byte[] data = new byte[33]; + Arrays.fill(data, (byte) 0); + data[0] = ResponseType.RET_OK.getValue(); + System.arraycopy(payload, 1, data, 1, 4); + + byte[] description = "TCM 210".getBytes(Charset.forName("ASCII")); + System.arraycopy(description, 0, data, 17, description.length); + return ESP3PacketFactory.BuildPacket(data.length, 0, ESPPacketType.RESPONSE.getValue(), data); + } + + case UNKOWN: // try to interpret it as a radio telegram + return handleRadioTelegram(dataLength, packetType, payload); + + case ERR_IDRANGE: + case ERR_MODEM_DUP_ID: + case ERR_MODEM_NOTACK: + case ERR_MODEM_NOTWANTEDACK: + case ERR_SYNTAX_CHECKSUM: + case ERR_SYNTAX_HSEQ: + case ERR_SYNTAX_LENGTH: + case ERR_SYNTAX_ORG: + case ERR_TX_IDRANGE: + case INF_IDBase: + case INF_MODEM_STATUS: + case INF_RX_SENSITIVITY: + default: + logger.debug("Received unsupported message telegram: {}", + ESP2Packet.ESP2Response.getResponse(payload[1]).name()); + return null; + } + } + + public static BasePacket BuildPacket(int dataLength, byte packetType, byte[] payload) { + ESP2PacketType type = ESP2PacketType.getPacketType(packetType); + + switch (type) { + case Receive_Radio_Telegram: // RRT + logger.debug("Received ESP2 radio telegram: {}", HexUtils.bytesToHex(payload)); + return handleRadioTelegram(dataLength, packetType, payload); + + case Receive_Message_Telegram: // RMT => Response + logger.debug("Received ESP2 message telegram: {}", HexUtils.bytesToHex(payload)); + return handleMessageTelegram(dataLength, packetType, payload); + + case Transmit_Radio_Telegram: // TRT + // This should never happen, as this telegram is just for outbound data + logger.trace("Received Transmit_Radio_Telegram: {}", HexUtils.bytesToHex(payload)); + break; + + case Transmit_Command_Telegram: // TCT => CommonCommand + // this should also never happen, as this telegram is also just for outbound data + // however FAM14 receives periodically 0xABFC messages + if (payload[1] == (byte) 0xFC) { + return null; + } + + logger.trace("Received Transmit_Command_Telegram: {}", HexUtils.bytesToHex(payload)); + break; + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java index 2fc62951b1bc7..6faf92060b6ea 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java @@ -12,148 +12,90 @@ */ package org.openhab.binding.enocean.internal.messages; -import java.security.InvalidParameterException; -import java.util.Arrays; - import org.openhab.binding.enocean.internal.EnOceanException; -import org.openhab.binding.enocean.internal.transceiver.Helper; /** * * @author Daniel Weber - Initial contribution */ -public abstract class ESP3Packet { - - private static final int ENOCEAN_HEADER_LENGTH = 4; - private static final int ENOCEAN_SYNC_BYTE_LENGTH = 1; - private static final int ENOCEAN_CRC3_HEADER_LENGTH = 1; - private static final int ENOCEAN_CRC8_DATA_LENGTH = 1; - - protected ESPPacketType packetType; - protected byte[] payload; - protected byte[] optionalPayload = null; - - public enum ESPPacketType { - RADIO_ERP1((byte) 0x01), - RESPONSE((byte) 0x02), - RADIO_SUB_TEL((byte) 0x03), - EVENT((byte) 0x04), - COMMON_COMMAND((byte) 0x05), - SMART_ACK_COMMAND((byte) 0x06), - REMOTE_MAN_COMMAND((byte) 0x07), - RADIO_MESSAGE((byte) 0x09), - RADIO_ERP2((byte) 0x0A); - - private byte value; - - private ESPPacketType(byte value) { - this.value = value; - } - - public static boolean hasValue(byte value) { - for (ESPPacketType p : ESPPacketType.values()) { - if (p.value == value) { - return true; - } - } - - return false; - } - - public static ESPPacketType getPacketType(byte packetType) { - for (ESPPacketType p : ESPPacketType.values()) { - if (p.value == packetType) { - return p; - } - } - - throw new InvalidParameterException("Unknown packetType value"); - } - +public class ESP3Packet { + + private static byte[] crc8_table = new byte[] { (byte) 0x00, (byte) 0x07, (byte) 0x0e, (byte) 0x09, (byte) 0x1c, + (byte) 0x1b, (byte) 0x12, (byte) 0x15, (byte) 0x38, (byte) 0x3f, (byte) 0x36, (byte) 0x31, (byte) 0x24, + (byte) 0x23, (byte) 0x2a, (byte) 0x2d, (byte) 0x70, (byte) 0x77, (byte) 0x7e, (byte) 0x79, (byte) 0x6c, + (byte) 0x6b, (byte) 0x62, (byte) 0x65, (byte) 0x48, (byte) 0x4f, (byte) 0x46, (byte) 0x41, (byte) 0x54, + (byte) 0x53, (byte) 0x5a, (byte) 0x5d, (byte) 0xe0, (byte) 0xe7, (byte) 0xee, (byte) 0xe9, (byte) 0xfc, + (byte) 0xfb, (byte) 0xf2, (byte) 0xf5, (byte) 0xd8, (byte) 0xdf, (byte) 0xd6, (byte) 0xd1, (byte) 0xc4, + (byte) 0xc3, (byte) 0xca, (byte) 0xcd, (byte) 0x90, (byte) 0x97, (byte) 0x9e, (byte) 0x99, (byte) 0x8c, + (byte) 0x8b, (byte) 0x82, (byte) 0x85, (byte) 0xa8, (byte) 0xaf, (byte) 0xa6, (byte) 0xa1, (byte) 0xb4, + (byte) 0xb3, (byte) 0xba, (byte) 0xbd, (byte) 0xc7, (byte) 0xc0, (byte) 0xc9, (byte) 0xce, (byte) 0xdb, + (byte) 0xdc, (byte) 0xd5, (byte) 0xd2, (byte) 0xff, (byte) 0xf8, (byte) 0xf1, (byte) 0xf6, (byte) 0xe3, + (byte) 0xe4, (byte) 0xed, (byte) 0xea, (byte) 0xb7, (byte) 0xb0, (byte) 0xb9, (byte) 0xbe, (byte) 0xab, + (byte) 0xac, (byte) 0xa5, (byte) 0xa2, (byte) 0x8f, (byte) 0x88, (byte) 0x81, (byte) 0x86, (byte) 0x93, + (byte) 0x94, (byte) 0x9d, (byte) 0x9a, (byte) 0x27, (byte) 0x20, (byte) 0x29, (byte) 0x2e, (byte) 0x3b, + (byte) 0x3c, (byte) 0x35, (byte) 0x32, (byte) 0x1f, (byte) 0x18, (byte) 0x11, (byte) 0x16, (byte) 0x03, + (byte) 0x04, (byte) 0x0d, (byte) 0x0a, (byte) 0x57, (byte) 0x50, (byte) 0x59, (byte) 0x5e, (byte) 0x4b, + (byte) 0x4c, (byte) 0x45, (byte) 0x42, (byte) 0x6f, (byte) 0x68, (byte) 0x61, (byte) 0x66, (byte) 0x73, + (byte) 0x74, (byte) 0x7d, (byte) 0x7a, (byte) 0x89, (byte) 0x8e, (byte) 0x87, (byte) 0x80, (byte) 0x95, + (byte) 0x92, (byte) 0x9b, (byte) 0x9c, (byte) 0xb1, (byte) 0xb6, (byte) 0xbf, (byte) 0xb8, (byte) 0xad, + (byte) 0xaa, (byte) 0xa3, (byte) 0xa4, (byte) 0xf9, (byte) 0xfe, (byte) 0xf7, (byte) 0xf0, (byte) 0xe5, + (byte) 0xe2, (byte) 0xeb, (byte) 0xec, (byte) 0xc1, (byte) 0xc6, (byte) 0xcf, (byte) 0xc8, (byte) 0xdd, + (byte) 0xda, (byte) 0xd3, (byte) 0xd4, (byte) 0x69, (byte) 0x6e, (byte) 0x67, (byte) 0x60, (byte) 0x75, + (byte) 0x72, (byte) 0x7b, (byte) 0x7c, (byte) 0x51, (byte) 0x56, (byte) 0x5f, (byte) 0x58, (byte) 0x4d, + (byte) 0x4a, (byte) 0x43, (byte) 0x44, (byte) 0x19, (byte) 0x1e, (byte) 0x17, (byte) 0x10, (byte) 0x05, + (byte) 0x02, (byte) 0x0b, (byte) 0x0c, (byte) 0x21, (byte) 0x26, (byte) 0x2f, (byte) 0x28, (byte) 0x3d, + (byte) 0x3a, (byte) 0x33, (byte) 0x34, (byte) 0x4e, (byte) 0x49, (byte) 0x40, (byte) 0x47, (byte) 0x52, + (byte) 0x55, (byte) 0x5c, (byte) 0x5b, (byte) 0x76, (byte) 0x71, (byte) 0x78, (byte) 0x7f, (byte) 0x6A, + (byte) 0x6d, (byte) 0x64, (byte) 0x63, (byte) 0x3e, (byte) 0x39, (byte) 0x30, (byte) 0x37, (byte) 0x22, + (byte) 0x25, (byte) 0x2c, (byte) 0x2b, (byte) 0x06, (byte) 0x01, (byte) 0x08, (byte) 0x0f, (byte) 0x1a, + (byte) 0x1d, (byte) 0x14, (byte) 0x13, (byte) 0xae, (byte) 0xa9, (byte) 0xa0, (byte) 0xa7, (byte) 0xb2, + (byte) 0xb5, (byte) 0xbc, (byte) 0xbb, (byte) 0x96, (byte) 0x91, (byte) 0x98, (byte) 0x9f, (byte) 0x8a, + (byte) 0x8D, (byte) 0x84, (byte) 0x83, (byte) 0xde, (byte) 0xd9, (byte) 0xd0, (byte) 0xd7, (byte) 0xc2, + (byte) 0xc5, (byte) 0xcc, (byte) 0xcb, (byte) 0xe6, (byte) 0xe1, (byte) 0xe8, (byte) 0xef, (byte) 0xfa, + (byte) 0xfd, (byte) 0xf4, (byte) 0xf3 }; + + public static final int ESP3_HEADER_LENGTH = 4; + private static final int ESP3_SYNC_BYTE_LENGTH = 1; + private static final int ESP3_CRC3_HEADER_LENGTH = 1; + private static final int ESP3_CRC8_DATA_LENGTH = 1; + + public static final int ESP3_RORG_LENGTH = 1; + public static final int ESP3_SENDERID_LENGTH = 4; + public static final int ESP3_STATUS_LENGTH = 1; + + public static final byte ESP3_SYNC_BYTE = 0x55; + + protected BasePacket basePacket; + + public ESP3Packet(BasePacket basePacket) { + this.basePacket = basePacket; } - public ESP3Packet() { - - } - - public ESP3Packet(int dataLength, int optionalDataLength, byte packetType, byte[] payload) { - - if (!ESPPacketType.hasValue(packetType)) { - throw new InvalidParameterException("Packet type is unknown"); - } - - if (dataLength + optionalDataLength > payload.length) { - throw new InvalidParameterException("data length does not match provided lengths"); + private byte calcCRC8(byte data[], int offset, int length) { + byte output = 0; + for (int i = offset; i < offset + length; i++) { + int index = (output ^ data[i]) & 0xff; + output = crc8_table[index]; } - - setPacketType(ESPPacketType.getPacketType(packetType)); - - this.payload = new byte[dataLength]; - System.arraycopy(payload, 0, this.payload, 0, dataLength); - - if (optionalDataLength > 0) { - this.optionalPayload = new byte[optionalDataLength]; - System.arraycopy(payload, dataLength, optionalPayload, 0, optionalDataLength); - } else { - this.optionalPayload = new byte[0]; - } - } - - public ESP3Packet(int dataLength, int optionalDataLength, ESPPacketType packetType, byte[] payload) { - this(dataLength, optionalDataLength, packetType.value, payload); - } - - public void setPacketType(ESPPacketType packetTye) { - this.packetType = packetTye; - } - - public ESPPacketType getPacketType() { - return this.packetType; - } - - public void setPayload(byte[] data) { - this.payload = Arrays.copyOf(data, data.length); - } - - public byte[] getPayload(int offset, int length) { - return Arrays.copyOfRange(payload, offset, offset + length); - } - - public final byte[] getPayload() { - return payload; - } - - public void setOptionalPayload(byte[] optionalData) { - if (optionalData == null) { - this.optionalPayload = null; - } else { - this.optionalPayload = Arrays.copyOf(optionalData, optionalData.length); - } - } - - public byte[] getOptionalPayload(int offset, int length) { - byte[] result = new byte[length]; - System.arraycopy(optionalPayload, offset, result, 0, length); - return result; - } - - public final byte[] getOptionalPayload() { - return optionalPayload; + return (byte) (output & 0xff); } public byte[] serialize() throws EnOceanException { try { + byte[] payload = basePacket.getPayload(); + byte[] optionalPayload = basePacket.getOptionalPayload(); + int optionalLength = optionalPayload != null ? optionalPayload.length : 0; - byte[] result = new byte[ENOCEAN_SYNC_BYTE_LENGTH + ENOCEAN_HEADER_LENGTH + ENOCEAN_CRC3_HEADER_LENGTH - + payload.length + optionalLength + ENOCEAN_CRC8_DATA_LENGTH]; + byte[] result = new byte[ESP3_SYNC_BYTE_LENGTH + ESP3_HEADER_LENGTH + ESP3_CRC3_HEADER_LENGTH + + payload.length + optionalLength + ESP3_CRC8_DATA_LENGTH]; - result[0] = Helper.ENOCEAN_SYNC_BYTE; + result[0] = ESP3_SYNC_BYTE; result[1] = (byte) ((payload.length >> 8) & 0xff); result[2] = (byte) (payload.length & 0xff); result[3] = (byte) (optionalLength & 0xff); - result[4] = packetType.value; - result[5] = Helper.calcCRC8(result, ENOCEAN_SYNC_BYTE_LENGTH, ENOCEAN_HEADER_LENGTH); + result[4] = basePacket.getPacketType().getValue(); + result[5] = calcCRC8(result, ESP3_SYNC_BYTE_LENGTH, ESP3_HEADER_LENGTH); for (int i = 0; i < payload.length; i++) { result[6 + i] = payload[i]; } @@ -162,11 +104,20 @@ public byte[] serialize() throws EnOceanException { result[6 + payload.length + i] = (byte) (optionalPayload[i] & 0xff); } } - result[6 + payload.length + optionalLength] = Helper.calcCRC8(result, 6, payload.length + optionalLength); + result[6 + payload.length + optionalLength] = calcCRC8(result, 6, payload.length + optionalLength); return result; } catch (Exception e) { throw new EnOceanException(e.getMessage()); } } + + public static boolean checkCRC8(byte data[], int length, byte crc8) { + byte output = 0; + for (int i = 0; i < length; i++) { + int index = (output ^ data[i]) & 0xff; + output = crc8_table[index]; + } + return output == crc8; + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java index 91937695ae785..19543d0dd358e 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java @@ -14,7 +14,8 @@ import org.eclipse.smarthome.core.library.types.StringType; import org.openhab.binding.enocean.internal.EnOceanBindingConstants; -import org.openhab.binding.enocean.internal.messages.ESP3Packet.ESPPacketType; +import org.openhab.binding.enocean.internal.messages.BasePacket.ESPPacketType; +import org.openhab.binding.enocean.internal.messages.CCMessage.CCMessageType; /** * @@ -22,34 +23,26 @@ */ public class ESP3PacketFactory { - public final static ESP3Packet CO_RD_VERSION = new CCMessage(1, 0, new byte[] { 3 }); - public final static ESP3Packet CO_RD_IDBASE = new CCMessage(1, 0, new byte[] { 8 }); - public final static ESP3Packet CO_RD_REPEATER = new CCMessage(1, 0, new byte[] { 10 }); + public final static BasePacket CO_RD_VERSION = new CCMessage(CCMessageType.CO_RD_VERSION); + public final static BasePacket CO_RD_IDBASE = new CCMessage(CCMessageType.CO_RD_IDBASE); + public final static BasePacket CO_RD_REPEATER = new CCMessage(CCMessageType.CO_RD_REPEATER); - public static ESP3Packet CO_WR_IDBASE(byte[] newId) { - return new CCMessage(5, 0, new byte[] { 7, newId[0], newId[1], newId[2], newId[3] }); + public static BasePacket CO_WR_IDBASE(byte[] newId) { + return new CCMessage(CCMessageType.CO_WR_IDBASE, new byte[] { 7, newId[0], newId[1], newId[2], newId[3] }); } - public static ESP3Packet CO_WR_REPEATER(StringType level) { + public static BasePacket CO_WR_REPEATER(StringType level) { switch (level.toString()) { case EnOceanBindingConstants.REPEATERMODE_OFF: - return new CCMessage(3, 0, new byte[] { 9, 0, 0 }); + return new CCMessage(CCMessageType.CO_WR_REPEATER, new byte[] { 9, 0, 0 }); case EnOceanBindingConstants.REPEATERMODE_LEVEL_1: - return new CCMessage(3, 0, new byte[] { 9, 1, 1 }); + return new CCMessage(CCMessageType.CO_WR_REPEATER, new byte[] { 9, 1, 1 }); default: - return new CCMessage(3, 0, new byte[] { 9, 1, 2 }); + return new CCMessage(CCMessageType.CO_WR_REPEATER, new byte[] { 9, 1, 2 }); } } - public static ESP3Packet CO_WR_SUBTEL(boolean enable) { - if (enable) { - return new CCMessage(2, 0, new byte[] { 17, 1 }); - } else { - return new CCMessage(2, 0, new byte[] { 17, 0 }); - } - } - - public static ESP3Packet BuildPacket(int dataLength, int optionalDataLength, byte packetType, byte[] payload) { + public static BasePacket BuildPacket(int dataLength, int optionalDataLength, byte packetType, byte[] payload) { ESPPacketType type = ESPPacketType.getPacketType(packetType); switch (type) { diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java index 0113aeb7d2a85..391fc11400850 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.enocean.internal.messages; -import org.openhab.binding.enocean.internal.transceiver.Helper; +import org.openhab.binding.enocean.internal.Helper; /** * @@ -31,13 +31,12 @@ public RDBaseIdResponse(Response response) { RDBaseIdResponse(int dataLength, int optionalDataLength, byte[] payload) { super(dataLength, optionalDataLength, payload); - if (this.payload == null || this.payload.length != 5 || this.optionalPayload == null - || this.optionalPayload.length != 1) { + if (this.data == null || this.data.length != 5 || this.optionalData == null || this.optionalData.length != 1) { return; } baseId = getPayload(1, 4); - remainingWriteCycles = optionalPayload[0] & 0xFF; + remainingWriteCycles = optionalData[0] & 0xFF; _isValid = true; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java index 53d74b7f69c66..9da6da81280c9 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.messages; +import java.util.Arrays; + import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.util.HexUtils; @@ -43,9 +45,7 @@ public RDVersionResponse(Response response) { apiVersion = String.format("%d.%d.%d.%d", payload[5] & 0xff, payload[6] & 0xff, payload[7] & 0xff, payload[8] & 0xff); - byte[] chip = new byte[4]; - System.arraycopy(payload, 9, chip, 0, 4); - chipId = HexUtils.bytesToHex(chip); + chipId = HexUtils.bytesToHex(Arrays.copyOfRange(payload, 9, 13)); StringBuffer sb = new StringBuffer(); for (int i = 17; i < payload.length; i++) { diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java index 6c1003da69359..0c978bc31970e 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java @@ -18,7 +18,7 @@ * * @author Daniel Weber - Initial contribution */ -public class Response extends ESP3Packet { +public class Response extends BasePacket { public enum ResponseType { RET_OK((byte) 0x00), @@ -39,7 +39,7 @@ public enum ResponseType { this.value = value; } - public int getValue() { + public byte getValue() { return this.value; } @@ -58,7 +58,7 @@ public static ResponseType getResponsetype(byte value) { protected ResponseType responseType; protected boolean _isValid = false; - Response(int dataLength, int optionalDataLength, byte[] payload) { + protected Response(int dataLength, int optionalDataLength, byte[] payload) { super(dataLength, optionalDataLength, ESPPacketType.RESPONSE, payload); try { diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP2Transceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP2Transceiver.java new file mode 100644 index 0000000000000..20723e2688147 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP2Transceiver.java @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2010-2020 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.enocean.internal.transceiver; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.smarthome.core.util.HexUtils; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; +import org.openhab.binding.enocean.internal.EnOceanException; +import org.openhab.binding.enocean.internal.messages.BasePacket; +import org.openhab.binding.enocean.internal.messages.ERP1Message; +import org.openhab.binding.enocean.internal.messages.ESP2Packet; +import org.openhab.binding.enocean.internal.messages.ESP2PacketConverter; +import org.openhab.binding.enocean.internal.messages.Response; +import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class EnOceanESP2Transceiver extends EnOceanTransceiver { + + public EnOceanESP2Transceiver(String path, TransceiverErrorListener errorListener, + ScheduledExecutorService scheduler, SerialPortManager serialPortManager) { + super(path, errorListener, scheduler, serialPortManager); + } + + enum ReadingState { + WaitingForFirstSyncByte, + WaitingForSecondSyncByte, + ReadingHeader, + ReadingData + } + + byte[] dataBuffer = new byte[ESP2Packet.ESP_PACKET_LENGTH]; + ReadingState state = ReadingState.WaitingForFirstSyncByte; + int currentPosition = 0; + int dataLength = -1; + byte packetType = -1; + + @Override + protected void processMessage(byte firstByte) { + + byte[] readingBuffer = new byte[ENOCEAN_MAX_DATA]; + int bytesRead = -1; + byte _byte; + + try { + + readingBuffer[0] = firstByte; + + bytesRead = this.inputStream.read(readingBuffer, 1, inputStream.available()); + if (bytesRead == -1) { + throw new IOException("could not read from inputstream"); + } + + if (readingTask == null || readingTask.isCancelled()) { + return; + } + + bytesRead++; + for (int p = 0; p < bytesRead; p++) { + _byte = readingBuffer[p]; + + switch (state) { + case WaitingForFirstSyncByte: + if (_byte == ESP2Packet.ENOCEAN_ESP2_FIRSTSYNC_BYTE) { + state = ReadingState.WaitingForSecondSyncByte; + logger.trace("Received First Sync Byte"); + } + break; + case WaitingForSecondSyncByte: + if (_byte == ESP2Packet.ENOCEAN_ESP2_SECONDSYNC_BYTE) { + state = ReadingState.ReadingHeader; + logger.trace("Received Second Sync Byte"); + } + break; + case ReadingHeader: { + state = ReadingState.ReadingData; + + currentPosition = 0; + dataBuffer[currentPosition++] = _byte; + dataLength = ((dataBuffer[0] & 0xFF) & 0b11111); + packetType = (byte) ((dataBuffer[0] & 0xFF) >> 5); + + logger.trace(">> Received header, data length {} packet type {}", dataLength, packetType); + } + break; + case ReadingData: + if (currentPosition == dataLength) { + if (ESP2Packet.validateCheckSum(dataBuffer, dataLength, _byte)) { + BasePacket packet = ESP2PacketConverter.BuildPacket(dataLength, packetType, dataBuffer); + if (packet != null) { + switch (packet.getPacketType()) { + case RADIO_ERP1: { + ERP1Message msg = (ERP1Message) packet; + logger.debug("Converted to: {} with RORG {} for {}", + packet.getPacketType().name(), msg.getRORG().name(), + HexUtils.bytesToHex(msg.getSenderId())); + + if(msg.getRORG() != RORG.Unknown) { + informListeners(msg); + } else { + logger.debug("Received unknown RORG"); + } + } + break; + case RESPONSE: { + Response response = (Response) packet; + logger.debug("Converted to: {} with code {}", packet.getPacketType().name(), + response.getResponseType().name()); + + handleResponse(response); + } + break; + default: + break; + } + } else { + if (dataBuffer[1] != (byte) 0xFC) { + logger.debug("Unknown/unsupported ESP2Packet: {}", + HexUtils.bytesToHex(Arrays.copyOf(dataBuffer, dataLength))); + } + } + } else { + logger.debug("ESP2Packet malformed: {}", HexUtils.bytesToHex(dataBuffer)); + } + + state = _byte == ESP2Packet.ENOCEAN_ESP2_FIRSTSYNC_BYTE + ? ReadingState.WaitingForSecondSyncByte + : ReadingState.WaitingForFirstSyncByte; + + currentPosition = 0; + dataLength = packetType = -1; + } else { + dataBuffer[currentPosition++] = _byte; + } + break; + } + } + } catch ( + + IOException ioexception) { + errorListener.ErrorOccured(ioexception); + return; + } + } + + @Override + protected byte[] serializePacket(BasePacket packet) throws EnOceanException { + return new ESP2Packet(packet).serialize(); + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP3Transceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP3Transceiver.java new file mode 100644 index 0000000000000..2df7dba06731c --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanESP3Transceiver.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2010-2020 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.enocean.internal.transceiver; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.smarthome.core.util.HexUtils; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; +import org.openhab.binding.enocean.internal.EnOceanException; +import org.openhab.binding.enocean.internal.messages.BasePacket; +import org.openhab.binding.enocean.internal.messages.ERP1Message; +import org.openhab.binding.enocean.internal.messages.ESP3Packet; +import org.openhab.binding.enocean.internal.messages.ESP3PacketFactory; +import org.openhab.binding.enocean.internal.messages.Response; +import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; + +/** + * + * @author Daniel Weber - Initial contribution + */ +public class EnOceanESP3Transceiver extends EnOceanTransceiver { + + public EnOceanESP3Transceiver(String path, TransceiverErrorListener errorListener, + ScheduledExecutorService scheduler, SerialPortManager serialPortManager) { + super(path, errorListener, scheduler, serialPortManager); + } + + enum ReadingState { + WaitingForSyncByte, + ReadingHeader, + ReadingData + } + + byte[] dataBuffer = new byte[ENOCEAN_MAX_DATA]; + ReadingState state = ReadingState.WaitingForSyncByte; + int currentPosition = 0; + int dataLength = -1; + int optionalLength = -1; + byte packetType = -1; + + @Override + protected void processMessage(byte firstByte) { + + byte[] readingBuffer = new byte[ENOCEAN_MAX_DATA]; + int bytesRead = -1; + byte _byte; + + try { + + readingBuffer[0] = firstByte; + + bytesRead = this.inputStream.read(readingBuffer, 1, inputStream.available()); + if (bytesRead == -1) { + throw new IOException("could not read from inputstream"); + } + + if (readingTask == null || readingTask.isCancelled()) { + return; + } + + bytesRead++; + for (int p = 0; p < bytesRead; p++) { + _byte = readingBuffer[p]; + + switch (state) { + case WaitingForSyncByte: + if (_byte == ESP3Packet.ESP3_SYNC_BYTE) { + state = ReadingState.ReadingHeader; + logger.trace("Received Sync Byte"); + } + break; + case ReadingHeader: + if (currentPosition == ESP3Packet.ESP3_HEADER_LENGTH) { + if (ESP3Packet.checkCRC8(dataBuffer, ESP3Packet.ESP3_HEADER_LENGTH, _byte) + && ((dataBuffer[0] & 0xFF) << 8) + (dataBuffer[1] & 0xFF) + + (dataBuffer[2] & 0xFF) > 0) { + + state = ReadingState.ReadingData; + + dataLength = ((dataBuffer[0] & 0xFF << 8) | (dataBuffer[1] & 0xFF)); + optionalLength = dataBuffer[2] & 0xFF; + packetType = dataBuffer[3]; + currentPosition = 0; + + if (packetType == 3) { + logger.trace("Received sub_msg"); + } + + logger.trace(">> Received header, data length {} optional length {} packet type {}", + dataLength, optionalLength, packetType); + } else { + // check if we find a sync byte in current buffer + int copyFrom = -1; + for (int i = 0; i < ESP3Packet.ESP3_HEADER_LENGTH; i++) { + if (dataBuffer[i] == ESP3Packet.ESP3_SYNC_BYTE) { + copyFrom = i + 1; + break; + } + } + + if (copyFrom != -1) { + System.arraycopy(dataBuffer, copyFrom, dataBuffer, 0, + ESP3Packet.ESP3_HEADER_LENGTH - copyFrom); + state = ReadingState.ReadingHeader; + currentPosition = ESP3Packet.ESP3_HEADER_LENGTH - copyFrom; + dataBuffer[currentPosition++] = _byte; + } else { + currentPosition = 0; + state = _byte == ESP3Packet.ESP3_SYNC_BYTE ? ReadingState.ReadingHeader + : ReadingState.WaitingForSyncByte; + } + logger.trace("CrC8 header check not successful"); + } + } else { + dataBuffer[currentPosition++] = _byte; + } + break; + case ReadingData: + if (currentPosition == dataLength + optionalLength) { + if (ESP3Packet.checkCRC8(dataBuffer, dataLength + optionalLength, _byte)) { + state = ReadingState.WaitingForSyncByte; + BasePacket packet = ESP3PacketFactory.BuildPacket(dataLength, optionalLength, + packetType, dataBuffer); + + if (packet != null) { + switch (packet.getPacketType()) { + case COMMON_COMMAND: + logger.debug("Common command: {}", + HexUtils.bytesToHex(packet.getPayload())); + break; + case EVENT: + logger.debug("Event occured: {}", HexUtils.bytesToHex(packet.getPayload())); + break; + case RADIO_ERP1: { + ERP1Message msg = (ERP1Message) packet; + logger.debug("{} with RORG {} for {} payload {} received", + packet.getPacketType().name(), msg.getRORG().name(), + HexUtils.bytesToHex(msg.getSenderId()), HexUtils.bytesToHex( + Arrays.copyOf(dataBuffer, dataLength + optionalLength))); + + if(msg.getRORG() != RORG.Unknown) { + informListeners(msg); + } else { + logger.debug("Received unknown RORG"); + } + } + break; + case RADIO_ERP2: + break; + case RADIO_MESSAGE: + break; + case RADIO_SUB_TEL: + break; + case REMOTE_MAN_COMMAND: + break; + case RESPONSE: { + Response response = (Response) packet; + logger.debug("{} with code {} payload {} received", + packet.getPacketType().name(), response.getResponseType().name(), + HexUtils.bytesToHex(packet.getPayload())); // Responses do not have + // optional data + handleResponse(response); + } + break; + case SMART_ACK_COMMAND: + break; + default: + break; + } + } else { + logger.trace("Unknown ESP3Packet: {}", HexUtils + .bytesToHex(Arrays.copyOf(dataBuffer, dataLength + optionalLength))); + } + } else { + state = _byte == ESP3Packet.ESP3_SYNC_BYTE ? ReadingState.ReadingHeader + : ReadingState.WaitingForSyncByte; + logger.trace("ESP3Packet malformed: {}", + HexUtils.bytesToHex(Arrays.copyOf(dataBuffer, dataLength + optionalLength))); + } + + currentPosition = 0; + dataLength = optionalLength = packetType = -1; + } else { + dataBuffer[currentPosition++] = _byte; + } + break; + } + } + } catch (IOException ioexception) { + errorListener.ErrorOccured(ioexception); + return; + } + } + + @Override + protected byte[] serializePacket(BasePacket packet) throws EnOceanException { + return new ESP3Packet(packet).serialize(); + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanSerialTransceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanSerialTransceiver.java deleted file mode 100644 index b1ed07bf76283..0000000000000 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanSerialTransceiver.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (c) 2010-2020 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.enocean.internal.transceiver; - -import java.io.IOException; -import java.util.TooManyListenersException; -import java.util.concurrent.ScheduledExecutorService; - -import org.apache.commons.io.IOUtils; -import org.eclipse.smarthome.io.transport.serial.PortInUseException; -import org.eclipse.smarthome.io.transport.serial.SerialPort; -import org.eclipse.smarthome.io.transport.serial.SerialPortEvent; -import org.eclipse.smarthome.io.transport.serial.SerialPortEventListener; -import org.eclipse.smarthome.io.transport.serial.SerialPortIdentifier; -import org.eclipse.smarthome.io.transport.serial.SerialPortManager; -import org.eclipse.smarthome.io.transport.serial.UnsupportedCommOperationException; -import org.openhab.binding.enocean.internal.EnOceanBindingConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - * @author Daniel Weber - Initial contribution - */ -public class EnOceanSerialTransceiver extends EnOceanTransceiver implements SerialPortEventListener { - - protected String path; - SerialPort serialPort; - - private static final int ENOCEAN_DEFAULT_BAUD = 57600; - - private Logger logger = LoggerFactory.getLogger(EnOceanSerialTransceiver.class); - private SerialPortManager serialPortManager; - - public EnOceanSerialTransceiver(String path, TransceiverErrorListener errorListener, - ScheduledExecutorService scheduler, SerialPortManager serialPortManager) { - super(errorListener, scheduler); - this.path = path; - this.serialPortManager = serialPortManager; - } - - @Override - public void Initialize() - throws UnsupportedCommOperationException, PortInUseException, IOException, TooManyListenersException { - - SerialPortIdentifier id = serialPortManager.getIdentifier(path); - if (id == null) { - throw new IOException("Could not find a gateway on given path '" + path + "', " - + serialPortManager.getIdentifiers().count() + " ports available."); - } - - serialPort = id.open(EnOceanBindingConstants.BINDING_ID, 1000); - serialPort.setSerialPortParams(ENOCEAN_DEFAULT_BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, - SerialPort.PARITY_NONE); - - try { - serialPort.enableReceiveThreshold(1); - serialPort.enableReceiveTimeout(100); // In ms. Small values mean faster shutdown but more cpu usage. - } catch (UnsupportedCommOperationException e) { - // rfc connections do not allow a ReceiveThreshold - } - - inputStream = serialPort.getInputStream(); - outputStream = serialPort.getOutputStream(); - - logger.info("EnOceanSerialTransceiver initialized"); - } - - @Override - public void ShutDown() { - - logger.debug("shutting down transceiver"); - super.ShutDown(); - - if (outputStream != null) { - logger.debug("Closing serial output stream"); - IOUtils.closeQuietly(outputStream); - } - if (inputStream != null) { - logger.debug("Closeing serial input stream"); - IOUtils.closeQuietly(inputStream); - } - - if (serialPort != null) { - logger.debug("Closing serial port"); - serialPort.close(); - } - - serialPort = null; - outputStream = null; - inputStream = null; - - logger.info("Transceiver shutdown"); - - } - - @Override - public void serialEvent(SerialPortEvent event) { - - if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { - - synchronized (this) { - this.notify(); - } - } - } - - @Override - protected int read(byte[] buffer, int length) { - try { - return this.inputStream.read(buffer, 0, length); - } catch (IOException e) { - return 0; - } - } -} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java index 7d469f0a3ecf2..a44ac5085dc3a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java @@ -25,14 +25,20 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import org.apache.commons.io.IOUtils; import org.eclipse.smarthome.core.util.HexUtils; import org.eclipse.smarthome.io.transport.serial.PortInUseException; +import org.eclipse.smarthome.io.transport.serial.SerialPort; +import org.eclipse.smarthome.io.transport.serial.SerialPortEvent; +import org.eclipse.smarthome.io.transport.serial.SerialPortEventListener; +import org.eclipse.smarthome.io.transport.serial.SerialPortIdentifier; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; import org.eclipse.smarthome.io.transport.serial.UnsupportedCommOperationException; +import org.openhab.binding.enocean.internal.EnOceanBindingConstants; import org.openhab.binding.enocean.internal.EnOceanException; +import org.openhab.binding.enocean.internal.messages.BasePacket; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; -import org.openhab.binding.enocean.internal.messages.ESP3PacketFactory; import org.openhab.binding.enocean.internal.messages.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,16 +47,23 @@ * * @author Daniel Weber - Initial contribution */ -public abstract class EnOceanTransceiver { +public abstract class EnOceanTransceiver implements SerialPortEventListener { + + public static final int ENOCEAN_MAX_DATA = 65790; // Thread management - private Future readingTask = null; + protected Future readingTask = null; private Future timeOut = null; - private Logger logger = LoggerFactory.getLogger(EnOceanTransceiver.class); + protected Logger logger = LoggerFactory.getLogger(EnOceanTransceiver.class); + + private SerialPortManager serialPortManager; + private static final int ENOCEAN_DEFAULT_BAUD = 57600; + protected String path; + SerialPort serialPort; class Request { - ESP3Packet RequestPacket; + BasePacket RequestPacket; Response ResponsePacket; ResponseListener ResponseListener; @@ -94,7 +107,8 @@ private synchronized void send() throws IOException { HexUtils.bytesToHex(currentRequest.RequestPacket.getPayload()), HexUtils.bytesToHex(currentRequest.RequestPacket.getOptionalPayload())); - byte[] b = currentRequest.RequestPacket.serialize(); + byte[] b = serializePacket(currentRequest.RequestPacket); + logger.trace("Sending raw data: {}", HexUtils.bytesToHex(b)); outputStream.write(b); outputStream.flush(); @@ -126,32 +140,53 @@ private synchronized void send() throws IOException { RequestQueue requestQueue; Request currentRequest = null; - protected Map> listeners; - protected ESP3PacketListener teachInListener; + protected Map> listeners; + protected PacketListener teachInListener; - // Input and output streams, must be created by transceiver implementations protected InputStream inputStream; protected OutputStream outputStream; private byte[] filteredDeviceId; TransceiverErrorListener errorListener; - enum ReadingState { - WaitingForSyncByte, - ReadingHeader, - ReadingData - } - - public EnOceanTransceiver(TransceiverErrorListener errorListener, ScheduledExecutorService scheduler) { + public EnOceanTransceiver(String path, TransceiverErrorListener errorListener, ScheduledExecutorService scheduler, + SerialPortManager serialPortManager) { requestQueue = new RequestQueue(scheduler); + listeners = new HashMap<>(); teachInListener = null; + this.errorListener = errorListener; + this.serialPortManager = serialPortManager; + this.path = path; } - public abstract void Initialize() - throws UnsupportedCommOperationException, PortInUseException, IOException, TooManyListenersException; + public void Initialize() + throws UnsupportedCommOperationException, PortInUseException, IOException, TooManyListenersException { + + SerialPortIdentifier id = serialPortManager.getIdentifier(path); + if (id == null) { + throw new IOException("Could not find a gateway on given path '" + path + "', " + + serialPortManager.getIdentifiers().count() + " ports available."); + } + + serialPort = id.open(EnOceanBindingConstants.BINDING_ID, 1000); + serialPort.setSerialPortParams(ENOCEAN_DEFAULT_BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, + SerialPort.PARITY_NONE); + + try { + serialPort.enableReceiveThreshold(1); + serialPort.enableReceiveTimeout(100); // In ms. Small values mean faster shutdown but more cpu usage. + } catch (UnsupportedCommOperationException e) { + // rfc connections do not allow a ReceiveThreshold + } + + inputStream = serialPort.getInputStream(); + outputStream = serialPort.getOutputStream(); + + logger.info("EnOceanSerialTransceiver initialized"); + } public void StartReceiving(ScheduledExecutorService scheduler) { @@ -168,6 +203,7 @@ public void run() { } public void ShutDown() { + logger.debug("shutting down transceiver"); logger.debug("Interrupt rx Thread"); if (timeOut != null) { @@ -187,6 +223,26 @@ public void ShutDown() { listeners.clear(); teachInListener = null; errorListener = null; + + if (outputStream != null) { + logger.debug("Closing serial output stream"); + IOUtils.closeQuietly(outputStream); + } + if (inputStream != null) { + logger.debug("Closeing serial input stream"); + IOUtils.closeQuietly(inputStream); + } + + if (serialPort != null) { + logger.debug("Closing serial port"); + serialPort.close(); + } + + serialPort = null; + outputStream = null; + inputStream = null; + + logger.info("Transceiver shutdown"); } private void receivePackets() { @@ -196,210 +252,23 @@ private void receivePackets() { int bytesRead = read(buffer, 1); if (bytesRead > 0) { - // if byte == sync byte => processMessage processMessage(buffer[0]); } } } - protected abstract int read(byte[] buffer, int length); - - byte[] dataBuffer = new byte[Helper.ENOCEAN_MAX_DATA]; - ReadingState state = ReadingState.WaitingForSyncByte; // we already received sync byte when we get called - int currentPosition = 0; - int dataLength = -1; - int optionalLength = -1; - byte packetType = -1; - - private void processMessage(byte firstByte) { - - byte[] readingBuffer = new byte[Helper.ENOCEAN_MAX_DATA]; - int bytesRead = -1; - byte _byte; + protected abstract void processMessage(byte firstByte); + protected int read(byte[] buffer, int length) { try { - - readingBuffer[0] = firstByte; - - bytesRead = this.inputStream.read(readingBuffer, 1, inputStream.available()); - if (bytesRead == -1) { - throw new IOException("could not read from inputstream"); - } - - if (readingTask == null || readingTask.isCancelled()) { - return; - } - - bytesRead++; - for (int p = 0; p < bytesRead; p++) { - _byte = readingBuffer[p]; - - switch (state) { - case WaitingForSyncByte: - if (_byte == Helper.ENOCEAN_SYNC_BYTE) { - state = ReadingState.ReadingHeader; - logger.trace("Received Sync Byte"); - } - break; - case ReadingHeader: - if (currentPosition == Helper.ENOCEAN_HEADER_LENGTH) { - if (Helper.checkCRC8(dataBuffer, Helper.ENOCEAN_HEADER_LENGTH, _byte) - && ((dataBuffer[0] & 0xFF) << 8) + (dataBuffer[1] & 0xFF) - + (dataBuffer[2] & 0xFF) > 0) { - - state = ReadingState.ReadingData; - - dataLength = ((dataBuffer[0] & 0xFF << 8) | (dataBuffer[1] & 0xFF)); - optionalLength = dataBuffer[2] & 0xFF; - packetType = dataBuffer[3]; - currentPosition = 0; - - if (packetType == 3) { - logger.trace("Received sub_msg"); - } - - logger.trace(">> Received header, data length {} optional length {} packet type {}", - dataLength, optionalLength, packetType); - } else { - // check if we find a sync byte in current buffer - int copyFrom = -1; - for (int i = 0; i < Helper.ENOCEAN_HEADER_LENGTH; i++) { - if (dataBuffer[i] == Helper.ENOCEAN_SYNC_BYTE) { - copyFrom = i + 1; - break; - } - } - - if (copyFrom != -1) { - System.arraycopy(dataBuffer, copyFrom, dataBuffer, 0, - Helper.ENOCEAN_HEADER_LENGTH - copyFrom); - state = ReadingState.ReadingHeader; - currentPosition = Helper.ENOCEAN_HEADER_LENGTH - copyFrom; - dataBuffer[currentPosition++] = _byte; - } else { - currentPosition = 0; - state = _byte == Helper.ENOCEAN_SYNC_BYTE ? ReadingState.ReadingHeader - : ReadingState.WaitingForSyncByte; - } - logger.trace("CrC8 header check not successful"); - } - } else { - dataBuffer[currentPosition++] = _byte; - } - break; - case ReadingData: - if (currentPosition == dataLength + optionalLength) { - if (Helper.checkCRC8(dataBuffer, dataLength + optionalLength, _byte)) { - state = ReadingState.WaitingForSyncByte; - ESP3Packet packet = ESP3PacketFactory.BuildPacket(dataLength, optionalLength, - packetType, dataBuffer); - - if (packet != null) { - switch (packet.getPacketType()) { - case COMMON_COMMAND: - break; - case EVENT: - break; - case RADIO_ERP1: { - ERP1Message msg = (ERP1Message) packet; - - byte[] d = new byte[dataLength + optionalLength]; - System.arraycopy(dataBuffer, 0, d, 0, d.length); - - logger.debug("{} with RORG {} for {} payload {} received", - packet.getPacketType().name(), msg.getRORG().name(), - HexUtils.bytesToHex(msg.getSenderId()), HexUtils.bytesToHex(d)); - - if(msg.getRORG() != RORG.Unknown) { - informListeners(msg); - } else { - logger.debug("Received unknown RORG, payload {}", HexUtils.bytesToHex(d)); - } - } - break; - case RADIO_ERP2: - break; - case RADIO_MESSAGE: - break; - case RADIO_SUB_TEL: - break; - case REMOTE_MAN_COMMAND: - break; - case RESPONSE: { - byte[] d = new byte[dataLength + optionalLength]; - System.arraycopy(dataBuffer, 0, d, 0, d.length); - - logger.debug("{} with code {} payload {} received", - packet.getPacketType().name(), - ((Response) packet).getResponseType().name(), - HexUtils.bytesToHex(d)); - - if (currentRequest != null) { - if (currentRequest.ResponseListener != null) { - currentRequest.ResponsePacket = (Response) packet; - try { - currentRequest.ResponseListener - .handleResponse(currentRequest.ResponsePacket); - } catch (Exception e) { - } - - logger.trace("Response handled"); - } else { - logger.trace("Response without listener"); - } - } - } - break; - case SMART_ACK_COMMAND: - break; - default: - break; - } - } else { - logger.trace("Unknown ESP3Packet"); - byte[] d = new byte[dataLength + optionalLength]; - System.arraycopy(dataBuffer, 0, d, 0, d.length); - logger.trace("{}", HexUtils.bytesToHex(d)); - } - } else { - state = _byte == Helper.ENOCEAN_SYNC_BYTE ? ReadingState.ReadingHeader - : ReadingState.WaitingForSyncByte; - logger.trace("esp packet malformed"); - } - - currentPosition = 0; - dataLength = optionalLength = packetType = -1; - } else { - dataBuffer[currentPosition++] = _byte; - } - break; - default: - break; - } - } - } catch (IOException ioexception) { - errorListener.ErrorOccured(ioexception); - return; + return this.inputStream.read(buffer, 0, length); + } catch (IOException e) { + return 0; } } - public void sendESP3Packet(ESP3Packet packet, ResponseListener responseCallback) - throws IOException { - - if (packet == null) { - return; - } - - logger.debug("Enqueue new send request with ESP3 type {} {} callback", packet.getPacketType().name(), - responseCallback == null ? "without" : "with"); - Request r = new Request(); - r.RequestPacket = packet; - r.ResponseListener = responseCallback; - - requestQueue.enqueRequest(r); - } - protected void informListeners(ERP1Message msg) { + try { byte[] senderId = msg.getSenderId(); @@ -413,7 +282,7 @@ protected void informListeners(ERP1Message msg) { if (teachInListener != null) { if (msg.getIsTeachIn() || (msg.getRORG() == RORG.RPS)) { logger.info("Received teach in message from {}", HexUtils.bytesToHex(msg.getSenderId())); - teachInListener.espPacketReceived(msg); + teachInListener.packetReceived(msg); return; } } else { @@ -425,9 +294,9 @@ protected void informListeners(ERP1Message msg) { } long s = Long.parseLong(HexUtils.bytesToHex(senderId), 16); - HashSet pl = listeners.get(s); + HashSet pl = listeners.get(s); if (pl != null) { - pl.forEach(l -> l.espPacketReceived(msg)); + pl.forEach(l -> l.packetReceived(msg)); } } } catch (Exception e) { @@ -435,15 +304,53 @@ protected void informListeners(ERP1Message msg) { } } - public void addPacketListener(ESP3PacketListener listener, long senderIdToListenTo) { + protected void handleResponse(Response response) throws IOException { + if (currentRequest != null) { + if (currentRequest.ResponseListener != null) { + currentRequest.ResponsePacket = response; + try { + currentRequest.ResponseListener.handleResponse(response); + } catch (Exception e) { + logger.debug("Exception during response handling"); + } finally { + logger.trace("Response handled"); + } + } else { + logger.trace("Response without listener"); + } + } else { + logger.trace("Response without request"); + } + + } + + public void sendBasePacket(BasePacket packet, ResponseListener responseCallback) + throws IOException { + + if (packet == null) { + return; + } + + logger.debug("Enqueue new send request with ESP3 type {} {} callback", packet.getPacketType().name(), + responseCallback == null ? "without" : "with"); + Request r = new Request(); + r.RequestPacket = packet; + r.ResponseListener = responseCallback; + + requestQueue.enqueRequest(r); + } + + protected abstract byte[] serializePacket(BasePacket packet) throws EnOceanException; + + public void addPacketListener(PacketListener listener, long senderIdToListenTo) { if (listeners.computeIfAbsent(senderIdToListenTo, k -> new HashSet<>()).add(listener)) { logger.debug("Listener added: {}", senderIdToListenTo); } } - public void removePacketListener(ESP3PacketListener listener, long senderIdToListenTo) { - HashSet pl = listeners.get(senderIdToListenTo); + public void removePacketListener(PacketListener listener, long senderIdToListenTo) { + HashSet pl = listeners.get(senderIdToListenTo); if (pl != null) { pl.remove(listener); if (pl.isEmpty()) { @@ -452,7 +359,7 @@ public void removePacketListener(ESP3PacketListener listener, long senderIdToLis } } - public void startDiscovery(ESP3PacketListener teachInListener) { + public void startDiscovery(PacketListener teachInListener) { this.teachInListener = teachInListener; } @@ -465,4 +372,15 @@ public void setFilteredDeviceId(byte[] filteredDeviceId) { System.arraycopy(filteredDeviceId, 0, filteredDeviceId, 0, filteredDeviceId.length); } } + + @Override + public void serialEvent(SerialPortEvent event) { + + if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { + + synchronized (this) { + this.notify(); + } + } + } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/Helper.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/Helper.java deleted file mode 100644 index c48df4ef44aca..0000000000000 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/Helper.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) 2010-2020 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.enocean.internal.transceiver; - -import java.util.Arrays; - -/** - * - * @author Daniel Weber - Initial contribution - */ -public class Helper { - private static byte[] crc8_table = new byte[] { (byte) 0x00, (byte) 0x07, (byte) 0x0e, (byte) 0x09, (byte) 0x1c, - (byte) 0x1b, (byte) 0x12, (byte) 0x15, (byte) 0x38, (byte) 0x3f, (byte) 0x36, (byte) 0x31, (byte) 0x24, - (byte) 0x23, (byte) 0x2a, (byte) 0x2d, (byte) 0x70, (byte) 0x77, (byte) 0x7e, (byte) 0x79, (byte) 0x6c, - (byte) 0x6b, (byte) 0x62, (byte) 0x65, (byte) 0x48, (byte) 0x4f, (byte) 0x46, (byte) 0x41, (byte) 0x54, - (byte) 0x53, (byte) 0x5a, (byte) 0x5d, (byte) 0xe0, (byte) 0xe7, (byte) 0xee, (byte) 0xe9, (byte) 0xfc, - (byte) 0xfb, (byte) 0xf2, (byte) 0xf5, (byte) 0xd8, (byte) 0xdf, (byte) 0xd6, (byte) 0xd1, (byte) 0xc4, - (byte) 0xc3, (byte) 0xca, (byte) 0xcd, (byte) 0x90, (byte) 0x97, (byte) 0x9e, (byte) 0x99, (byte) 0x8c, - (byte) 0x8b, (byte) 0x82, (byte) 0x85, (byte) 0xa8, (byte) 0xaf, (byte) 0xa6, (byte) 0xa1, (byte) 0xb4, - (byte) 0xb3, (byte) 0xba, (byte) 0xbd, (byte) 0xc7, (byte) 0xc0, (byte) 0xc9, (byte) 0xce, (byte) 0xdb, - (byte) 0xdc, (byte) 0xd5, (byte) 0xd2, (byte) 0xff, (byte) 0xf8, (byte) 0xf1, (byte) 0xf6, (byte) 0xe3, - (byte) 0xe4, (byte) 0xed, (byte) 0xea, (byte) 0xb7, (byte) 0xb0, (byte) 0xb9, (byte) 0xbe, (byte) 0xab, - (byte) 0xac, (byte) 0xa5, (byte) 0xa2, (byte) 0x8f, (byte) 0x88, (byte) 0x81, (byte) 0x86, (byte) 0x93, - (byte) 0x94, (byte) 0x9d, (byte) 0x9a, (byte) 0x27, (byte) 0x20, (byte) 0x29, (byte) 0x2e, (byte) 0x3b, - (byte) 0x3c, (byte) 0x35, (byte) 0x32, (byte) 0x1f, (byte) 0x18, (byte) 0x11, (byte) 0x16, (byte) 0x03, - (byte) 0x04, (byte) 0x0d, (byte) 0x0a, (byte) 0x57, (byte) 0x50, (byte) 0x59, (byte) 0x5e, (byte) 0x4b, - (byte) 0x4c, (byte) 0x45, (byte) 0x42, (byte) 0x6f, (byte) 0x68, (byte) 0x61, (byte) 0x66, (byte) 0x73, - (byte) 0x74, (byte) 0x7d, (byte) 0x7a, (byte) 0x89, (byte) 0x8e, (byte) 0x87, (byte) 0x80, (byte) 0x95, - (byte) 0x92, (byte) 0x9b, (byte) 0x9c, (byte) 0xb1, (byte) 0xb6, (byte) 0xbf, (byte) 0xb8, (byte) 0xad, - (byte) 0xaa, (byte) 0xa3, (byte) 0xa4, (byte) 0xf9, (byte) 0xfe, (byte) 0xf7, (byte) 0xf0, (byte) 0xe5, - (byte) 0xe2, (byte) 0xeb, (byte) 0xec, (byte) 0xc1, (byte) 0xc6, (byte) 0xcf, (byte) 0xc8, (byte) 0xdd, - (byte) 0xda, (byte) 0xd3, (byte) 0xd4, (byte) 0x69, (byte) 0x6e, (byte) 0x67, (byte) 0x60, (byte) 0x75, - (byte) 0x72, (byte) 0x7b, (byte) 0x7c, (byte) 0x51, (byte) 0x56, (byte) 0x5f, (byte) 0x58, (byte) 0x4d, - (byte) 0x4a, (byte) 0x43, (byte) 0x44, (byte) 0x19, (byte) 0x1e, (byte) 0x17, (byte) 0x10, (byte) 0x05, - (byte) 0x02, (byte) 0x0b, (byte) 0x0c, (byte) 0x21, (byte) 0x26, (byte) 0x2f, (byte) 0x28, (byte) 0x3d, - (byte) 0x3a, (byte) 0x33, (byte) 0x34, (byte) 0x4e, (byte) 0x49, (byte) 0x40, (byte) 0x47, (byte) 0x52, - (byte) 0x55, (byte) 0x5c, (byte) 0x5b, (byte) 0x76, (byte) 0x71, (byte) 0x78, (byte) 0x7f, (byte) 0x6A, - (byte) 0x6d, (byte) 0x64, (byte) 0x63, (byte) 0x3e, (byte) 0x39, (byte) 0x30, (byte) 0x37, (byte) 0x22, - (byte) 0x25, (byte) 0x2c, (byte) 0x2b, (byte) 0x06, (byte) 0x01, (byte) 0x08, (byte) 0x0f, (byte) 0x1a, - (byte) 0x1d, (byte) 0x14, (byte) 0x13, (byte) 0xae, (byte) 0xa9, (byte) 0xa0, (byte) 0xa7, (byte) 0xb2, - (byte) 0xb5, (byte) 0xbc, (byte) 0xbb, (byte) 0x96, (byte) 0x91, (byte) 0x98, (byte) 0x9f, (byte) 0x8a, - (byte) 0x8D, (byte) 0x84, (byte) 0x83, (byte) 0xde, (byte) 0xd9, (byte) 0xd0, (byte) 0xd7, (byte) 0xc2, - (byte) 0xc5, (byte) 0xcc, (byte) 0xcb, (byte) 0xe6, (byte) 0xe1, (byte) 0xe8, (byte) 0xef, (byte) 0xfa, - (byte) 0xfd, (byte) 0xf4, (byte) 0xf3 }; - - public static final byte ENOCEAN_SYNC_BYTE = 0x55; - public static final int ENOCEAN_MAX_DATA = 65790; // 2 byte data length + 1 byte optional data length - public static final int ENOCEAN_HEADER_LENGTH = 4; - - public static boolean checkCRC8(byte data[], int length, byte crc8) { - byte output = 0; - for (int i = 0; i < length; i++) { - int index = (output ^ data[i]) & 0xff; - output = crc8_table[index]; - } - return output == crc8; - } - - public static byte calcCRC8(byte data[], int offset, int length) { - byte output = 0; - for (int i = offset; i < offset + length; i++) { - int index = (output ^ data[i]) & 0xff; - output = crc8_table[index]; - } - return (byte) (output & 0xff); - } - - public static byte[] concatAll(byte[] a, byte[]... rest) { - int totalLength = a.length; - for (byte[] b : rest) { - totalLength += b.length; - } - - byte[] result = Arrays.copyOf(a, totalLength); - int offset = a.length; - for (byte[] array : rest) { - System.arraycopy(array, 0, result, offset, array.length); - offset += array.length; - } - return result; - } -} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ESP3PacketListener.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/PacketListener.java similarity index 77% rename from bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ESP3PacketListener.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/PacketListener.java index a1de287479e87..d3b9fbf6f27b1 100644 --- a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ESP3PacketListener.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/PacketListener.java @@ -12,15 +12,15 @@ */ package org.openhab.binding.enocean.internal.transceiver; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; +import org.openhab.binding.enocean.internal.messages.BasePacket; /** * * @author Daniel Weber - Initial contribution */ -public interface ESP3PacketListener { +public interface PacketListener { - public void espPacketReceived(ESP3Packet packet); + public void packetReceived(BasePacket packet); public long getSenderIdToListenTo(); } diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/bridge.xml index 8bbe041a334f3..905d45853138a 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/bridge.xml @@ -20,11 +20,27 @@ Path to the EnOcean gateway true + + true + + + + + + true + true + ESP3 + true false + + true + + 00000000 + true