From 248ca1830a2d5a3a0c6b965dcb95c14cd9863dcf Mon Sep 17 00:00:00 2001 From: Boris Krivonog Date: Sat, 12 Nov 2022 10:44:45 +0100 Subject: [PATCH] If for some reason HP response fails to properly parse, i.e. `java.lang.NumberFormatException: For input string: "##"` exception is not handled and scheduler is not re-triggered (polling stops).Fixed build warnings. (#13685) Signed-off-by: Boris Krivonog --- .../internal/handler/HusdataHandler.java | 28 ++++++-- .../internal/handler/IpHusdataHandler.java | 2 + .../handler/IpRego6xxHeatPumpHandler.java | 2 + .../handler/Rego6xxHeatPumpHandler.java | 66 ++++++++++++------- .../handler/SerialHusdataHandler.java | 13 +++- .../handler/SerialRego6xxHeatPumpHandler.java | 13 +++- .../internal/protocol/IpRegoConnection.java | 24 +++++-- .../internal/protocol/RegoConnection.java | 3 + .../protocol/SerialRegoConnection.java | 31 +++++++-- .../rego6xx/AbstractLongResponseParser.java | 3 + .../rego6xx/AbstractResponseParser.java | 2 + .../internal/rego6xx/Checksum.java | 3 + .../internal/rego6xx/CommandFactory.java | 3 + .../internal/rego6xx/ErrorLine.java | 5 ++ .../rego6xx/ErrorLineResponseParser.java | 5 +- .../rego6xx/Rego6xxProtocolException.java | 3 + .../internal/rego6xx/RegoRegisterMapper.java | 13 ++-- .../internal/rego6xx/ResponseParser.java | 3 + .../rego6xx/ResponseParserFactory.java | 5 +- .../internal/rego6xx/ShortResponseParser.java | 3 + .../rego6xx/StringResponseParser.java | 3 + .../internal/rego6xx/ValueConverter.java | 3 + .../internal/rego6xx/WriteResponse.java | 9 ++- 23 files changed, 191 insertions(+), 54 deletions(-) diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/HusdataHandler.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/HusdataHandler.java index 050e271db..e6362393d 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/HusdataHandler.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/HusdataHandler.java @@ -26,6 +26,8 @@ import java.util.Map; 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.regoheatpump.internal.protocol.RegoConnection; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; @@ -34,6 +36,7 @@ import org.openhab.core.library.unit.Units; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.types.Command; import org.slf4j.Logger; @@ -45,13 +48,14 @@ import org.slf4j.LoggerFactory; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault abstract class HusdataHandler extends BaseThingHandler { private static final Map MAPPINGS; private final Logger logger = LoggerFactory.getLogger(HusdataHandler.class); - private RegoConnection connection; - private ScheduledFuture scheduledRefreshFuture; - private BufferedReader bufferedReader; + private @Nullable RegoConnection connection; + private @Nullable ScheduledFuture scheduledRefreshFuture; + private @Nullable BufferedReader bufferedReader; static { MAPPINGS = mappings(); @@ -61,12 +65,18 @@ abstract class HusdataHandler extends BaseThingHandler { super(thing); } - protected abstract RegoConnection createConnection(); + protected abstract RegoConnection createConnection() throws IOException; @Override public void initialize() { bufferedReader = null; - connection = createConnection(); + + try { + connection = createConnection(); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + return; + } updateStatus(ThingStatus.UNKNOWN); @@ -78,14 +88,16 @@ abstract class HusdataHandler extends BaseThingHandler { public void dispose() { super.dispose(); + ScheduledFuture scheduledRefreshFuture = this.scheduledRefreshFuture; + this.scheduledRefreshFuture = null; if (scheduledRefreshFuture != null) { scheduledRefreshFuture.cancel(true); - scheduledRefreshFuture = null; } + RegoConnection connection = this.connection; + this.connection = null; if (connection != null) { connection.close(); - connection = null; } } @@ -112,8 +124,10 @@ abstract class HusdataHandler extends BaseThingHandler { outputStream.flush(); } + BufferedReader bufferedReader = this.bufferedReader; if (bufferedReader == null) { bufferedReader = new BufferedReader(new InputStreamReader(connection.inputStream())); + this.bufferedReader = bufferedReader; } final String line = bufferedReader.readLine(); diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpHusdataHandler.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpHusdataHandler.java index 53307ccdf..24d9dc4fc 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpHusdataHandler.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpHusdataHandler.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.regoheatpump.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.regoheatpump.internal.RegoHeatPumpBindingConstants; import org.openhab.binding.regoheatpump.internal.protocol.IpRegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; @@ -23,6 +24,7 @@ import org.openhab.core.thing.Thing; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class IpHusdataHandler extends HusdataHandler { public IpHusdataHandler(Thing thing) { super(thing); diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpRego6xxHeatPumpHandler.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpRego6xxHeatPumpHandler.java index 1a3d6da16..4d8bcaeaa 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpRego6xxHeatPumpHandler.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpRego6xxHeatPumpHandler.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.regoheatpump.internal.handler; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.regoheatpump.internal.RegoHeatPumpBindingConstants; import org.openhab.binding.regoheatpump.internal.protocol.IpRegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; @@ -23,6 +24,7 @@ import org.openhab.core.thing.Thing; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class IpRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler { public IpRego6xxHeatPumpHandler(Thing thing) { super(thing); diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/Rego6xxHeatPumpHandler.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/Rego6xxHeatPumpHandler.java index 0cc2ba760..bcaf5feeb 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/Rego6xxHeatPumpHandler.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/Rego6xxHeatPumpHandler.java @@ -29,6 +29,8 @@ import java.util.stream.Collectors; import javax.measure.Unit; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; import org.openhab.binding.regoheatpump.internal.rego6xx.CommandFactory; import org.openhab.binding.regoheatpump.internal.rego6xx.ErrorLine; @@ -60,13 +62,15 @@ import org.slf4j.LoggerFactory; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { private static final class ChannelDescriptor { - private Date lastUpdate; - private byte[] cachedValue; + private @Nullable Date lastUpdate; + private byte @Nullable [] cachedValue; - public byte[] cachedValueIfNotExpired(int refreshTime) { + public byte @Nullable [] cachedValueIfNotExpired(int refreshTime) { + Date lastUpdate = this.lastUpdate; if (lastUpdate == null || (lastUpdate.getTime() + refreshTime * 900 < new Date().getTime())) { return null; } @@ -82,23 +86,27 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(Rego6xxHeatPumpHandler.class); private final Map channelDescriptors = new HashMap<>(); + private final RegoRegisterMapper mapper = RegoRegisterMapper.REGO600; + private @Nullable RegoConnection connection; + private @Nullable ScheduledFuture scheduledRefreshFuture; private int refreshInterval; - private RegoConnection connection; - private RegoRegisterMapper mapper; - private ScheduledFuture scheduledRefreshFuture; protected Rego6xxHeatPumpHandler(Thing thing) { super(thing); } - protected abstract RegoConnection createConnection(); + protected abstract RegoConnection createConnection() throws IOException; @Override public void initialize() { - mapper = RegoRegisterMapper.REGO600; refreshInterval = ((Number) getConfig().get(REFRESH_INTERVAL)).intValue(); - connection = createConnection(); + try { + connection = createConnection(); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + return; + } scheduledRefreshFuture = scheduler.scheduleWithFixedDelay(this::refresh, 2, refreshInterval, TimeUnit.SECONDS); @@ -109,21 +117,21 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { public void dispose() { super.dispose(); + RegoConnection connection = this.connection; + this.connection = null; if (connection != null) { connection.close(); } + ScheduledFuture scheduledRefreshFuture = this.scheduledRefreshFuture; + this.scheduledRefreshFuture = null; if (scheduledRefreshFuture != null) { scheduledRefreshFuture.cancel(true); - scheduledRefreshFuture = null; } synchronized (channelDescriptors) { channelDescriptors.clear(); } - - connection = null; - mapper = null; } @Override @@ -220,7 +228,7 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { private void readAndUpdateLastError(String channelIID, Function converter) { executeCommandAndUpdateState(channelIID, CommandFactory.createReadLastErrorCommand(), ResponseParserFactory.ERROR_LINE, e -> { - return e == null ? UnDefType.NULL : converter.apply(e); + return e == ErrorLine.NO_ERROR ? UnDefType.NULL : converter.apply(e); }); } @@ -252,7 +260,7 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { }); } - private synchronized void executeCommand(String channelIID, byte[] command, ResponseParser parser, + private synchronized void executeCommand(@Nullable String channelIID, byte[] command, ResponseParser parser, Consumer resultProcessor) { try { T result = executeCommandWithRetry(channelIID, command, parser, 5); @@ -265,17 +273,19 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { } updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); - } catch (Rego6xxProtocolException e) { + } catch (Rego6xxProtocolException | RuntimeException e) { logger.warn("Executing command for channel '{}' failed.", channelIID, e); - updateState(channelIID, UnDefType.UNDEF); + if (channelIID != null) { + updateState(channelIID, UnDefType.UNDEF); + } } catch (InterruptedException e) { logger.debug("Execution interrupted when accessing value for channel '{}'.", channelIID, e); Thread.currentThread().interrupt(); } } - private T executeCommandWithRetry(String channelIID, byte[] command, ResponseParser parser, int retry) - throws Rego6xxProtocolException, IOException, InterruptedException { + private T executeCommandWithRetry(@Nullable String channelIID, byte[] command, ResponseParser parser, + int retry) throws Rego6xxProtocolException, IOException, InterruptedException { try { checkRegoDevice(); return executeCommand(channelIID, command, parser); @@ -316,11 +326,12 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { } } - private T executeCommand(String channelIID, byte[] command, ResponseParser parser) + private T executeCommand(@Nullable String channelIID, byte[] command, ResponseParser parser) throws Rego6xxProtocolException, IOException, InterruptedException { try { return executeCommandInternal(channelIID, command, parser); } catch (IOException e) { + RegoConnection connection = this.connection; if (connection != null) { connection.close(); } @@ -329,7 +340,7 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { } } - private T executeCommandInternal(String channelIID, byte[] command, ResponseParser parser) + private T executeCommandInternal(@Nullable String channelIID, byte[] command, ResponseParser parser) throws Rego6xxProtocolException, IOException, InterruptedException { // CHANNEL_LAST_ERROR_CODE and CHANNEL_LAST_ERROR_TIMESTAMP are read from same // register. To prevent accessing same register twice when both channels are linked, @@ -338,8 +349,12 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { || CHANNEL_LAST_ERROR_TIMESTAMP.equals(channelIID)) ? CHANNEL_LAST_ERROR : channelIID; // Use transient channel descriptor for null (not cached) channels. - ChannelDescriptor descriptor = channelIID == null ? new ChannelDescriptor() - : channelDescriptorForChannel(mappedChannelIID); + ChannelDescriptor descriptor; + if (mappedChannelIID == null) { + descriptor = new ChannelDescriptor(); + } else { + descriptor = channelDescriptorForChannel(mappedChannelIID); + } byte[] cachedValue = descriptor.cachedValueIfNotExpired(refreshInterval); if (cachedValue != null) { @@ -348,6 +363,11 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { } // Send command to device and wait for response. + RegoConnection connection = this.connection; + if (connection == null) { + throw new IOException("Unable to execute command - no connection available"); + + } if (!connection.isConnected()) { connection.connect(); } diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialHusdataHandler.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialHusdataHandler.java index a37d471fe..f93bca4db 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialHusdataHandler.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialHusdataHandler.java @@ -12,6 +12,10 @@ */ package org.openhab.binding.regoheatpump.internal.handler; +import java.io.IOException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.regoheatpump.internal.RegoHeatPumpBindingConstants; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.SerialRegoConnection; @@ -27,9 +31,10 @@ import org.openhab.core.thing.ThingStatusDetail; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class SerialHusdataHandler extends HusdataHandler { private final SerialPortManager serialPortManager; - private SerialPortIdentifier serialPortIdentifier; + private @Nullable SerialPortIdentifier serialPortIdentifier; public SerialHusdataHandler(Thing thing, SerialPortManager serialPortManager) { super(thing); @@ -49,7 +54,11 @@ public class SerialHusdataHandler extends HusdataHandler { } @Override - protected RegoConnection createConnection() { + protected RegoConnection createConnection() throws IOException { + SerialPortIdentifier serialPortIdentifier = this.serialPortIdentifier; + if (serialPortIdentifier == null) { + throw new IOException("Serial port does not exist"); + } return new SerialRegoConnection(serialPortIdentifier, 19200); } } diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialRego6xxHeatPumpHandler.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialRego6xxHeatPumpHandler.java index 02476464d..709326cf2 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialRego6xxHeatPumpHandler.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialRego6xxHeatPumpHandler.java @@ -12,6 +12,10 @@ */ package org.openhab.binding.regoheatpump.internal.handler; +import java.io.IOException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.regoheatpump.internal.RegoHeatPumpBindingConstants; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.SerialRegoConnection; @@ -27,9 +31,10 @@ import org.openhab.core.thing.ThingStatusDetail; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class SerialRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler { private final SerialPortManager serialPortManager; - private SerialPortIdentifier serialPortIdentifier; + private @Nullable SerialPortIdentifier serialPortIdentifier; public SerialRego6xxHeatPumpHandler(Thing thing, SerialPortManager serialPortManager) { super(thing); @@ -49,7 +54,11 @@ public class SerialRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler { } @Override - protected RegoConnection createConnection() { + protected RegoConnection createConnection() throws IOException { + SerialPortIdentifier serialPortIdentifier = this.serialPortIdentifier; + if (serialPortIdentifier == null) { + throw new IOException("Serial port does not exist"); + } return new SerialRegoConnection(serialPortIdentifier, 19200); } } diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/IpRegoConnection.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/IpRegoConnection.java index f81295ef2..365858bb5 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/IpRegoConnection.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/IpRegoConnection.java @@ -18,6 +18,8 @@ import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +28,7 @@ import org.slf4j.LoggerFactory; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class IpRegoConnection implements RegoConnection { /** * Connection timeout in milliseconds @@ -40,7 +43,7 @@ public class IpRegoConnection implements RegoConnection { private final Logger logger = LoggerFactory.getLogger(IpRegoConnection.class); private final String address; private final int port; - private Socket clientSocket; + private @Nullable Socket clientSocket; public IpRegoConnection(String address, int port) { this.address = address; @@ -50,10 +53,12 @@ public class IpRegoConnection implements RegoConnection { @Override public void connect() throws IOException { logger.debug("Connecting to '{}', port = {}.", address, port); + Socket clientSocket = this.clientSocket; if (clientSocket == null) { clientSocket = new Socket(); clientSocket.setSoTimeout(SOCKET_READ_TIMEOUT); clientSocket.setKeepAlive(true); + this.clientSocket = clientSocket; } clientSocket.connect(new InetSocketAddress(address, port), CONNECTION_TIMEOUT); logger.debug("Connected to '{}', port = {}.", address, port); @@ -61,12 +66,15 @@ public class IpRegoConnection implements RegoConnection { @Override public boolean isConnected() { + Socket clientSocket = this.clientSocket; return clientSocket != null && clientSocket.isConnected(); } @Override public void close() { try { + Socket clientSocket = this.clientSocket; + this.clientSocket = null; if (clientSocket != null) { clientSocket.close(); } @@ -74,17 +82,23 @@ public class IpRegoConnection implements RegoConnection { // There is really not much we can do here, ignore the error and continue execution. logger.warn("Closing socket failed", e); } - - clientSocket = null; } @Override public OutputStream outputStream() throws IOException { - return clientSocket.getOutputStream(); + return getClientSocket().getOutputStream(); } @Override public InputStream inputStream() throws IOException { - return clientSocket.getInputStream(); + return getClientSocket().getInputStream(); + } + + private Socket getClientSocket() throws IOException { + Socket clientSocket = this.clientSocket; + if (clientSocket == null) { + throw new IOException("Socket closed"); + } + return clientSocket; } } diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/RegoConnection.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/RegoConnection.java index 6dda7a585..b23385597 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/RegoConnection.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/RegoConnection.java @@ -16,11 +16,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link RegoConnection} is responsible for creating connections to clients. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public interface RegoConnection { /** * Connect to the receiver. Return true if the connection has succeeded or if already connected. diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/SerialRegoConnection.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/SerialRegoConnection.java index 2521b6815..d337b2dd6 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/SerialRegoConnection.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/SerialRegoConnection.java @@ -16,6 +16,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPortIdentifier; @@ -26,10 +28,11 @@ import org.openhab.core.io.transport.serial.UnsupportedCommOperationException; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class SerialRegoConnection implements RegoConnection { private final int baudRate; private final String portName; - private SerialPort serialPort; + private @Nullable SerialPort serialPort; private final SerialPortIdentifier serialPortIdentifier; public SerialRegoConnection(SerialPortIdentifier serialPortIdentifier, int baudRate) { @@ -41,10 +44,11 @@ public class SerialRegoConnection implements RegoConnection { @Override public void connect() throws IOException { try { - serialPort = serialPortIdentifier.open(SerialRegoConnection.class.getCanonicalName(), 2000); + SerialPort serialPort = serialPortIdentifier.open(SerialRegoConnection.class.getCanonicalName(), 2000); serialPort.enableReceiveTimeout(100); serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); + this.serialPort = serialPort; } catch (PortInUseException e) { throw new IOException("Serial port already used: " + portName, e); } catch (UnsupportedCommOperationException e) { @@ -59,19 +63,36 @@ public class SerialRegoConnection implements RegoConnection { @Override public void close() { + SerialPort serialPort = this.serialPort; + this.serialPort = null; if (serialPort != null) { serialPort.close(); - serialPort = null; } } @Override public OutputStream outputStream() throws IOException { - return serialPort.getOutputStream(); + OutputStream outputStream = getSerialPort().getOutputStream(); + if (outputStream == null) { + throw new IOException("Sending data is not supported"); + } + return outputStream; } @Override public InputStream inputStream() throws IOException { - return serialPort.getInputStream(); + InputStream inputStream = getSerialPort().getInputStream(); + if (inputStream == null) { + throw new IOException("Receiving data is not supported"); + } + return inputStream; + } + + private SerialPort getSerialPort() throws IOException { + SerialPort serialPort = this.serialPort; + if (serialPort == null) { + throw new IOException("Connection closed"); + } + return serialPort; } } diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractLongResponseParser.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractLongResponseParser.java index 75ac37f72..ed16a7f7c 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractLongResponseParser.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractLongResponseParser.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link AbstractLongResponseParser} is responsible for parsing long form responses. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault abstract class AbstractLongResponseParser extends AbstractResponseParser { @Override public int responseLength() { diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractResponseParser.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractResponseParser.java index e8340fd29..a118281c4 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractResponseParser.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractResponseParser.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.util.HexUtils; /** @@ -20,6 +21,7 @@ import org.openhab.core.util.HexUtils; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault abstract class AbstractResponseParser implements ResponseParser { private static final byte COMPUTER_ADDRESS = (byte) 0x01; diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Checksum.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Checksum.java index b95f05f8f..18c238d64 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Checksum.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Checksum.java @@ -14,11 +14,14 @@ package org.openhab.binding.regoheatpump.internal.rego6xx; import java.util.Arrays; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link Checksum} is responsible for calculating checksum of given data. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault class Checksum { static byte calculate(byte[]... lists) { return Arrays.stream(lists).reduce((byte) 0, Checksum::calculate, (a, b) -> b); diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/CommandFactory.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/CommandFactory.java index c3333b586..27c6ce067 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/CommandFactory.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/CommandFactory.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link CommandFactory} is responsible for creating different commands that can * be send to a rego 6xx unit. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class CommandFactory { private static final byte DEVICE_ADDRESS = (byte) 0x81; diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLine.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLine.java index ca7941b35..3097515c2 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLine.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLine.java @@ -15,15 +15,20 @@ package org.openhab.binding.regoheatpump.internal.rego6xx; import java.time.ZoneId; import java.time.ZonedDateTime; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link ErrorLine} is responsible for holding information about a single error line. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class ErrorLine { private final byte error; private final String timestamp; + public static final ErrorLine NO_ERROR = new ErrorLine((byte) 0, ""); + public ErrorLine(byte error, String timestamp) { this.error = error; this.timestamp = timestamp; diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLineResponseParser.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLineResponseParser.java index cc693124d..00ecfe793 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLineResponseParser.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLineResponseParser.java @@ -12,18 +12,21 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link ErrorLineResponseParser} is responsible for parsing error information (log) entry. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault class ErrorLineResponseParser extends AbstractLongResponseParser { @Override protected ErrorLine convert(byte[] responseBytes) { // 255 marks no error. if (responseBytes[1] == (byte) 255) { - return null; + return ErrorLine.NO_ERROR; } return new ErrorLine(ValueConverter.arrayToByte(responseBytes, 1), diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Rego6xxProtocolException.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Rego6xxProtocolException.java index b1646b156..5204316da 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Rego6xxProtocolException.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Rego6xxProtocolException.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link Rego6xxProtocolException} is responsible for holding information about an Rego6xx protocol error. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class Rego6xxProtocolException extends Exception { private static final long serialVersionUID = 7556083982084149686L; diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/RegoRegisterMapper.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/RegoRegisterMapper.java index 96af4081d..c5276e378 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/RegoRegisterMapper.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/RegoRegisterMapper.java @@ -19,6 +19,8 @@ import java.util.Map; import javax.measure.Unit; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; @@ -27,6 +29,7 @@ import org.openhab.core.library.unit.Units; * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class RegoRegisterMapper { public static final RegoRegisterMapper REGO600; @@ -35,7 +38,7 @@ public class RegoRegisterMapper { public double scaleFactor(); - public Unit unit(); + public @Nullable Unit unit(); public int convertValue(short value); } @@ -44,9 +47,9 @@ public class RegoRegisterMapper { private static class ChannelImpl implements Channel { private final short address; private final double scaleFactor; - private final Unit unit; + private @Nullable final Unit unit; - private ChannelImpl(short address, double scaleFactor, Unit unit) { + private ChannelImpl(short address, double scaleFactor, @Nullable Unit unit) { this.address = address; this.scaleFactor = scaleFactor; this.unit = unit; @@ -63,7 +66,7 @@ public class RegoRegisterMapper { } @Override - public Unit unit() { + public @Nullable Unit unit() { return unit; } @@ -104,7 +107,7 @@ public class RegoRegisterMapper { this.mappings = mappings; } - public Channel map(String channelIID) { + public @Nullable Channel map(String channelIID) { return mappings.get(channelIID); } diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParser.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParser.java index 42371597c..4619354d0 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParser.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParser.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link ResponseParser} is responsible for parsing arbitrary data coming from a rego 6xx unit. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public interface ResponseParser { public int responseLength(); diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParserFactory.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParserFactory.java index 2b03c84a3..31b7016e5 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParserFactory.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParserFactory.java @@ -12,15 +12,18 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link ResponseParserFactory} is responsible for providing parsers for all known data * forms coming from the rego 6xx unit. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault public class ResponseParserFactory { public static final ResponseParser SHORT = new ShortResponseParser(); public static final ResponseParser STRING = new StringResponseParser(); public static final ResponseParser ERROR_LINE = new ErrorLineResponseParser(); - public static final ResponseParser WRITE = new WriteResponse(); + public static final ResponseParser WRITE = new WriteResponse(); } diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ShortResponseParser.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ShortResponseParser.java index e9216eb81..8ebc0c6ab 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ShortResponseParser.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ShortResponseParser.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link ShortResponseParser} is responsible for parsing short form data format * coming from the rego 6xx unit. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault class ShortResponseParser extends AbstractResponseParser { @Override diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/StringResponseParser.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/StringResponseParser.java index 5f86aa7ad..ab2cee10f 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/StringResponseParser.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/StringResponseParser.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link StringResponseParser} is responsible for parsing long (text) form data format * coming from the rego 6xx unit. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault class StringResponseParser extends AbstractLongResponseParser { @Override diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ValueConverter.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ValueConverter.java index e9b9658a7..1670b4888 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ValueConverter.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ValueConverter.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link ValueConverter} is responsible for converting various rego 6xx specific data types. * * @author Boris Krivonog - Initial contribution */ +@NonNullByDefault class ValueConverter { public static byte[] shortToSevenBitFormat(short value) { byte b1 = (byte) ((value & 0xC000) >> 14); diff --git a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/WriteResponse.java b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/WriteResponse.java index 8359087ba..23cfe7781 100644 --- a/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/WriteResponse.java +++ b/bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/WriteResponse.java @@ -12,20 +12,23 @@ */ package org.openhab.binding.regoheatpump.internal.rego6xx; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link WriteResponse} is responsible for parsing write responses * coming from the rego 6xx unit. * * @author Boris Krivonog - Initial contribution */ -class WriteResponse extends AbstractResponseParser { +@NonNullByDefault +class WriteResponse extends AbstractResponseParser { @Override public int responseLength() { return 1; } @Override - protected Void convert(byte[] responseBytes) { - return null; + protected String convert(byte[] responseBytes) { + return ""; } }