From 73ee3aecdf645dd65edaaefc0d03aeef9f2336e4 Mon Sep 17 00:00:00 2001 From: Jonas Hohaus Date: Mon, 11 May 2020 22:48:41 +0200 Subject: [PATCH] [phc] Improve serial communication and add support for dimmer (#6447) * Add support for dimmer * Improve receiving, recognition of serial messages * Null Annotations + gnu.io to core + Formatting * Improve sending of serial messages * Add bus connection to README * minor corrections * split method and use BolckingQueue in InternalBuffer * use diamond operator and format * Correct README * Remove gnu.io from pom.xml added in #7589 Signed-off-by: Jonas Hohaus --- bundles/org.openhab.binding.phc/README.md | 89 ++- .../doc/RJ12-Connector.png | Bin 0 -> 18429 bytes bundles/org.openhab.binding.phc/pom.xml | 4 - .../phc/internal/PHCBindingConstants.java | 10 +- .../phc/internal/PHCHandlerFactory.java | 31 +- .../binding/phc/internal/PHCHelper.java | 26 +- .../phc/internal/handler/InternalBuffer.java | 74 ++ .../internal/handler/PHCBridgeHandler.java | 741 ++++++++++-------- .../phc/internal/handler/PHCHandler.java | 83 +- .../phc/internal/handler/QueueObject.java | 40 +- .../resources/ESH-INF/binding/binding.xml | 4 +- .../resources/ESH-INF/thing/channel-types.xml | 29 +- .../resources/ESH-INF/thing/thing-types.xml | 68 +- 13 files changed, 747 insertions(+), 452 deletions(-) create mode 100644 bundles/org.openhab.binding.phc/doc/RJ12-Connector.png create mode 100644 bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/InternalBuffer.java diff --git a/bundles/org.openhab.binding.phc/README.md b/bundles/org.openhab.binding.phc/README.md index 2214361d4a8d6..2b21885034b28 100644 --- a/bundles/org.openhab.binding.phc/README.md +++ b/bundles/org.openhab.binding.phc/README.md @@ -1,25 +1,13 @@ ---- -layout: documentation ---- - -{% include base.html %} - # PHC Binding -This binding allows you to integrate modules(at the Moment AM, EM and JRM) of PHC, without the PHC control (STM), in openHAB. +This binding allows you to integrate modules(at the Moment AM, EM, JRM and DIM) of PHC, without the PHC control (STM), in openHAB. The serial protocol is mainly extracted, with thanks to the developers from the projects [PHCtoUDP](https://sourceforge.net/projects/phctoudp/) and [OpenHC](https://sourceforge.net/projects/openhc/?source=directory). -## Bridge - -The Bridge manages the communication between the things and the modules via a serial port (RS485). It represents the STM. -At the Moment you can only use one Bridge (like one STM). - -#### Configurations - -**Serial Port:** Type the serial port of the RS485 adaptor, e.g. COM3 (Windows) or /dev/ttyUSB0 (Linux). +The basics of the module bus protocol can also be found in the [Wiki of the PHC-Forum (german)](https://wiki.phc-forum.de/index.php/PHC-Protokoll_des_internen_Bus). +While the Wiki is offline you can find a PDF version [here](https://phc-forum.de/index.php/forum/phc-programmierung/129-phc-protokoll?start=15#1329). -#### Serial Communication +## Serial Communication The binding was tested with QinHeng Electronics HL-340 USB-Serial adapter (RS485) and the Digitus DA-70157 (FTDI/FT323RL) on Raspbian Ubilinux (Up Board) and Windows 10: @@ -31,18 +19,56 @@ The binding was tested with QinHeng Electronics HL-340 USB-Serial adapter (RS485 | | FTDI | doesn´t work | | | on board | bad | | Up Board/ubilinux(Jessie)| HL-340 | not reliable | -| | FTDI | ok | +| | FTDI | good | -Sometimes it is a bit slowly and you have to switch two times for reaction, but all in all it works ok. -I'm trying to get a faster solution for communication in the next few weeks. +If there are many modules on one bridge, the initialization can take a few minutes. If it does not work you can plug in the modules one after the other. +Sometimes after initialization, you might have to switch two times or the reaction could be a bit slow, but after you used a channel it should all work fine. For all devices running with Linux that use the ch341 driver (HL-340), the new version (ch34x) is needed. A guide how to install this can be found here: [CH340/341 UART Driver for Raspberry Pi](https://github.com/aperepel/raspberrypi-ch340-driver). If you don´t have the same kernel as used in the guide you have to compile the module yourself. In the guide is described a specific way for the Raspberry Pi. With another Linux version you can go the normal way with linux-headers. +According to the [Wiki of the PHC-Forum](https://wiki.phc-forum.de/index.php/PHC-Protokoll_des_internen_Bus#USB_RS-485_Adapter) the newer version of the FTDI adapter doesn't really work anymore either. + In Linux amongst others the user 'openhab' must be added to the group 'dialout': ```sudo usermod -a -G dialout openhab``` For more information read the [installation guide](https://www.openhab.org/docs/installation/linux.html#recommended-additional-setup-steps). +### Connection + +There are two alternatives, the first of which is much simpler. + +#### Connection via power supply (simpler, preferred) + +The simplest way would be to connect the RS485 adaptor to the PHC power supply like in the table below and Out at the power supply to the first module like the STM before. + +| adaptor | PHC power supply | +|----------|------------------| +| 485+ | +A | +| 485- | -B | + +#### Make a direct RJ12 connection + +Connect a RJ12 plug with the RS485 adaptor and the power supply as follows. + +| RJ12 like in picture below | The cores on the other side | +|----------------------------|-----------------------------| +| 0V | 0V on power supply | +| B- | 485- on adaptor | +| A+ | 485+ on adaptor | +| 24+ | +24V on power supply | + +![RJ12 Connector](doc/RJ12-Connector.png) + +## Bridge + +The Bridge manages the communication between the things and the modules via a serial port (RS485). +It represents the STM. +At the Moment you can only use one Bridge (like one STM). + +#### Configurations + +**Serial Port:** Type the serial port of the RS485 adaptor, e.g. COM3 (Windows) or /dev/ttyUSB0 (Linux). + ## Supported Things - **AM module:** This represents the AM module with 8 outgoing channels (relays). @@ -51,6 +77,8 @@ In Linux amongst others the user 'openhab' must be added to the group 'dialout': - **JRM module:** This represents the JRM module with 4 channels for Shutters. +- **DIM:** This represents the DM module with 2 dimmer channels. + ## Discovery Not implemented yet. @@ -58,7 +86,7 @@ Not implemented yet. ## Thing Configuration A thing accords with a module in the PHC software and the channels (with linked items) accord with the inputs and outputs. -Please note, if you define the things manually (not in the UI) that the ThingID always have to be the address. +Please note, if you define the things manually (not in the UI) that the ThingID always have to be the address (like the PID switches on the module). #### Parameters @@ -66,6 +94,8 @@ Please note, if you define the things manually (not in the UI) that the ThingID - **UpDownTime[1-4] (only JRM):** (advanced) The time in seconds that the shutter needs to move up or down. The default, if no value is specified, is 30 seconds. +- **DimTime[1-2] (only DIM):** (advanced) The time in seconds in that the dimmer should move 100%. The default is 2 seconds, then for example dimming from 0 to 100% takes 1 second. + ## Channels | Thing Type | Channel-Group Id | Channels | Item Type | @@ -75,6 +105,8 @@ Please note, if you define the things manually (not in the UI) that the ThingID | EM | emLed | 00-07 | Switch | | JRM | jrm | 00-03 | Rollershutter | | JRM | jrmT | 00-03 | Number | +| DIM | dim | 00-01 | Dimmer | +| DIM | dimT | 00-01 | Number | **Channel UID:** ```phc:::#``` e.g. ```phc:AM:01101:am#03``` @@ -82,7 +114,11 @@ Please note, if you define the things manually (not in the UI) that the ThingID - **em:** Incoming channels. - **emLed:** Outgoing switch channels e.g. for LEDs in light shutters. - **jrm:** Outgoing shutter channels. -- **jrmT:** Time for shutter channels in seconds with an accuracy of 1/10 seconds. These channels are used instead of the configuration parameters. If you send the time via this channel, the Binding uses this time till you send another. After reboot the config parameter is used by default. +- **jrmT:** Time for shutter channels in seconds with an accuracy of 1/10 seconds. +These channels are used instead of the configuration parameters. +If you send the time via this channel, the Binding uses this time till you send another. +After reboot the config parameter is used by default. +- **dim:** Outgoing dimmer channels. ## Full Example @@ -91,9 +127,10 @@ Please note, if you define the things manually (not in the UI) that the ThingID ``` Bridge phc:bridge:demo [port="/dev/ttyUSB0"]{ // The ThingID have to be the address. - Thing AM 10110 [address="01101"] + Thing AM 01101 [address="01101"] Thing EM 00110 [address="00110"] Thing JRM 10111 [address="10111", upDownTime3="60", upDownTime4="20"] + Thing DIM 00000 [address="00000"] ``` .items @@ -114,12 +151,16 @@ Rollershutter Shutter_4 {channel="phc:JRM:10111:jrm#03"} Number ShutterTime_1 {channel="phc:JRM:10111:jrmT#00"} +//DIM Module +Dimmer Dimmer_1 {channel="phc:DIM:00000:dim#00} +Dimmer Dimmer_2 {channel="phc:DIM:00000:dim#01} + // EM Module -Switch InputLed_1 {channel="phc:EM:00110:emLed#03"} +Switch InputLed_3 {channel="phc:EM:00110:emLed#03"} Switch Input_1 {channel="phc:EM:00110:em#00"} Switch Input_2 {channel="phc:EM:00110:em#01"} -Switch Input_3 {channel="phc:EM:00110:em#01"} +Switch Input_3 {channel="phc:EM:00110:em#02"} ... Switch Input_16 {channel="phc:EM:00110:em#15"} ``` diff --git a/bundles/org.openhab.binding.phc/doc/RJ12-Connector.png b/bundles/org.openhab.binding.phc/doc/RJ12-Connector.png new file mode 100644 index 0000000000000000000000000000000000000000..4626f90bc288a457991f508622f07cd0c1fa0767 GIT binary patch literal 18429 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—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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.phc/pom.xml b/bundles/org.openhab.binding.phc/pom.xml index fc576763fead0..90a70f9a26f7b 100644 --- a/bundles/org.openhab.binding.phc/pom.xml +++ b/bundles/org.openhab.binding.phc/pom.xml @@ -14,8 +14,4 @@ openHAB Add-ons :: Bundles :: PHC Binding - - gnu.io;version="[3.12,6)" - - diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCBindingConstants.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCBindingConstants.java index cfcc249c45f31..9c48d9f21e6ee 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCBindingConstants.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCBindingConstants.java @@ -12,14 +12,16 @@ */ package org.openhab.binding.phc.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.thing.ThingTypeUID; /** - * The {@link PHCBinding} class defines common constants, which are - * used across the whole binding. + * The {@link PHCBinding} class defines common constants, which are used across + * the whole binding. * * @author Jonas Hohaus - Initial contribution */ +@NonNullByDefault public class PHCBindingConstants { public static final String BINDING_ID = "phc"; @@ -30,6 +32,7 @@ public class PHCBindingConstants { public static final ThingTypeUID THING_TYPE_AM = new ThingTypeUID(BINDING_ID, "AM"); public static final ThingTypeUID THING_TYPE_EM = new ThingTypeUID(BINDING_ID, "EM"); public static final ThingTypeUID THING_TYPE_JRM = new ThingTypeUID(BINDING_ID, "JRM"); + public static final ThingTypeUID THING_TYPE_DIM = new ThingTypeUID(BINDING_ID, "DIM"); // List of all Channel Group IDs public static final String CHANNELS_AM = "am"; @@ -37,6 +40,7 @@ public class PHCBindingConstants { public static final String CHANNELS_EM_LED = "emLed"; public static final String CHANNELS_JRM = "jrm"; public static final String CHANNELS_JRM_TIME = "jrmT"; + public static final String CHANNELS_DIM = "dim"; // List of all configuration parameters public static final String PORT = "port"; @@ -45,4 +49,6 @@ public class PHCBindingConstants { public static final String UP_DOWN_TIME_2 = "upDownTime2"; public static final String UP_DOWN_TIME_3 = "upDownTime3"; public static final String UP_DOWN_TIME_4 = "upDownTime4"; + public static final String DIM_TIME_1 = "dimTime1"; + public static final String DIM_TIME_2 = "dimTime2"; } diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHandlerFactory.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHandlerFactory.java index 86a81dac9d86f..0528bcaedfc6c 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHandlerFactory.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHandlerFactory.java @@ -19,6 +19,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Thing; @@ -27,9 +29,11 @@ import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; import org.openhab.binding.phc.internal.handler.PHCBridgeHandler; import org.openhab.binding.phc.internal.handler.PHCHandler; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** * The {@link PHCHandlerFactory} is responsible for creating things and thing @@ -37,11 +41,24 @@ * * @author Jonas Hohaus - Initial contribution */ +@NonNullByDefault @Component(service = ThingHandlerFactory.class, configurationPid = "binding.phc") public class PHCHandlerFactory extends BaseThingHandlerFactory { - private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( - Stream.of(THING_TYPE_BRIDGE, THING_TYPE_AM, THING_TYPE_EM, THING_TYPE_JRM).collect(Collectors.toSet())); + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(THING_TYPE_BRIDGE, THING_TYPE_AM, THING_TYPE_EM, THING_TYPE_JRM, THING_TYPE_DIM) + .collect(Collectors.toSet())); + + private @NonNullByDefault({}) SerialPortManager serialPortManager; + + @Reference + protected void setSerialPortManager(final SerialPortManager serialPortManager) { + this.serialPortManager = serialPortManager; + } + + protected void unsetSerialPortManager(final SerialPortManager serialPortManager) { + this.serialPortManager = null; + } @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { @@ -49,15 +66,15 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } @Override - public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID, - ThingUID bridgeUID) { + public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, + @Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) { Thing thing; if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { if (thingTypeUID.equals(THING_TYPE_BRIDGE)) { thing = super.createThing(thingTypeUID, configuration, thingUID, null); } else { - ThingUID phcThingUID = new ThingUID(thingTypeUID, configuration.get(ADDRESS).toString().toUpperCase()); + ThingUID phcThingUID = new ThingUID(thingTypeUID, configuration.get(ADDRESS).toString()); thing = super.createThing(thingTypeUID, configuration, phcThingUID, bridgeUID); } } else { @@ -69,13 +86,13 @@ public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, } @Override - protected ThingHandler createHandler(Thing thing) { + protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingHandler handler = null; if (thingTypeUID.equals(THING_TYPE_BRIDGE)) { - handler = new PHCBridgeHandler((Bridge) thing); + handler = new PHCBridgeHandler((Bridge) thing, serialPortManager); } else if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { handler = new PHCHandler(thing); } diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java index 2deaa2a608fa4..c320fa13216ed 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/PHCHelper.java @@ -13,15 +13,17 @@ package org.openhab.binding.phc.internal; import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; /** - * The {@link PHCHelper} is responsible for finding the appropriate Thing(UID) to the Channel of the PHC module. + * The {@link PHCHelper} is responsible for finding the appropriate Thing(UID) + * to the Channel of the PHC module. * * @author Jonas Hohaus - Initial contribution */ - +@NonNullByDefault public class PHCHelper { /** @@ -47,17 +49,13 @@ public static ThingUID getThingUIDreverse(ThingTypeUID thingTypeUID, byte module * @param b * @return */ - public static Object byteToBinaryString(byte b) { - return StringUtils.leftPad(StringUtils.trim(Integer.toBinaryString(b & 0xFF)), 8, '0') + " "; - } - - /** - * Convert the byte b into an hex String - * - * @param b - * @return - */ - public static Object byteToHexString(byte b) { - return Integer.toHexString(b & 0xFF) + " "; + public static Object bytesToBinaryString(byte[] bytes) { + StringBuilder bin = new StringBuilder(); + for (byte b : bytes) { + bin.append(StringUtils.leftPad(StringUtils.trim(Integer.toBinaryString(b & 0xFF)), 8, '0')); + bin.append(' '); + } + + return bin.toString(); } } diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/InternalBuffer.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/InternalBuffer.java new file mode 100644 index 0000000000000..962492ae4ef66 --- /dev/null +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/InternalBuffer.java @@ -0,0 +1,74 @@ +/** + * 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.phc.internal.handler; + +import java.util.Arrays; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * Buffer for received messages + * + * @author Jonas Hohaus - Initial contribution + */ +class InternalBuffer { + private static final int MAX_SIZE = 512; + + private final BlockingQueue byteQueue = new LinkedBlockingQueue<>(); + private byte[] buffer; + private int bufferIndex = 0; + private int size; + + public void offer(byte[] buffer) { + // If the buffer becomes too large, already processed commands accumulate and + // the reaction becomes slow. + if (size < MAX_SIZE) { + byte[] localBuffer = Arrays.copyOf(buffer, Math.min(MAX_SIZE - size, buffer.length)); + byteQueue.offer(localBuffer); + synchronized (this) { + size += localBuffer.length; + } + } + } + + public boolean hasNext() { + return (size > 0); + } + + public byte get() throws InterruptedException { + byte[] buf = getBuffer(); + if (buf != null) { + byte result = buf[bufferIndex++]; + synchronized (this) { + size--; + } + + return result; + } + + throw new IllegalStateException("get without hasNext"); + } + + public int size() { + return size; + } + + private byte[] getBuffer() throws InterruptedException { + if (buffer == null || bufferIndex == buffer.length) { + buffer = byteQueue.take(); + bufferIndex = 0; + } + + return buffer; + } +} diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java index 8ce1c55a765dc..bd8ce2f2b8ebf 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCBridgeHandler.java @@ -16,20 +16,20 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TooManyListenersException; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledThreadPoolExecutor; -import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.library.types.StopMoveType; -import org.eclipse.smarthome.core.library.types.UpDownType; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; @@ -38,64 +38,72 @@ import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.State; +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.phc.internal.PHCBindingConstants; import org.openhab.binding.phc.internal.PHCHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gnu.io.CommPortIdentifier; -import gnu.io.NoSuchPortException; -import gnu.io.PortInUseException; -import gnu.io.RXTXPort; -import gnu.io.SerialPort; -import gnu.io.SerialPortEvent; -import gnu.io.SerialPortEventListener; -import gnu.io.UnsupportedCommOperationException; - /** * The {@link PHCBridgeHandler} is responsible for handling the serial Communication to and from the PHC Modules. * * @author Jonas Hohaus - Initial contribution */ - +@NonNullByDefault public class PHCBridgeHandler extends BaseBridgeHandler implements SerialPortEventListener { private final Logger logger = LoggerFactory.getLogger(PHCBridgeHandler.class); private static final int BAUD = 19200; - private static final int SEND_RETRY_COUNT = 15; // max count to send the same message - private static final int SEND_RETRY_TIME_MILLIS = 80; // time to wait for an acknowledge before send the message + private static final int SEND_RETRY_COUNT = 20; // max count to send the same message + private static final int SEND_RETRY_TIME_MILLIS = 60; // time to wait for an acknowledge before send the message // again in milliseconds - private InputStream serialIn; - private OutputStream serialOut; - short lastReceivedCrc; - byte[] messageFragment; - RXTXPort serialPort; + private @Nullable InputStream serialIn; + private @Nullable OutputStream serialOut; + private @Nullable SerialPort commPort; + private final SerialPortManager serialPortManager; - private final Map toggleMap = new HashMap<>(); - private final ConcurrentNavigableMap queue = new ConcurrentSkipListMap<>(); + private final Map toggleMap = new HashMap<>(); + private final InternalBuffer buffer = new InternalBuffer(); + private final BlockingQueue receiveQueue = new LinkedBlockingQueue<>(); + private final BlockingQueue sendQueue = new LinkedBlockingQueue<>(); + private final ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(3); private final byte emLedOutputState[] = new byte[32]; private final byte amOutputState[] = new byte[32]; + private final byte dmOutputState[] = new byte[32]; private final List modules = new ArrayList<>(); - public PHCBridgeHandler(Bridge phcBridge) { + public PHCBridgeHandler(Bridge phcBridge, SerialPortManager serialPortManager) { super(phcBridge); + this.serialPortManager = serialPortManager; } @Override public void initialize() { String port = ((String) getConfig().get(PHCBindingConstants.PORT)); - try { - // find the given port - CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(port); + // find the given port + SerialPortIdentifier portId = serialPortManager.getIdentifier(port); + if (portId == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Serial port '" + port + "' could not be found."); + return; + } + + try { // initialize serial port - serialPort = portId.open(this.getClass().getName(), 2000); // owner, timeout + SerialPort serialPort = portId.open(this.getClass().getName(), 2000); // owner, timeout serialIn = serialPort.getInputStream(); // set port parameters serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_2, SerialPort.PARITY_NONE); @@ -104,209 +112,358 @@ public void initialize() { serialPort.addEventListener(this); // activate the DATA_AVAILABLE notifier serialPort.notifyOnDataAvailable(true); - serialPort.setRTS(true); // get the output stream serialOut = serialPort.getOutputStream(); - sendPorBroadcast((byte) 0xFF); + commPort = serialPort; + + sendPorBroadcast(); byte[] b = { 0x01 }; for (int j = 0; j <= 0x1F; j++) { serialWrite(buildMessage((byte) j, 0, b, false)); - } updateStatus(ThingStatus.ONLINE); - new Thread() { + // receive messages + threadPoolExecutor.execute(new Runnable() { @Override public void run() { - processQueueLoop(); + processReceivedBytes(); } - }.start(); + }); + + // process received messages + threadPoolExecutor.execute(new Runnable() { + + @Override + public void run() { + processReceiveQueue(); + } + }); + + // sendig commands to the modules + threadPoolExecutor.execute(new Runnable() { + + @Override + public void run() { + processSendQueue(); + } + }); } catch (PortInUseException | TooManyListenersException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Could not open serial port " + serialPort + ": " + e.getMessage()); + "Could not open serial port " + port + ": " + e.getMessage()); } catch (UnsupportedCommOperationException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Could not configure serial port " + serialPort + ": " + e.getMessage()); - } catch (NoSuchPortException e) { - StringBuilder sb = new StringBuilder(); - @SuppressWarnings("unchecked") - Enumeration portList = CommPortIdentifier.getPortIdentifiers(); - while (portList.hasMoreElements()) { - CommPortIdentifier id = portList.nextElement(); - if (id.getPortType() == CommPortIdentifier.PORT_SERIAL) { - sb.append(id.getName() + "\n"); - } - } - logger.warn("Serial port '{}' could not be found. Available ports are:\n {}", port, sb); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Serial port '" + port + "' could not be found."); + "Could not configure serial port " + port + ": " + e.getMessage()); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Failed to get input or output stream for serialPort: " + e.getMessage()); + logger.debug("Failed to get inputstream for serialPort", e); } } /** - * Puts the available data on serial port into a buffer and transfer it to the processing method. + * Reads the data on serial port and puts it into the internal buffer. */ @Override public void serialEvent(SerialPortEvent event) { - if (event.getEventType() != SerialPortEvent.DATA_AVAILABLE) { - return; + if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE && serialIn != null) { + try { + byte[] bytes = new byte[serialIn.available()]; + serialIn.read(bytes); + + buffer.offer(bytes); + + if (logger.isTraceEnabled()) { + logger.trace("buffer offered {}", HexUtils.bytesToHex(bytes, " ")); + } + } catch (IOException e) { + logger.warn("Error on reading input stream to internal buffer", e); + } } + } - byte[] buffer; + /** + * process internal incoming buffer (recognize on read messages) + */ + private void processReceivedBytes() { + int faultCounter = 0; try { - if (serialIn.available() <= 0) { - return; - } + byte module = buffer.get(); + + while (true) { + // Recognition of messages from byte buffer. + // not a known module address + if (!modules.contains(module)) { + module = buffer.get(); + continue; + } - Thread.sleep(5); - buffer = new byte[serialIn.available()]; - serialIn.read(buffer); + if (logger.isDebugEnabled()) { + logger.debug("get module: {}", new String(HexUtils.byteToHex(module))); + } - if (messageFragment != null) { - processInputStream(ArrayUtils.addAll(messageFragment, buffer)); - } else { - processInputStream(buffer); + byte sizeToggle = buffer.get(); + + // read length of command and check if makes sense + if ((sizeToggle < 1 || sizeToggle > 3) && ((sizeToggle & 0xFF) < 0x81 || (sizeToggle & 0xFF) > 0x83)) { + if (logger.isDebugEnabled()) { + logger.debug("get invalid sizeToggle: {}", new String(HexUtils.byteToHex(sizeToggle))); + } + + module = sizeToggle; + continue; + } + + // read toggle, size and command + int size = (sizeToggle & 0x7F); + boolean toggle = (sizeToggle & 0x80) == 0x80; + + logger.debug("get toggle: {}", toggle); + + byte[] command = new byte[size]; + + for (int i = 0; i < size; i++) { + command[i] = buffer.get(); + } + + // log command + if (logger.isTraceEnabled()) { + logger.trace("command read: {}", PHCHelper.bytesToBinaryString(command)); + } + + // read crc + byte crcByte1 = buffer.get(); + byte crcByte2 = buffer.get(); + + short crc = (short) (crcByte1 & 0xFF); + crc |= (crcByte2 << 8); + + // calculate checkCrc + short checkCrc = calcCrc(module, sizeToggle, command); + + // check crc + if (crc != checkCrc) { + logger.debug("CRC not correct (crc from message, calculated crc): {}, {}", crc, checkCrc); + + faultCounter = handleCrcFault(faultCounter); + + module = buffer.get(); + continue; + } + + if (logger.isTraceEnabled()) { + logger.trace("get crc: {}", HexUtils.bytesToHex(new byte[] { crcByte1, crcByte2 }, " ")); + } + + faultCounter = 0; + + processReceivedMsg(module, toggle, command); + module = buffer.get(); } - } catch (IOException e) { - // ignore } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } - /** - * Split and process the given buffer to single messages. - * - * @param buffer - */ - private void processInputStream(byte[] buffer) { - List result = new ArrayList<>(); - List repeat = new ArrayList<>(); - int pos = 0; - messageFragment = null; - Byte moduleAddress = null; - - while (pos < buffer.length) { - int messageStart = 0; - - for (byte b : buffer) { - if (modules.contains(b)) { - moduleAddress = b; - messageStart = pos++; - break; + private int handleCrcFault(int faultCounter) throws InterruptedException { + if (faultCounter > 0) { + // Normally in this case we read the message repeatedly offset to the real -> skip one to 6 bytes + for (int i = 0; i < faultCounter; i++) { + if (buffer.hasNext()) { + buffer.get(); } - pos++; } + } + + int resCounter = faultCounter + 1; + if (resCounter > 6) { + resCounter = 0; + } + return resCounter; + } - if (moduleAddress == null) { - return; + private void processReceivedMsg(byte module, boolean toggle, byte[] command) { + // Acknowledgement received (command first byte 0) + if (command[0] == 0) { + String moduleType; + byte channel = 0; // only needed for dim + if ((module & 0xE0) == 0x40) { + moduleType = PHCBindingConstants.CHANNELS_AM; + } else if ((module & 0xE0) == 0xA0) { + moduleType = PHCBindingConstants.CHANNELS_DIM; + channel = (byte) ((command[0] >>> 5) & 0x0F); + } else { + moduleType = PHCBindingConstants.CHANNELS_EM_LED; } - if (buffer.length - 1 - pos < 3) { - messageFragment = new byte[buffer.length - pos + 1]; - System.arraycopy(buffer, messageStart, messageFragment, 0, messageFragment.length); + setModuleOutputState(moduleType, (byte) (module & 0x1F), command[1], channel); + toggleMap.put(module, !toggle); + + // initialization (first byte FF) + } else if (command[0] == (byte) 0xFF) { + if ((module & 0xE0) == 0x00) { // EM + sendEmConfig(module); + } else if ((module & 0xE0) == 0x40 || (module & 0xE0) == 0xA0) { // AM, JRM and DIM + sendAmConfig(module); } - byte size = 0; - result.add(buffer[pos]); - size = (byte) (buffer[pos] & 0x7F); + logger.debug("initialization: {}", module); - if (size > 0 && size < 4) { - if (buffer.length - 1 - pos < size + 2) { - messageFragment = new byte[buffer.length - pos + 1]; - System.arraycopy(buffer, messageStart, messageFragment, 0, messageFragment.length); - } + // ignored - ping (first byte 01) + } else if (command[0] == 0x01) { + logger.debug("first byte 0x01 -> ignored"); + + // EM command / update + } else { + if (((module & 0xE0) == 0x00)) { + sendEmAcknowledge(module, toggle); + logger.debug("send acknowledge (modul, toggle) {} {}", module, toggle); - byte[] message = new byte[size + 4]; - message[0] = moduleAddress; - message[1] = buffer[pos++]; + byte channel = (byte) ((command[0] >>> 4) & 0x0F); - for (int i = 2; i < size + 4; i++) { - message[i] = buffer[pos++]; - result.add(message[i]); + OnOffType onOff = OnOffType.OFF; + + if ((command[0] & 0x0F) == 2) { + onOff = OnOffType.ON; } - if (!repeat.contains(Arrays.toString(message))) { // avoids multiple processing of the same message - processIncomingMessage(message, size); - repeat.add(Arrays.toString(message)); + QueueObject qo = new QueueObject(PHCBindingConstants.CHANNELS_EM, module, channel, onOff); + + // put recognized message into queue + if (!receiveQueue.contains(qo)) { + receiveQueue.offer(qo); } + + // ignore if message not from EM module + } else if (logger.isDebugEnabled()) { + logger.debug("Incoming message (module, toggle, command) not from EM module: {} {} {}", + new String(HexUtils.byteToHex(module)), toggle, PHCHelper.bytesToBinaryString(command)); } } } - private void processQueueLoop() { + /** + * process receive queue + */ + private void processReceiveQueue() { while (true) { - long limit = System.currentTimeMillis(); - - Map subQueue = queue.subMap(0L, true, limit, true); - - for (Long key : subQueue.keySet()) { - QueueObject qo = subQueue.get(key); - queue.remove(key); - if (qo.getCounter() < SEND_RETRY_COUNT && !qo.getCommand() - .equals(isChannelOutputStateSet(qo.getModuleType(), qo.getModuleAddress(), qo.getChannel()))) { - qo.increaseCounter(); - - switch (qo.getModuleType()) { - case PHCBindingConstants.CHANNELS_AM: - sendAm(qo.getModuleAddress(), qo.getChannel(), qo.getCommand()); - break; - case PHCBindingConstants.CHANNELS_EM_LED: - sendEm(qo.getModuleAddress(), qo.getChannel(), qo.getCommand()); - break; - case PHCBindingConstants.CHANNELS_JRM: - sendJRM(qo.getModuleAddress(), qo.getChannel(), qo.getCommand(), qo.getUpDownTime()); - break; - } + try { + QueueObject qo = receiveQueue.take(); - queue.put(System.currentTimeMillis() + SEND_RETRY_TIME_MILLIS, qo); - } else if (qo.getCounter() >= SEND_RETRY_COUNT - && !qo.getModuleType().equals(PHCBindingConstants.CHANNELS_JRM)) { - // Can´t process the acknowledgement of JRM yet. - logger.info("No acknowlgdge from the module {} received.", qo.getModuleAddress()); - } + logger.debug("Consume Receive QueueObject: {}", qo); + handleIncomingCommand(qo.getModuleAddress(), qo.getChannel(), (OnOffType) qo.getCommand()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } } } - private void setModuleOutputState(String moduleType, byte moduleAddress, byte state) { - if (moduleType == PHCBindingConstants.CHANNELS_EM_LED) { - emLedOutputState[moduleAddress] = state; - } else if (moduleType == PHCBindingConstants.CHANNELS_AM) { - amOutputState[moduleAddress & 0x1F] = state; - } else if (moduleType == PHCBindingConstants.CHANNELS_JRM) { - // not implemented yet + /** + * process send queue + */ + private void processSendQueue() { + while (true) { + try { + QueueObject qo = sendQueue.take(); + + sendQueueObject(qo); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } } } - private State isChannelOutputStateSet(String moduleType, byte moduleAddress, byte channel) { - State set = null; + private void sendQueueObject(QueueObject qo) { + int sendCount = 0; + // Send the command to the module until a response is received. Max. SEND_RETRY_COUNT repeats. + do { + switch (qo.getModuleType()) { + case PHCBindingConstants.CHANNELS_AM: + sendAm(qo.getModuleAddress(), qo.getChannel(), qo.getCommand()); + break; + case PHCBindingConstants.CHANNELS_EM_LED: + sendEm(qo.getModuleAddress(), qo.getChannel(), qo.getCommand()); + break; + case PHCBindingConstants.CHANNELS_JRM: + sendJrm(qo.getModuleAddress(), qo.getChannel(), qo.getCommand(), qo.getTime()); + break; + case PHCBindingConstants.CHANNELS_DIM: + sendDim(qo.getModuleAddress(), qo.getChannel(), qo.getCommand(), qo.getTime()); + break; + } + + sendCount++; + try { + Thread.sleep(SEND_RETRY_TIME_MILLIS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } while (!isChannelOutputState(qo.getModuleType(), qo.getModuleAddress(), qo.getChannel(), qo.getCommand()) + && sendCount < SEND_RETRY_COUNT); + + if (PHCBindingConstants.CHANNELS_JRM.equals(qo.getModuleType())) { + // there aren't state per channel for JRM modules + amOutputState[qo.getModuleAddress() & 0x1F] = -1; + } else if (PHCBindingConstants.CHANNELS_DIM.equals(qo.getModuleType())) { + // state ist the same for every dim level except zero/off -> inizialize state + // with 0x0F after sending an command. + dmOutputState[qo.getModuleAddress() & 0x1F] |= (0x0F << (qo.getChannel() * 4)); + } + + if (sendCount >= SEND_RETRY_COUNT) { + // change the toggle: if no acknowledge received it may be wrong. + byte module = qo.getModuleAddress(); + if (PHCBindingConstants.CHANNELS_AM.equals(qo.getModuleType()) + || PHCBindingConstants.CHANNELS_JRM.equals(qo.getModuleType())) { + module |= 0x40; + } else if (PHCBindingConstants.CHANNELS_DIM.equals(qo.getModuleType())) { + module |= 0xA0; + } + toggleMap.put(module, !getToggle(module)); - if (moduleType.equals(PHCBindingConstants.CHANNELS_EM_LED)) { - set = ((emLedOutputState[moduleAddress] >> channel) & 0x01) == 1 ? OnOffType.ON : OnOffType.OFF; - } else if (moduleType.equals(PHCBindingConstants.CHANNELS_AM)) { - set = ((amOutputState[moduleAddress & 0x1F] >> channel) & 0x01) == 1 ? OnOffType.ON : OnOffType.OFF; - } else if (moduleType.equals(PHCBindingConstants.CHANNELS_JRM)) { - set = ((amOutputState[moduleAddress & 0x1F] >> channel) & 0x01) == 1 ? OnOffType.ON : OnOffType.OFF; + if (logger.isDebugEnabled()) { + logger.debug("No acknowledge from the module {} received.", qo.getModuleAddress()); + } } + } - return set; + private void setModuleOutputState(String moduleType, byte moduleAddress, byte state, byte channel) { + if (PHCBindingConstants.CHANNELS_EM_LED.equals(moduleType)) { + emLedOutputState[moduleAddress] = state; + } else if (PHCBindingConstants.CHANNELS_AM.equals(moduleType)) { + amOutputState[moduleAddress & 0x1F] = state; + } else if (PHCBindingConstants.CHANNELS_DIM.equals(moduleType)) { + dmOutputState[moduleAddress & 0x1F] = (byte) (state << channel * 4); + } + } + + private boolean isChannelOutputState(String moduleType, byte moduleAddress, byte channel, Command cmd) { + int state = OnOffType.OFF.equals(cmd) ? 0 : 1; + + if (PHCBindingConstants.CHANNELS_EM_LED.equals(moduleType)) { + return ((emLedOutputState[moduleAddress & 0x1F] >>> channel) & 0x01) == state; + } else if (PHCBindingConstants.CHANNELS_AM.equals(moduleType)) { + return ((amOutputState[moduleAddress & 0x1F] >>> channel) & 0x01) == state; + } else if (PHCBindingConstants.CHANNELS_JRM.equals(moduleType)) { + return (amOutputState[moduleAddress & 0x1F] != -1); + } else if (PHCBindingConstants.CHANNELS_DIM.equals(moduleType)) { + return ((dmOutputState[moduleAddress & 0x1F] >>> channel * 4) & 0x0F) != 0x0F; + } else { + return false; + } } - private boolean toggleChannel(byte moduleAddress, byte channel) { - String key = new String(new byte[] { moduleAddress, channel }); - boolean toggle = false; - if (toggleMap.containsKey(key)) { - toggle = toggleMap.get(key); + private boolean getToggle(byte moduleAddress) { + if (!toggleMap.containsKey(moduleAddress)) { + toggleMap.put(moduleAddress, false); } - toggleMap.put(key, !toggle); - return toggle; + return toggleMap.get(moduleAddress); } /** @@ -318,46 +475,49 @@ private boolean toggleChannel(byte moduleAddress, byte channel) { * @param command * @param upDownTime */ - public void send(String moduleType, String moduleAddress, String channel, Command command, short upDownTime) { - if (command instanceof OnOffType || command instanceof UpDownType || command.equals(StopMoveType.STOP)) { - if (moduleType.equals(PHCBindingConstants.CHANNELS_JRM)) { // can´t process acknowledge yet - queue.put(System.currentTimeMillis(), - new QueueObject(moduleType, moduleAddress, channel, command, 9, upDownTime)); - } else { - queue.put(System.currentTimeMillis(), new QueueObject(moduleType, moduleAddress, channel, command)); - } + public void send(@Nullable String moduleType, int moduleAddress, String channel, Command command, + short upDownTime) { + if (PHCBindingConstants.CHANNELS_JRM.equals(moduleType) + || PHCBindingConstants.CHANNELS_DIM.equals(moduleType)) { + sendQueue.offer(new QueueObject(moduleType, moduleAddress, channel, command, upDownTime)); + } else { + sendQueue.offer(new QueueObject(moduleType, moduleAddress, channel, command)); } } private void sendAm(byte moduleAddress, byte channel, Command command) { - moduleAddress |= 0x40; + byte module = (byte) (moduleAddress | 0x40); + byte[] cmd = { (byte) (channel << 5) }; - if (command.equals(OnOffType.ON)) { + if (OnOffType.ON.equals(command)) { cmd[0] |= 2; } else { cmd[0] |= 3; } - serialWrite(buildMessage(moduleAddress, channel, cmd, toggleChannel(moduleAddress, channel))); + serialWrite(buildMessage(module, channel, cmd, getToggle(module))); } private void sendEm(byte moduleAddress, byte channel, Command command) { byte[] cmd = { (byte) (channel << 4) }; - if (command.equals(OnOffType.ON)) { + if (OnOffType.ON.equals(command)) { cmd[0] |= 2; } else { cmd[0] |= 3; } - serialWrite(buildMessage(moduleAddress, channel, cmd, toggleChannel(moduleAddress, channel))); + serialWrite(buildMessage(moduleAddress, channel, cmd, getToggle(moduleAddress))); } - private void sendJRM(byte moduleAddress, byte channel, Command command, short upDownTime) { + private void sendJrm(byte moduleAddress, byte channel, Command command, short upDownTime) { // The up and the down message needs two additional bytes for the time. int size = (command == StopMoveType.STOP) ? 2 : 4; byte[] cmd = new byte[size]; + if (channel == 0) { + channel = 4; + } - moduleAddress |= 0x40; + byte module = (byte) (moduleAddress | 0x40); cmd[0] = (byte) (channel << 5); cmd[1] = 0x3F; @@ -378,12 +538,39 @@ private void sendJRM(byte moduleAddress, byte channel, Command command, short up break; } - serialWrite(buildMessage(moduleAddress, channel, cmd, toggleChannel(moduleAddress, channel))); + serialWrite(buildMessage(module, channel, cmd, getToggle(module))); + } + + private void sendDim(byte moduleAddress, byte channel, Command command, short dimTime) { + byte module = (byte) (moduleAddress | 0xA0); + byte[] cmd = new byte[(command instanceof PercentType && !(((PercentType) command).byteValue() == 0)) ? 3 : 1]; + + cmd[0] = (byte) (channel << 5); + + if (command instanceof OnOffType) { + if (OnOffType.ON.equals(command)) { + cmd[0] |= 3; + } else if (OnOffType.OFF.equals(command)) { + cmd[0] |= 4; + } + } else { + if (((PercentType) command).byteValue() == 0) { + cmd[0] |= 4; + } else { + cmd[0] |= 22; + cmd[1] = (byte) (((PercentType) command).byteValue() * 2.55); + cmd[2] = (byte) dimTime; + } + } + serialWrite(buildMessage(module, channel, cmd, getToggle(module))); } - private void sendPorBroadcast(byte b) { - byte[] cmd = { 0 }; - writeMsg(buildMessage((byte) 0xFF, 0, cmd, false), 20); // 20 times needed? + private void sendPorBroadcast() { + byte[] msg = buildMessage((byte) 0xFF, 0, new byte[] { 0 }, false); + for (int i = 0; i < 20; i++) { + serialWrite(msg); + + } } private void sendAmConfig(byte moduleAddress) { @@ -416,8 +603,11 @@ private void sendEmConfig(byte moduleAddress) { } private void sendEmAcknowledge(byte module, boolean toggle) { - byte[] cmd = { 0 }; - writeMsg(buildMessage(module, 0, cmd, toggle), 2); // 2 times needed? + byte[] msg = buildMessage(module, 0, new byte[] { 0 }, toggle); + for (int i = 0; i < 3; i++) { // send three times stops the module faster from sending messages if the first + // response is not recognized. + serialWrite(msg); + } } /** @@ -436,16 +626,9 @@ private byte[] buildMessage(byte modulAddr, int channel, byte[] cmd, boolean tog buffer[0] = modulAddr; buffer[1] = (byte) (toggle ? (len | 0x80) : len); // 0x80: 1000 0000 - for (int i = 0; i < len; i++) { - buffer[2 + i] = cmd[i]; - } - - short crc = (short) 0xFFFF; + System.arraycopy(cmd, 0, buffer, 2, len); - for (int i = 0; i < (2 + len); i++) { - crc = crc16Update(crc, buffer[i]); - } - crc ^= 0xFFFF; + short crc = calcCrc(modulAddr, buffer[1], cmd); buffer[2 + len] = (byte) (crc & 0xFF); buffer[3 + len] = (byte) ((crc >> 8) & 0xFF); @@ -454,97 +637,41 @@ private byte[] buildMessage(byte modulAddr, int channel, byte[] cmd, boolean tog } /** - * Calculate/update the 16 bit crc of the message. - * - * @param crc - * @param data + * Calculate the 16 bit crc of the message. + * + * @param module + * @param sizeToggle + * @param cmd * @return */ - private short crc16Update(short crc, byte data) { - data ^= crc & 0xFF; - data ^= data << 4; - short data16 = data; + private short calcCrc(byte module, byte sizeToggle, byte[] cmd) { + short crc = (short) 0xFFFF; - crc = (short) (((data16 << 8) | (((crc >> 8) & 0xFF) & 0xFF)) ^ ((data >> 4) & 0xF) - ^ ((data16 << 3) & 0b11111111111)); + crc = crc16Update(crc, module); + crc = crc16Update(crc, sizeToggle); + + for (byte b : cmd) { + crc = crc16Update(crc, b); + } + + crc ^= 0xFFFF; return crc; } /** - * Process the incoming message and start the reaction. + * Update the 16 bit crc of the message. * - * @param inByte - * @param size + * @param crc + * @param data + * @return */ - private void processIncomingMessage(byte[] inByte, int size) { - short calcCrc = (short) 0xFFFF; - short rcvCrc = (short) (inByte[2 + size] & 0xFF); - rcvCrc |= (inByte[size + 3] << 8); - boolean toggleIn = false; - - if ((inByte[1] & 0x80) == 0x80) { - toggleIn = true; - } - - for (int i = 0; i < (size + 2); i++) { - calcCrc = crc16Update(calcCrc, inByte[i]); - } - calcCrc ^= 0xFFFF; - - if (rcvCrc != calcCrc) { - return; - } - - byte moduleAddress = inByte[0]; - - byte[] cmd = new byte[size]; - for (int i = 0; i < cmd.length; i++) { - cmd[i] = inByte[2 + i]; - } - - int[] channel = new int[size]; - for (int i = 0; i < cmd.length; i++) { - channel[i] = (inByte[2 + i] >> 4) & 0xF; - } - - if ((moduleAddress & 0xE0) != 0xE0) { - if (cmd[0] == 0) { - if ((((moduleAddress & 0xE0) == 0x40) || ((moduleAddress & 0xE0) == 0x00)) && (cmd.length == 2)) { - String moduleType = ((moduleAddress & 0xE0) == 0x40) ? PHCBindingConstants.CHANNELS_AM - : PHCBindingConstants.CHANNELS_EM_LED; - setModuleOutputState(moduleType, moduleAddress, cmd[1]); - } - } else if (cmd[0] == (byte) 0xFF) { - if ((moduleAddress & 0xE0) == 0x00) { // EM - sendEmConfig(moduleAddress); - } - if ((moduleAddress & 0xE0) == 0x40) { // AM and JRM - sendAmConfig(moduleAddress); - } - - } else if (cmd[0] != 0x01) { // I'm not sure what the command 0x01 means. It isn't relevant for normal - // functionality. - if ((moduleAddress & 0xE0) == 0) { - if (rcvCrc == lastReceivedCrc) { - sendEmAcknowledge(moduleAddress, toggleIn); // Just send the acknowledge message again, when - // PHC didn't recognize it. - } else { - sendEmAcknowledge(moduleAddress, toggleIn); - handleIncomingCommand(moduleAddress, channel[0], cmd); - - } - } - } - } + private short crc16Update(short crc, byte messagePart) { + byte data = (byte) (messagePart ^ (crc & 0xFF)); + data ^= data << 4; + short data16 = data; - if (logger.isDebugEnabled()) { - StringBuilder logMessage = new StringBuilder(); - for (int i = 0; i < inByte.length; i++) { - logMessage.append(PHCHelper.byteToBinaryString(inByte[i])); - } - logMessage.append(", " + inByte.length); - logger.debug("received: {}", logMessage); - } + return (short) (((data16 << 8) | (((crc >> 8) & 0xFF) & 0xFF)) ^ ((data >> 4) & 0xF) + ^ ((data16 << 3) & 0b11111111111)); } /** @@ -555,55 +682,40 @@ private void processIncomingMessage(byte[] inByte, int size) { * @param cmd * @param rcvCrc */ - private void handleIncomingCommand(byte moduleAddress, int channel, byte[] cmd) { + private void handleIncomingCommand(byte moduleAddress, int channel, OnOffType onOff) { ThingUID uid = PHCHelper.getThingUIDreverse(PHCBindingConstants.THING_TYPE_EM, moduleAddress); Thing thing = getThingByUID(uid); String channelId = "em#" + StringUtils.leftPad(Integer.toString(channel), 2, '0'); - if (thing != null) { - OnOffType state = OnOffType.OFF; - if ((cmd[0] & 0x0F) == 2) { - state = OnOffType.ON; - } + if (thing != null && thing.getHandler() != null) { + logger.debug("Input: {}, {}, {}", thing.getUID(), channelId, onOff); - logger.debug("{}, {}", thing.getUID(), state); - ((PHCHandler) thing.getHandler()).handleIncoming(channelId, state); + PHCHandler handler = (PHCHandler) thing.getHandler(); + if (handler != null) { + handler.handleIncoming(channelId, onOff); + } else { + logger.debug("No Handler for Thing {} available.", thing.getUID()); + } } else { - logger.info("No Thing with UID {} available.", uid.getAsString()); - } - } - - /** - * Send message to to serial port x times. - * - * @param msg - * @param sendCnt - */ - private void writeMsg(byte[] msg, int sendCnt) { - while (0 < sendCnt) { - serialWrite(msg); - sendCnt--; + logger.debug("No Thing with UID {} available.", uid.getAsString()); } } private void serialWrite(byte[] msg) { - try { - // write to serial port - serialOut.write(msg); - serialOut.flush(); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Error writing '" + msg + "' to serial port : " + e.getMessage()); - } + if (serialOut != null) { + try { + // write to serial port + serialOut.write(msg); + serialOut.flush(); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Error writing '" + msg + "' to serial port : " + e.getMessage()); + } - if (logger.isDebugEnabled()) { - StringBuilder log = new StringBuilder(); - for (byte b : msg) { - log.append(PHCHelper.byteToBinaryString(b)); - log.append(' '); + if (logger.isTraceEnabled()) { + logger.trace("send: {}", PHCHelper.bytesToBinaryString(msg)); } - logger.debug("send: {}", log); } } @@ -620,4 +732,13 @@ public void addModule(byte module) { public void handleCommand(ChannelUID channelUID, Command command) { // unnecessary } + + @Override + public void dispose() { + threadPoolExecutor.shutdownNow(); + if (commPort != null) { + commPort.close(); + commPort = null; + } + } } diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCHandler.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCHandler.java index 9f80f00d6a7e0..adadb484d00b7 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCHandler.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/PHCHandler.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.phc.internal.handler; +import static org.openhab.binding.phc.internal.PHCBindingConstants.*; + import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; @@ -20,6 +22,7 @@ import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.library.types.StopMoveType; import org.eclipse.smarthome.core.library.types.UpDownType; import org.eclipse.smarthome.core.thing.Bridge; @@ -31,7 +34,6 @@ import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.phc.internal.PHCBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +49,8 @@ public class PHCHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(PHCHandler.class); private String moduleAddress; // like DIP switches - private final short[] upDownTimes = new short[4]; + private byte module; + private final short[] times = new short[4]; private final Map channelState = new HashMap<>(); private PHCBridgeHandler bridgeHandler; @@ -57,27 +60,30 @@ public PHCHandler(Thing thing) { @Override public void initialize() { - moduleAddress = (String) getConfig().get(PHCBindingConstants.ADDRESS); + moduleAddress = (String) getConfig().get(ADDRESS); if (getPHCBridgeHandler() == null) { return; } - getPHCBridgeHandler() - .addModule(Byte.parseByte(getThing().getThingTypeUID().equals(PHCBindingConstants.THING_TYPE_EM) - ? new StringBuilder(moduleAddress).reverse().toString() - : ("010" + new StringBuilder(moduleAddress).reverse().toString()), 2)); // 010x = 0x4x for - // AM and JRM - - if (getThing().getThingTypeUID().equals(PHCBindingConstants.THING_TYPE_JRM)) { - upDownTimes[0] = (short) (((BigDecimal) getConfig().get(PHCBindingConstants.UP_DOWN_TIME_1)).shortValue() - * 10); - upDownTimes[1] = (short) (((BigDecimal) getConfig().get(PHCBindingConstants.UP_DOWN_TIME_2)).shortValue() - * 10); - upDownTimes[2] = (short) (((BigDecimal) getConfig().get(PHCBindingConstants.UP_DOWN_TIME_3)).shortValue() - * 10); - upDownTimes[3] = (short) (((BigDecimal) getConfig().get(PHCBindingConstants.UP_DOWN_TIME_4)).shortValue() - * 10); + module = Byte.parseByte(new StringBuilder(moduleAddress).reverse().toString(), 2); + + if (getThing().getThingTypeUID().equals(THING_TYPE_AM) || getThing().getThingTypeUID().equals(THING_TYPE_JRM)) { + module |= 0x40; + } else if (getThing().getThingTypeUID().equals(THING_TYPE_DIM)) { + module |= 0xA0; + } + getPHCBridgeHandler().addModule(module); + + if (getThing().getThingTypeUID().equals(THING_TYPE_JRM)) { + times[0] = (short) (((BigDecimal) getConfig().get(UP_DOWN_TIME_1)).shortValue() * 10); + times[1] = (short) (((BigDecimal) getConfig().get(UP_DOWN_TIME_2)).shortValue() * 10); + times[2] = (short) (((BigDecimal) getConfig().get(UP_DOWN_TIME_3)).shortValue() * 10); + times[3] = (short) (((BigDecimal) getConfig().get(UP_DOWN_TIME_4)).shortValue() * 10); + + } else if (getThing().getThingTypeUID().equals(THING_TYPE_DIM)) { + times[0] = (((BigDecimal) getConfig().get(DIM_TIME_1)).shortValue()); + times[1] = (((BigDecimal) getConfig().get(DIM_TIME_2)).shortValue()); } Bridge bridge = getBridge(); @@ -89,35 +95,40 @@ public void initialize() { } public void handleIncoming(String channelId, OnOffType state) { + if (logger.isDebugEnabled()) { + logger.debug("EM command: {}, last: {}, in: {}", channelId, channelState.get(channelId), state); + } + if (!channelState.containsKey(channelId) || !channelState.get(channelId).equals(state)) { postCommand(channelId, state); channelState.put(channelId, state); } - logger.debug("EM command: {}, last: {}, in: {}", channelId, channelState.get(channelId), state); } @Override public void handleCommand(ChannelUID channelUID, Command command) { - if ((PHCBindingConstants.CHANNELS_AM.equals(channelUID.getGroupId())) || PHCBindingConstants.CHANNELS_JRM - .equals(channelUID.getGroupId()) - && (command instanceof OnOffType || command instanceof UpDownType || command instanceof StopMoveType)) { - if (getThing().getStatus().equals(ThingStatus.ONLINE)) { - getPHCBridgeHandler().send(channelUID.getGroupId(), - new StringBuilder(moduleAddress).reverse().toString(), channelUID.getIdWithoutGroup(), command, - PHCBindingConstants.CHANNELS_JRM.equals(channelUID.getGroupId()) - ? upDownTimes[Integer.parseInt(channelUID.getIdWithoutGroup())] - : 0); - logger.debug("send command: {}, {}", channelUID, command); - } else { - logger.info("The Thing {} is offline it requires to select a Bridge", getThing().getUID()); + final String groupId = channelUID.getGroupId(); + if (getThing().getStatus().equals(ThingStatus.ONLINE)) { + if ((CHANNELS_JRM.equals(groupId) && (command instanceof UpDownType || command instanceof StopMoveType)) + || (CHANNELS_DIM.equals(groupId) + && (command instanceof OnOffType || command instanceof PercentType))) { + getPHCBridgeHandler().send(groupId, module & 0x1F, channelUID.getIdWithoutGroup(), command, + times[Integer.parseInt(channelUID.getIdWithoutGroup())]); + } else if ((CHANNELS_AM.equals(groupId) || CHANNELS_EM_LED.equals(groupId)) + && command instanceof OnOffType) { + getPHCBridgeHandler().send(groupId, module & 0x1F, channelUID.getIdWithoutGroup(), command, (short) 0); } + + logger.debug("send command: {}, {}", channelUID, command); + } else { + logger.info("The Thing {} is offline.", getThing().getUID()); } } @Override public void handleUpdate(ChannelUID channelUID, State newState) { - if (PHCBindingConstants.CHANNELS_JRM_TIME.equals(channelUID.getGroupId())) { - upDownTimes[Integer + if (CHANNELS_JRM_TIME.equals(channelUID.getGroupId())) { + times[Integer .parseInt(channelUID.getIdWithoutGroup())] = (short) (((DecimalType) newState).floatValue() * 10); } } @@ -129,7 +140,7 @@ public void handleConfigurationUpdate(Map configurationParameter Configuration configuration = editConfiguration(); for (Entry configurationParmeter : configurationParameters.entrySet()) { - if (!configurationParmeter.getKey().equals(PHCBindingConstants.ADDRESS)) { + if (!configurationParmeter.getKey().equals(ADDRESS)) { configuration.put(configurationParmeter.getKey(), configurationParmeter.getValue()); } else { configuration.put(configurationParmeter.getKey(), moduleAddress); @@ -159,6 +170,10 @@ private PHCBridgeHandler getPHCBridgeHandler() { bridgeHandler = (PHCBridgeHandler) handler; } else { logger.debug("No available bridge handler for {}.", bridge.getUID()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_MISSING_ERROR, + "No available bridge handler."); + return null; } } diff --git a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/QueueObject.java b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/QueueObject.java index 0ac16a75edc16..50ee754d11cf4 100644 --- a/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/QueueObject.java +++ b/bundles/org.openhab.binding.phc/src/main/java/org/openhab/binding/phc/internal/handler/QueueObject.java @@ -25,24 +25,25 @@ class QueueObject { private final byte channel; private final Command command; - private int counter; - private short upDownTime; + private short time; - public QueueObject(String moduleType, String moduleAddress, String channel, Command command) { + public QueueObject(String moduleType, byte moduleAddress, byte channel, Command command) { this.moduleType = moduleType; - this.moduleAddress = Byte.parseByte(moduleAddress, 2); - this.channel = Byte.parseByte(channel); + this.moduleAddress = moduleAddress; + this.channel = channel; this.command = command; } - public QueueObject(String moduleType, String moduleAddress, String channel, Command command, int counter, - short upDownTime) { + public QueueObject(String moduleType, int moduleAddress, String channel, Command command) { this.moduleType = moduleType; - this.moduleAddress = Byte.parseByte(moduleAddress, 2); + this.moduleAddress = (byte) moduleAddress; this.channel = Byte.parseByte(channel); this.command = command; - this.counter = counter; - this.upDownTime = upDownTime; + } + + public QueueObject(String moduleType, int moduleAddress, String channel, Command command, short time) { + this(moduleType, moduleAddress, channel, command); + this.time = time; } public String getModuleType() { @@ -61,15 +62,20 @@ public Command getCommand() { return command; } - public void increaseCounter() { - counter++; + public short getTime() { + return time; } - public int getCounter() { - return counter; - } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("moduleType: "); + sb.append(moduleType); + sb.append(", moduleAddress: "); + sb.append(moduleAddress); + sb.append(", channel: "); + sb.append(channel); - public short getUpDownTime() { - return upDownTime; + return sb.toString(); } } diff --git a/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/binding/binding.xml index a32f68eae9269..947c51567a097 100644 --- a/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/binding/binding.xml @@ -1,10 +1,10 @@ - + PHC Binding - This is a binding for PHC modules (EM, AM and JRM). It communicates with the PHC Modulbus (RS485). + This is a binding for PHC modules (EM, AM, JRM and DIM). It communicates with the PHC Modulbus (RS485). Jonas Hohaus diff --git a/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/channel-types.xml index 8d2cce61a54aa..a5d66e1995124 100644 --- a/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/channel-types.xml +++ b/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/channel-types.xml @@ -42,28 +42,6 @@ - - - Outgoing shutter channels (relay). - - - - - - - - - - - Time for shutter channels in seconds with an accuracy of 1/10 seconds. - - - - - - - - Switch @@ -88,6 +66,13 @@ Number The Time in seconds for an JRM channel. + + + + + Dimmer + + Channel for a DIM module. diff --git a/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/thing-types.xml index 383231536f61f..14e25b40996aa 100644 --- a/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.phc/src/main/resources/ESH-INF/thing/thing-types.xml @@ -27,7 +27,7 @@ - Thing for an AM channel. + Thing for an output/relay module (AM). @@ -49,7 +49,7 @@ - Thing for an EM channel. + Thing for an input/switch module (EM). @@ -71,12 +71,18 @@ - Thing for an JRM channel. - - - - - + Thing for an shutter module (JRM). + + + + + + + + + + + @@ -84,35 +90,65 @@ Address of the module as binary, like the DIP switches. true - + true The Time which the first shutter needs to move up/down. - false 30 - + true The Time which the second shutter needs to move up/down. - false 30 - + true The Time which the third shutter needs to move up/down. - false 30 - + true The Time which the fourth shutter needs to move up/down. - false 30 + + + + + + + Thing for a dimmer module (DM). + + + + + + + + + + Address of the module as binary, like the DIP switches. + true + + + true + + The time (in seconds) in which the first dimmer should dim 100%. + 2 + + + true + + The time (in seconds) in which the second dimmer should dim 100%. + 2 + + + +