From 4b70da49db510cfc31b4653bef4fbfdbea79a3af Mon Sep 17 00:00:00 2001 From: Bob A Date: Wed, 31 Mar 2021 16:44:58 -0400 Subject: [PATCH] [lutron] Add support for bridged RadioRA (classic) systems (#10302) Signed-off-by: Bob Adair --- bundles/org.openhab.binding.lutron/README.md | 30 +++++++++----- .../internal/radiora/RS232Connection.java | 41 +++++++++++++++---- .../internal/radiora/RS232MessageParser.java | 7 +++- .../internal/radiora/RadioRAConnection.java | 3 ++ .../radiora/RadioRAConnectionException.java | 3 ++ .../radiora/RadioRAFeedbackListener.java | 2 + .../internal/radiora/config/DimmerConfig.java | 19 ++------- .../radiora/config/PhantomButtonConfig.java | 21 +++------- .../internal/radiora/config/RS232Config.java | 21 +++------- .../internal/radiora/config/SwitchConfig.java | 12 +++--- .../radiora/handler/DimmerHandler.java | 31 ++++++++++---- .../radiora/handler/LutronHandler.java | 12 +++++- .../radiora/handler/PhantomButtonHandler.java | 20 +++++++-- .../radiora/handler/RS232Handler.java | 8 +++- .../radiora/handler/SwitchHandler.java | 27 +++++++++--- .../radiora/protocol/ButtonPressCommand.java | 14 ++++++- .../radiora/protocol/LEDMapFeedback.java | 3 ++ .../protocol/LocalZoneChangeFeedback.java | 29 +++++++++++++ .../radiora/protocol/RadioRACommand.java | 3 ++ .../radiora/protocol/RadioRAFeedback.java | 3 ++ .../protocol/SetDimmerLevelCommand.java | 14 ++++++- .../protocol/SetSwitchLevelCommand.java | 13 +++++- .../radiora/protocol/ZoneMapFeedback.java | 25 +++++++++++ .../protocol/ZoneMapInquiryCommand.java | 3 ++ .../resources/OH-INF/thing/thing-types.xml | 15 +++++++ 25 files changed, 278 insertions(+), 101 deletions(-) diff --git a/bundles/org.openhab.binding.lutron/README.md b/bundles/org.openhab.binding.lutron/README.md index 149f6b8b5..a0dd82321 100644 --- a/bundles/org.openhab.binding.lutron/README.md +++ b/bundles/org.openhab.binding.lutron/README.md @@ -794,7 +794,12 @@ end This binding integrates with the legacy Lutron RadioRA (Classic) lighting system. This binding depends on RS232 communication. -It has only been tested using the Chronos time module but the RS232 module should work as well. +It has only been tested using the Chronos System Bridge and Timeclock (RA-SBT-CHR) module, but Lutron's RA-RS232 or RB-RS232 module should work as well. + +Support has been added for bridged RadioRA systems. +A system is considered “bridged” when a Chronos System Bridge and Timeclock is used to integrate two RadioRA Systems in a single residence. +In a bridged system, the `system` parameter of each configured ra-dimmer, ra-switch, or ra-phantomButton thing should be set to indicate which RadioRA system it is a part of (i.e. 1 or 2). +In a non-bridged system, these parameters should be left at their default of 0. ## Supported Things @@ -808,17 +813,20 @@ This binding currently supports the following thing types: | ra-phantomButton | Thing | Phantom Button to control multiple controls (Scenes) | -## Thing Configurations +## Thing Configuration Parameters -| Thing | Config | Description | -|------------------|--------------|-----------------------------------------------------------------------| -| ra-rs232 | portName | The serial port to use to communicate with Chronos or RS232 module | -| | baud | (Optional) Baud Rate (defaults to 9600) | -| ra-dimmer | zoneNumber | Assigned Zone Number within the Lutron RadioRA system | -| | fadeOutSec | (Optional) Time in seconds dimmer should take when lowering the level | -| | fadeInSec | (Optional) Time in seconds dimmer should take when lowering the level | -| ra-switch | zoneNumber | Assigned Zone Number within the Lutron RadioRA system | -| ra-phantomButton | buttonNumber | Phantom Button Number within the Lutron RadioRA system | +| Thing | Parameter | Description | +|------------------|--------------|------------------------------------------------------------------------| +| ra-rs232 | portName | The serial port to use to communicate with Chronos or RS232 module | +| | baud | (Optional) Baud Rate (defaults to 9600) | +| ra-dimmer | zoneNumber | Assigned Zone Number within the Lutron RadioRA system | +| | system | (Optional) System number (1 or 2) in a bridged system. Default=0 (n/a) | +| | fadeOutSec | (Optional) Time in seconds dimmer should take when lowering the level | +| | fadeInSec | (Optional) Time in seconds dimmer should take when lowering the level | +| ra-switch | zoneNumber | Assigned Zone Number within the Lutron RadioRA system | +| | system | (Optional) System number (1 or 2) in a bridged system. Default=0 (n/a) | +| ra-phantomButton | buttonNumber | Phantom Button Number within the Lutron RadioRA system | +| | system | (Optional) System number (1 or 2) in a bridged system. Default=0 (n/a) | ## Channels diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232Connection.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232Connection.java index 2335ef0f9..f917aa012 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232Connection.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232Connection.java @@ -15,8 +15,11 @@ package org.openhab.binding.lutron.internal.radiora; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStream; import java.util.TooManyListenersException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lutron.internal.radiora.protocol.RadioRAFeedback; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; @@ -34,16 +37,17 @@ import org.slf4j.LoggerFactory; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class RS232Connection implements RadioRAConnection, SerialPortEventListener { private final Logger logger = LoggerFactory.getLogger(RS232Connection.class); protected SerialPortManager serialPortManager; - protected SerialPort serialPort; + protected @Nullable SerialPort serialPort; - protected BufferedReader inputReader; + protected @Nullable BufferedReader inputReader; - protected RadioRAFeedbackListener listener; + protected @Nullable RadioRAFeedbackListener listener; protected RS232MessageParser parser = new RS232MessageParser(); public RS232Connection(SerialPortManager serialPortManager) { @@ -59,7 +63,8 @@ public class RS232Connection implements RadioRAConnection, SerialPortEventListen } try { - serialPort = portIdentifier.open("openhab", 5000); + SerialPort serialPort = portIdentifier.open("openhab", 5000); + this.serialPort = serialPort; serialPort.notifyOnDataAvailable(true); serialPort.setSerialPortParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); serialPort.addEventListener(this); @@ -78,8 +83,19 @@ public class RS232Connection implements RadioRAConnection, SerialPortEventListen @Override public void write(String command) { logger.debug("Writing to serial port: {}", command.toString()); + SerialPort serialPort = this.serialPort; + try { - serialPort.getOutputStream().write(command.getBytes()); + if (serialPort != null) { + OutputStream outputStream = serialPort.getOutputStream(); + if (outputStream != null) { + outputStream.write(command.getBytes()); + } else { + logger.debug("Cannot write to serial port. outputStream is null."); + } + } else { + logger.debug("Cannot write to serial port. serialPort is null."); + } } catch (IOException e) { logger.debug("An error occurred writing to serial port", e); } @@ -87,15 +103,19 @@ public class RS232Connection implements RadioRAConnection, SerialPortEventListen @Override public void disconnect() { - serialPort.close(); + SerialPort serialPort = this.serialPort; + if (serialPort != null) { + serialPort.close(); + } } @Override public void serialEvent(SerialPortEvent ev) { switch (ev.getEventType()) { case SerialPortEvent.DATA_AVAILABLE: + BufferedReader inputReader = this.inputReader; try { - if (!inputReader.ready()) { + if (inputReader == null || !inputReader.ready()) { logger.debug("Serial Data Available but input reader not ready"); return; } @@ -106,7 +126,12 @@ public class RS232Connection implements RadioRAConnection, SerialPortEventListen if (feedback != null) { logger.debug("Msg Parsed as {}", feedback.getClass().getName()); - listener.handleRadioRAFeedback(feedback); + RadioRAFeedbackListener listener = this.listener; + if (listener != null) { + listener.handleRadioRAFeedback(feedback); + } else { + logger.debug("Cannot handle feedback message. Listener is null."); + } } logger.debug("Finished handling feedback"); } catch (IOException e) { diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232MessageParser.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232MessageParser.java index 4ee5082f7..6052f748d 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232MessageParser.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RS232MessageParser.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.lutron.internal.radiora; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lutron.internal.radiora.protocol.LEDMapFeedback; import org.openhab.binding.lutron.internal.radiora.protocol.LocalZoneChangeFeedback; import org.openhab.binding.lutron.internal.radiora.protocol.RadioRAFeedback; @@ -25,11 +27,12 @@ import org.slf4j.LoggerFactory; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class RS232MessageParser { - private Logger logger = LoggerFactory.getLogger(RS232MessageParser.class); + private final Logger logger = LoggerFactory.getLogger(RS232MessageParser.class); - public RadioRAFeedback parse(String msg) { + public @Nullable RadioRAFeedback parse(String msg) { String prefix = parsePrefix(msg); switch (prefix) { diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnection.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnection.java index 1b4cb2183..e03b6287d 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnection.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnection.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.lutron.internal.radiora; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Interface to the RadioRA Classic system * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public interface RadioRAConnection { public void open(String portName, int baud) throws RadioRAConnectionException; diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnectionException.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnectionException.java index 198f3bf0d..df9f7106d 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnectionException.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAConnectionException.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.lutron.internal.radiora; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Thrown when an attempt to open a RadioRA Connection fails. * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class RadioRAConnectionException extends Exception { private static final long serialVersionUID = 1L; diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAFeedbackListener.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAFeedbackListener.java index 9b884f45d..4e6050e6c 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAFeedbackListener.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/RadioRAFeedbackListener.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.lutron.internal.radiora; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lutron.internal.radiora.protocol.RadioRAFeedback; /** @@ -20,6 +21,7 @@ import org.openhab.binding.lutron.internal.radiora.protocol.RadioRAFeedback; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public interface RadioRAFeedbackListener { void handleRadioRAFeedback(RadioRAFeedback feedback); diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/DimmerConfig.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/DimmerConfig.java index b6344794e..08a32a39d 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/DimmerConfig.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/DimmerConfig.java @@ -21,31 +21,20 @@ import java.math.BigDecimal; * */ public class DimmerConfig { - private int zoneNumber; - private BigDecimal fadeOutSec; - private BigDecimal fadeInSec; + public int zoneNumber; + public int system = 0; + public BigDecimal fadeOutSec; + public BigDecimal fadeInSec; public int getZoneNumber() { return zoneNumber; } - public void setZoneNumber(int zoneNumber) { - this.zoneNumber = zoneNumber; - } - public BigDecimal getFadeOutSec() { return fadeOutSec; } - public void setFadeOutSec(BigDecimal fadeOutSec) { - this.fadeOutSec = fadeOutSec; - } - public BigDecimal getFadeInSec() { return fadeInSec; } - - public void setFadeInSec(BigDecimal fadeInSec) { - this.fadeInSec = fadeInSec; - } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/PhantomButtonConfig.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/PhantomButtonConfig.java index 8637c4427..2c68be75f 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/PhantomButtonConfig.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/PhantomButtonConfig.java @@ -12,32 +12,21 @@ */ package org.openhab.binding.lutron.internal.radiora.config; -import java.math.BigDecimal; +import org.eclipse.jdt.annotation.NonNullByDefault; /** * Configuration class for PhantomButton thing type. - * + * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class PhantomButtonConfig { - private int buttonNumber; - private BigDecimal fadeSec; + public int buttonNumber; + public int system = 0; public int getButtonNumber() { return buttonNumber; } - - public void setButtonNumber(int buttonNumber) { - this.buttonNumber = buttonNumber; - } - - public BigDecimal getFadeSec() { - return fadeSec; - } - - public void setFadeSec(BigDecimal fadeSec) { - this.fadeSec = fadeSec; - } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/RS232Config.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/RS232Config.java index 1b236846d..07ab05787 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/RS232Config.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/RS232Config.java @@ -12,39 +12,30 @@ */ package org.openhab.binding.lutron.internal.radiora.config; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Configuration class for RS232 thing type. * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class RS232Config { - private String portName; - private int baud = 9600; - private int zoneMapQueryInterval = 60; + public String portName = ""; + public int baud = 9600; + public int zoneMapQueryInterval = 60; public String getPortName() { return portName; } - public void setPortName(String portName) { - this.portName = portName; - } - public int getBaud() { return baud; } - public void setBaud(int baud) { - this.baud = baud; - } - public int getZoneMapQueryInterval() { return zoneMapQueryInterval; } - - public void setZoneMapQueryInterval(int zoneMapQueryInterval) { - this.zoneMapQueryInterval = zoneMapQueryInterval; - } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/SwitchConfig.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/SwitchConfig.java index ec7a5bec4..064a000de 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/SwitchConfig.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/config/SwitchConfig.java @@ -12,21 +12,21 @@ */ package org.openhab.binding.lutron.internal.radiora.config; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Configuration class for Switch thing type. - * + * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class SwitchConfig { - private int zoneNumber; + public int zoneNumber; + public int system = 0; public int getZoneNumber() { return zoneNumber; } - - public void setZoneNumber(int zoneNumber) { - this.zoneNumber = zoneNumber; - } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/DimmerHandler.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/DimmerHandler.java index 997c871eb..051ab6254 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/DimmerHandler.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/DimmerHandler.java @@ -15,6 +15,7 @@ package org.openhab.binding.lutron.internal.radiora.handler; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lutron.internal.LutronBindingConstants; import org.openhab.binding.lutron.internal.radiora.config.DimmerConfig; import org.openhab.binding.lutron.internal.radiora.protocol.LocalZoneChangeFeedback; @@ -34,6 +35,7 @@ import org.openhab.core.types.Command; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class DimmerHandler extends LutronHandler { /** @@ -41,24 +43,33 @@ public class DimmerHandler extends LutronHandler { * to external dimmer changes since RadioRA protocol does not send dimmer * levels in their messages. */ + private @NonNullByDefault({}) DimmerConfig config; private AtomicInteger lastKnownIntensity = new AtomicInteger(100); - private AtomicBoolean switchEnabled = new AtomicBoolean(false); public DimmerHandler(Thing thing) { super(thing); } + @Override + public void initialize() { + config = getConfigAs(DimmerConfig.class); + super.initialize(); + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { - DimmerConfig config = getConfigAs(DimmerConfig.class); + RS232Handler bridgeHandler = getRS232Handler(); + if (bridgeHandler == null) { + return; + } if (LutronBindingConstants.CHANNEL_LIGHTLEVEL.equals(channelUID.getId())) { if (command instanceof PercentType) { int intensity = ((PercentType) command).intValue(); - SetDimmerLevelCommand cmd = new SetDimmerLevelCommand(config.getZoneNumber(), intensity); - getRS232Handler().sendCommand(cmd); + SetDimmerLevelCommand cmd = new SetDimmerLevelCommand(config.getZoneNumber(), intensity, config.system); + bridgeHandler.sendCommand(cmd); updateInternalState(intensity); } @@ -66,11 +77,10 @@ public class DimmerHandler extends LutronHandler { if (command instanceof OnOffType) { OnOffType onOffCmd = (OnOffType) command; - SetSwitchLevelCommand cmd = new SetSwitchLevelCommand(config.getZoneNumber(), onOffCmd); - getRS232Handler().sendCommand(cmd); + SetSwitchLevelCommand cmd = new SetSwitchLevelCommand(config.getZoneNumber(), onOffCmd, config.system); + bridgeHandler.sendCommand(cmd); updateInternalState(onOffCmd); - } } } @@ -85,7 +95,10 @@ public class DimmerHandler extends LutronHandler { } private void handleZoneMapFeedback(ZoneMapFeedback feedback) { - char value = feedback.getZoneValue(getConfigAs(DimmerConfig.class).getZoneNumber()); + if (!systemsMatch(feedback.getSystem(), config.system)) { + return; + } + char value = feedback.getZoneValue(config.getZoneNumber()); if (value == '1') { turnDimmerOnToLastKnownIntensity(); } else if (value == '0') { @@ -94,7 +107,7 @@ public class DimmerHandler extends LutronHandler { } private void handleLocalZoneChangeFeedback(LocalZoneChangeFeedback feedback) { - if (feedback.getZoneNumber() == getConfigAs(DimmerConfig.class).getZoneNumber()) { + if (systemsMatch(feedback.getSystem(), config.system) && feedback.getZoneNumber() == config.getZoneNumber()) { if (LocalZoneChangeFeedback.State.ON.equals(feedback.getState())) { turnDimmerOnToLastKnownIntensity(); } else if (LocalZoneChangeFeedback.State.OFF.equals(feedback.getState())) { diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/LutronHandler.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/LutronHandler.java index b49366a5a..b0e9cd80f 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/LutronHandler.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/LutronHandler.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.lutron.internal.radiora.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lutron.internal.radiora.protocol.RadioRAFeedback; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; @@ -26,13 +28,14 @@ import org.openhab.core.thing.binding.ThingHandler; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public abstract class LutronHandler extends BaseThingHandler { public LutronHandler(Thing thing) { super(thing); } - public RS232Handler getRS232Handler() { + public @Nullable RS232Handler getRS232Handler() { Bridge bridge = getBridge(); if (bridge == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, "Unable to get bridge"); @@ -47,6 +50,13 @@ public abstract class LutronHandler extends BaseThingHandler { } } + /** + * Returns true if system numbers match, meaning that either both are 2 or both are 1 or 0 (n/a). + */ + public static boolean systemsMatch(int a, int b) { + return ((a == 2 && b == 2) || ((a == 0 || a == 1) && (b == 0 || b == 1))); + } + public abstract void handleFeedback(RadioRAFeedback feedback); @Override diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/PhantomButtonHandler.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/PhantomButtonHandler.java index 22199d8c5..6b17f9193 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/PhantomButtonHandler.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/PhantomButtonHandler.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.lutron.internal.radiora.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lutron.internal.LutronBindingConstants; import org.openhab.binding.lutron.internal.radiora.config.PhantomButtonConfig; import org.openhab.binding.lutron.internal.radiora.protocol.ButtonPressCommand; @@ -28,20 +29,31 @@ import org.openhab.core.types.Command; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class PhantomButtonHandler extends LutronHandler { + private @NonNullByDefault({}) PhantomButtonConfig config; + public PhantomButtonHandler(Thing thing) { super(thing); } + @Override + public void initialize() { + config = getConfigAs(PhantomButtonConfig.class); + super.initialize(); + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { + RS232Handler bridgeHandler = getRS232Handler(); if (channelUID.getId().equals(LutronBindingConstants.CHANNEL_SWITCH)) { if (command instanceof OnOffType) { - ButtonPressCommand cmd = new ButtonPressCommand( - getConfigAs(PhantomButtonConfig.class).getButtonNumber(), - ButtonPressCommand.ButtonState.valueOf(command.toString())); - getRS232Handler().sendCommand(cmd); + ButtonPressCommand cmd = new ButtonPressCommand(config.getButtonNumber(), + ButtonPressCommand.ButtonState.valueOf(command.toString()), config.system); + if (bridgeHandler != null) { + bridgeHandler.sendCommand(cmd); + } } } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/RS232Handler.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/RS232Handler.java index 086b5a687..66cd7b502 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/RS232Handler.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/RS232Handler.java @@ -15,6 +15,8 @@ package org.openhab.binding.lutron.internal.radiora.handler; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lutron.internal.radiora.RS232Connection; import org.openhab.binding.lutron.internal.radiora.RadioRAConnection; import org.openhab.binding.lutron.internal.radiora.RadioRAConnectionException; @@ -41,13 +43,14 @@ import org.slf4j.LoggerFactory; * * @author Jeff Lauterbach - Initial contribution */ +@NonNullByDefault public class RS232Handler extends BaseBridgeHandler implements RadioRAFeedbackListener { - private Logger logger = LoggerFactory.getLogger(RS232Handler.class); + private final Logger logger = LoggerFactory.getLogger(RS232Handler.class); private RadioRAConnection connection; - private ScheduledFuture zoneMapScheduledTask; + private @Nullable ScheduledFuture zoneMapScheduledTask; public RS232Handler(Bridge bridge, SerialPortManager serialPortManager) { super(bridge); @@ -58,6 +61,7 @@ public class RS232Handler extends BaseBridgeHandler implements RadioRAFeedbackLi @Override public void dispose() { + ScheduledFuture zoneMapScheduledTask = this.zoneMapScheduledTask; if (zoneMapScheduledTask != null) { zoneMapScheduledTask.cancel(true); } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/SwitchHandler.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/SwitchHandler.java index 1232b2ae3..aa3e27eab 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/SwitchHandler.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/handler/SwitchHandler.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.lutron.internal.radiora.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lutron.internal.LutronBindingConstants; import org.openhab.binding.lutron.internal.radiora.config.SwitchConfig; import org.openhab.binding.lutron.internal.radiora.protocol.LocalZoneChangeFeedback; @@ -31,22 +32,33 @@ import org.slf4j.LoggerFactory; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class SwitchHandler extends LutronHandler { - private Logger logger = LoggerFactory.getLogger(SwitchHandler.class); + private final Logger logger = LoggerFactory.getLogger(SwitchHandler.class); + private @NonNullByDefault({}) SwitchConfig config; public SwitchHandler(Thing thing) { super(thing); } + @Override + public void initialize() { + config = getConfigAs(SwitchConfig.class); + super.initialize(); + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { + RS232Handler bridgeHandler = getRS232Handler(); if (LutronBindingConstants.CHANNEL_SWITCH.equals(channelUID.getId())) { if (command instanceof OnOffType) { - SetSwitchLevelCommand cmd = new SetSwitchLevelCommand(getConfigAs(SwitchConfig.class).getZoneNumber(), - (OnOffType) command); + SetSwitchLevelCommand cmd = new SetSwitchLevelCommand(config.getZoneNumber(), (OnOffType) command, + config.system); - getRS232Handler().sendCommand(cmd); + if (bridgeHandler != null) { + bridgeHandler.sendCommand(cmd); + } } } } @@ -61,7 +73,10 @@ public class SwitchHandler extends LutronHandler { } private void handleZoneMapFeedback(ZoneMapFeedback feedback) { - char value = feedback.getZoneValue(getConfigAs(SwitchConfig.class).getZoneNumber()); + if (!systemsMatch(feedback.getSystem(), config.system)) { + return; + } + char value = feedback.getZoneValue(config.getZoneNumber()); if (value == '1') { updateState(LutronBindingConstants.CHANNEL_SWITCH, OnOffType.ON); @@ -71,7 +86,7 @@ public class SwitchHandler extends LutronHandler { } private void handleLocalZoneChangeFeedback(LocalZoneChangeFeedback feedback) { - if (feedback.getZoneNumber() == getConfigAs(SwitchConfig.class).getZoneNumber()) { + if (systemsMatch(feedback.getSystem(), config.system) && feedback.getZoneNumber() == config.getZoneNumber()) { if (LocalZoneChangeFeedback.State.CHG.equals(feedback.getState())) { logger.debug("Not Implemented Yet - CHG state received from Local Zone Change Feedback."); } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ButtonPressCommand.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ButtonPressCommand.java index 5d07fd639..8f5a31fc6 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ButtonPressCommand.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ButtonPressCommand.java @@ -15,6 +15,9 @@ package org.openhab.binding.lutron.internal.radiora.protocol; import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Button Press (BP) Command. * Trigger a Phantom Button Press on the RadioRA Serial Device. @@ -22,6 +25,7 @@ import java.util.List; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class ButtonPressCommand extends RadioRACommand { public enum ButtonState { @@ -32,11 +36,13 @@ public class ButtonPressCommand extends RadioRACommand { private int buttonNumber; // 1 to 15, 16 ALL ON, 17 ALL OFF private ButtonState state; // ON/OFF/TOG - private Integer fadeSec; // 0 to 240 (optional) + private @Nullable Integer fadeSec; // 0 to 240 (optional) + private int system; // 1 or 2, or 0 for none - public ButtonPressCommand(int buttonNumber, ButtonState state) { + public ButtonPressCommand(int buttonNumber, ButtonState state, int system) { this.buttonNumber = buttonNumber; this.state = state; + this.system = system; } public void setFadeSeconds(int seconds) { @@ -58,6 +64,10 @@ public class ButtonPressCommand extends RadioRACommand { args.add(String.valueOf(fadeSec)); } + if (system == 1 || system == 2) { + args.add("S" + String.valueOf(system)); + } + return args; } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LEDMapFeedback.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LEDMapFeedback.java index f25a01367..7c6ca738f 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LEDMapFeedback.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LEDMapFeedback.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.lutron.internal.radiora.protocol; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Feedback (LMP) that gives the state of all phantom LEDs *

@@ -34,6 +36,7 @@ package org.openhab.binding.lutron.internal.radiora.protocol; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class LEDMapFeedback extends RadioRAFeedback { private String bitmap; // 15 bit String of (0,1). 1 is ON, 0 is OFF diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LocalZoneChangeFeedback.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LocalZoneChangeFeedback.java index 96fff4fc6..5cd9391f2 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LocalZoneChangeFeedback.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/LocalZoneChangeFeedback.java @@ -12,6 +12,10 @@ */ package org.openhab.binding.lutron.internal.radiora.protocol; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Feedback for when a device was changed locally (not through Master Control) *

@@ -37,13 +41,22 @@ package org.openhab.binding.lutron.internal.radiora.protocol; * LZC,04,ON * * + * In a bridged system, a system parameter S1 or S2 will be appended. + * + *

+ * LZC,04,ON,S2
+ * 
+ * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class LocalZoneChangeFeedback extends RadioRAFeedback { + private final Logger logger = LoggerFactory.getLogger(LocalZoneChangeFeedback.class); private int zoneNumber; // 1 to 32 private State state; // ON, OFF, CHG + private int system; // 1 or 2, or 0 for none public enum State { ON, @@ -56,6 +69,18 @@ public class LocalZoneChangeFeedback extends RadioRAFeedback { zoneNumber = Integer.parseInt(params[1].trim()); state = State.valueOf(params[2].trim().toUpperCase()); + + system = 0; + if (params.length > 3) { + String sysParam = params[3].trim().toUpperCase(); + if ("S1".equals(sysParam)) { + system = 1; + } else if ("S2".equals(sysParam)) { + system = 2; + } else { + logger.debug("Invalid 3rd parameter {} in LZC message. Should be S1 or S2.", sysParam); + } + } } public State getState() { @@ -65,4 +90,8 @@ public class LocalZoneChangeFeedback extends RadioRAFeedback { public int getZoneNumber() { return zoneNumber; } + + public int getSystem() { + return system; + } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRACommand.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRACommand.java index 180f48f22..ba103e665 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRACommand.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRACommand.java @@ -14,12 +14,15 @@ package org.openhab.binding.lutron.internal.radiora.protocol; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Abstract base class for commands. * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public abstract class RadioRACommand { protected static final String FIELD_SEPARATOR = ","; diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRAFeedback.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRAFeedback.java index 366eddec0..5ee20a6d6 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRAFeedback.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/RadioRAFeedback.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.lutron.internal.radiora.protocol; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Base class for Feedback from RadioRA * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class RadioRAFeedback { public String[] parse(String msg, int numParams) { diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetDimmerLevelCommand.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetDimmerLevelCommand.java index ae17a3781..16f4762e8 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetDimmerLevelCommand.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetDimmerLevelCommand.java @@ -15,6 +15,9 @@ package org.openhab.binding.lutron.internal.radiora.protocol; import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Set Dimmer Level (SDL) * Set an individual Dimmer’s light level. @@ -22,15 +25,18 @@ import java.util.List; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class SetDimmerLevelCommand extends RadioRACommand { private int zoneNumber; // 1 to 32 private int dimmerLevel; // 0 to 100 - private Integer fadeSec; // 0 to 240 (optional) + private @Nullable Integer fadeSec; // 0 to 240 (optional) + private int system; // 1 or 2, or 0 for none - public SetDimmerLevelCommand(int zoneNumber, int dimmerLevel) { + public SetDimmerLevelCommand(int zoneNumber, int dimmerLevel, int system) { this.zoneNumber = zoneNumber; this.dimmerLevel = dimmerLevel; + this.system = system; } public void setFadeSeconds(int seconds) { @@ -52,6 +58,10 @@ public class SetDimmerLevelCommand extends RadioRACommand { args.add(String.valueOf(fadeSec)); } + if (system == 1 || system == 2) { + args.add("S" + String.valueOf(system)); + } + return args; } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetSwitchLevelCommand.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetSwitchLevelCommand.java index 06694a51e..d762c9b68 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetSwitchLevelCommand.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/SetSwitchLevelCommand.java @@ -15,6 +15,8 @@ package org.openhab.binding.lutron.internal.radiora.protocol; import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.types.OnOffType; /** @@ -24,15 +26,18 @@ import org.openhab.core.library.types.OnOffType; * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class SetSwitchLevelCommand extends RadioRACommand { private int zoneNumber; // 1 to 32 private OnOffType state; // ON/OFF - private Integer delaySec; // 0 to 240 (optional) + private @Nullable Integer delaySec; // 0 to 240 (optional) + private int system; // 1 or 2, or 0 for none - public SetSwitchLevelCommand(int zoneNumber, OnOffType state) { + public SetSwitchLevelCommand(int zoneNumber, OnOffType state, int system) { this.zoneNumber = zoneNumber; this.state = state; + this.system = system; } public void setDelaySeconds(int seconds) { @@ -54,6 +59,10 @@ public class SetSwitchLevelCommand extends RadioRACommand { args.add(String.valueOf(delaySec)); } + if (system == 1 || system == 2) { + args.add("S" + String.valueOf(system)); + } + return args; } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapFeedback.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapFeedback.java index 68fae75c0..6f046287f 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapFeedback.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapFeedback.java @@ -12,6 +12,10 @@ */ package org.openhab.binding.lutron.internal.radiora.protocol; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Feedback that gives the state of all zones *

@@ -26,22 +30,39 @@ package org.openhab.binding.lutron.internal.radiora.protocol; * Example: *

* Zones 2 and 9 are ON, all others are OFF, and Zones 31 and 32 are unassigned. + * In a bridged system, a system parameter S1 or S2 will be appended. * *

  * ZMP,010000001000000000000000000000XX
+ * ZMP,00100000010000000000000000000000,S2
  * 
* * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class ZoneMapFeedback extends RadioRAFeedback { + private final Logger logger = LoggerFactory.getLogger(ZoneMapFeedback.class); private String zoneStates; // 32 bit String of (0,1,X) + private int system; // 1 or 2, or 0 for none public ZoneMapFeedback(String msg) { String[] params = parse(msg, 1); zoneStates = params[1]; + + system = 0; + if (params.length > 2) { + String sysParam = params[2].trim().toUpperCase(); + if ("S1".equals(sysParam)) { + system = 1; + } else if ("S2".equals(sysParam)) { + system = 2; + } else { + logger.debug("Invalid 2nd parameter {} in ZMP message. Should be S1 or S2.", sysParam); + } + } } public String getZoneStates() { @@ -55,4 +76,8 @@ public class ZoneMapFeedback extends RadioRAFeedback { return zoneStates.charAt(zone - 1); } + + public int getSystem() { + return system; + } } diff --git a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapInquiryCommand.java b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapInquiryCommand.java index caf411b74..11e6cba15 100644 --- a/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapInquiryCommand.java +++ b/bundles/org.openhab.binding.lutron/src/main/java/org/openhab/binding/lutron/internal/radiora/protocol/ZoneMapInquiryCommand.java @@ -15,12 +15,15 @@ package org.openhab.binding.lutron.internal.radiora.protocol; import java.util.Collections; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Requests an updated ZoneMap. * * @author Jeff Lauterbach - Initial Contribution * */ +@NonNullByDefault public class ZoneMapInquiryCommand extends RadioRACommand { @Override diff --git a/bundles/org.openhab.binding.lutron/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.lutron/src/main/resources/OH-INF/thing/thing-types.xml index d9e9459ac..b3b40832e 100644 --- a/bundles/org.openhab.binding.lutron/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.lutron/src/main/resources/OH-INF/thing/thing-types.xml @@ -1203,6 +1203,11 @@ Assigned Zone Number within the Lutron RadioRA system. + + + System number (bridged systems only). Set to 1 or 2. 0 = NA (default). + 0 + Time in seconds dimmer should take when lowering the level @@ -1231,6 +1236,11 @@ Assigned Zone Number within the Lutron RadioRA system. + + + System number (bridged systems only). Set to 1 or 2. 0 = NA (default). + 0 + @@ -1250,6 +1260,11 @@ Phantom Button Number within the Lutron RadioRA system. + + + System number (bridged systems only). Set to 1 or 2. 0 = NA (default). + 0 +