]> git.basschouten.com Git - openhab-addons.git/commitdiff
If for some reason HP response fails to properly parse, i.e. `java.lang.NumberFormatE...
authorBoris Krivonog <boris.krivonog@inova.si>
Sat, 12 Nov 2022 09:44:45 +0000 (10:44 +0100)
committerGitHub <noreply@github.com>
Sat, 12 Nov 2022 09:44:45 +0000 (10:44 +0100)
Signed-off-by: Boris Krivonog <boris.krivonog@inova.si>
23 files changed:
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/HusdataHandler.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpHusdataHandler.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/IpRego6xxHeatPumpHandler.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/Rego6xxHeatPumpHandler.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialHusdataHandler.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/handler/SerialRego6xxHeatPumpHandler.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/IpRegoConnection.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/RegoConnection.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/protocol/SerialRegoConnection.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractLongResponseParser.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/AbstractResponseParser.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Checksum.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/CommandFactory.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLine.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ErrorLineResponseParser.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/Rego6xxProtocolException.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/RegoRegisterMapper.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParser.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ResponseParserFactory.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ShortResponseParser.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/StringResponseParser.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/ValueConverter.java
bundles/org.openhab.binding.regoheatpump/src/main/java/org/openhab/binding/regoheatpump/internal/rego6xx/WriteResponse.java

index 050e271db07f507f878e30252ead636d9faa5d33..e6362393de17b3b43721913a2b7f25d833e200c0 100644 (file)
@@ -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<Integer, String> 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();
index 53307ccdf1176fbcc57666f50d16ebd1b0b2a4fa..24d9dc4fcd9752a830a9e3c7c73c77195b0712ca 100644 (file)
@@ -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);
index 1a3d6da161cee2efe94f7b3531983762818a2c86..4d8bcaeaae604b14620846ccb0c34418fe4f2a85 100644 (file)
@@ -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);
index 0cc2ba760c03b1be3dedadb8f4110ecc52b9ab45..bcaf5feeb413e343e5e7c6a7123b121ba52e21a7 100644 (file)
@@ -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<String, ChannelDescriptor> 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<ErrorLine, State> 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 <T> void executeCommand(String channelIID, byte[] command, ResponseParser<T> parser,
+    private synchronized <T> void executeCommand(@Nullable String channelIID, byte[] command, ResponseParser<T> parser,
             Consumer<T> 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> T executeCommandWithRetry(String channelIID, byte[] command, ResponseParser<T> parser, int retry)
-            throws Rego6xxProtocolException, IOException, InterruptedException {
+    private <T> T executeCommandWithRetry(@Nullable String channelIID, byte[] command, ResponseParser<T> 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> T executeCommand(String channelIID, byte[] command, ResponseParser<T> parser)
+    private <T> T executeCommand(@Nullable String channelIID, byte[] command, ResponseParser<T> 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> T executeCommandInternal(String channelIID, byte[] command, ResponseParser<T> parser)
+    private <T> T executeCommandInternal(@Nullable String channelIID, byte[] command, ResponseParser<T> 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();
         }
index a37d471fe8ed7430c0de9f9f5c861ff6b778d44d..f93bca4dbfffcfe7d4aa25f222f7e3dc2d58ceee 100644 (file)
  */
 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);
     }
 }
index 02476464d2544103f8f1fe6931c3cd71e98b0891..709326cf2bb028b730b3942159a4ab6f3f2fc104 100644 (file)
  */
 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);
     }
 }
index f81295ef27860f91312f953b483d85fbb8e94b19..365858bb5eca0b9c53a199910d6910a6554e6e4e 100644 (file)
@@ -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;
     }
 }
index 6dda7a585791e52f129d5a92dcf697ff2125852b..b233855978f02961a9b0c2ddc96ca6ad27b01713 100644 (file)
@@ -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.
index 2521b681526c08e7f7da9fcdbf88e61a78c1c875..d337b2dd6af85f8287fc84dfef3f6e7194fa82a8 100644 (file)
@@ -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;
     }
 }
index 75ac37f72fd2d7809c6eaeb138cf2f42fd396353..ed16a7f7caa8563c14496ad36a6a37cea7db3b02 100644 (file)
  */
 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<T> extends AbstractResponseParser<T> {
     @Override
     public int responseLength() {
index e8340fd29e40686a65de6422c6c60d471a67ec56..a118281c423f8f63987353e167acf5e01a7b7e1c 100644 (file)
@@ -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<T> implements ResponseParser<T> {
     private static final byte COMPUTER_ADDRESS = (byte) 0x01;
 
index b95f05f8f0b5093692c30fcd8219edffd1b035eb..18c238d64401a2d385b047c424a48d8951343545 100644 (file)
@@ -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);
index c3333b58600f29ef220e74144c61fe358e964bcb..27c6ce06798df156772a81b08870afc4af84a914 100644 (file)
  */
 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;
 
index ca7941b35964c4ab68c74a6ab47b6f9bab693dcd..3097515c2599ac7bf0c1cdb9c4b3fbe106476939 100644 (file)
@@ -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;
index cc693124d77c2706d76eda3c73ae881054037144..00ecfe793402261ea63431af4de0305b32feb9e3 100644 (file)
  */
 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<ErrorLine> {
 
     @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),
index b1646b15680f7fe3ea95e3d0bad71df1f636327f..5204316da865d560760c38cfe6e47c4c091027ab 100644 (file)
  */
 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;
index 96af4081d933045facd6906ea0c232c753de3fb9..c5276e3789420bc21e067a5ceae1e7e2fe7d0209 100644 (file)
@@ -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);
     }
 
index 42371597c73823f2303b03df79a3d2c64addafaf..4619354d03ce75b1999b8b1269e86507619db4a0 100644 (file)
  */
 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<T> {
     public int responseLength();
 
index 2b03c84a36f46bf11025ea78162888e55a87fc24..31b7016e5914eaa279e3f155b7f01b2ab7e346d2 100644 (file)
  */
 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> SHORT = new ShortResponseParser();
     public static final ResponseParser<String> STRING = new StringResponseParser();
     public static final ResponseParser<ErrorLine> ERROR_LINE = new ErrorLineResponseParser();
-    public static final ResponseParser<Void> WRITE = new WriteResponse();
+    public static final ResponseParser<String> WRITE = new WriteResponse();
 }
index e9216eb811b5b428ea359388aa7d66dcc8c0dcfd..8ebc0c6ab4689926e8cfd8d436fd25652cf736a6 100644 (file)
  */
 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<Short> {
 
     @Override
index 5f86aa7ad55f7a8f9ab9acfc9432d924c649d60d..ab2cee10f0e4a530da5f6e827212ad27330c9720 100644 (file)
  */
 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<String> {
 
     @Override
index e9b9658a75757099add23193b84804e49ef527a2..1670b48884f26331f7a983d9c7aa2c7b1c75ad1f 100644 (file)
  */
 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);
index 8359087baf9873412e309aceec78faa14e4ba739..23cfe77819313b7d146ea7b4b7210ed53034ef94 100644 (file)
  */
 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<Void> {
+@NonNullByDefault
+class WriteResponse extends AbstractResponseParser<String> {
     @Override
     public int responseLength() {
         return 1;
     }
 
     @Override
-    protected Void convert(byte[] responseBytes) {
-        return null;
+    protected String convert(byte[] responseBytes) {
+        return "";
     }
 }