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 <boris.krivonog@inova.si>
This commit is contained in:
Boris Krivonog 2022-11-12 10:44:45 +01:00 committed by GitHub
parent fe269b127a
commit 248ca1830a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 191 additions and 54 deletions

View File

@ -26,6 +26,8 @@ import java.util.Map;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; 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.binding.regoheatpump.internal.protocol.RegoConnection;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType; 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.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -45,13 +48,14 @@ import org.slf4j.LoggerFactory;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
abstract class HusdataHandler extends BaseThingHandler { abstract class HusdataHandler extends BaseThingHandler {
private static final Map<Integer, String> MAPPINGS; private static final Map<Integer, String> MAPPINGS;
private final Logger logger = LoggerFactory.getLogger(HusdataHandler.class); private final Logger logger = LoggerFactory.getLogger(HusdataHandler.class);
private RegoConnection connection; private @Nullable RegoConnection connection;
private ScheduledFuture<?> scheduledRefreshFuture; private @Nullable ScheduledFuture<?> scheduledRefreshFuture;
private BufferedReader bufferedReader; private @Nullable BufferedReader bufferedReader;
static { static {
MAPPINGS = mappings(); MAPPINGS = mappings();
@ -61,12 +65,18 @@ abstract class HusdataHandler extends BaseThingHandler {
super(thing); super(thing);
} }
protected abstract RegoConnection createConnection(); protected abstract RegoConnection createConnection() throws IOException;
@Override @Override
public void initialize() { public void initialize() {
bufferedReader = null; bufferedReader = null;
try {
connection = createConnection(); connection = createConnection();
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
return;
}
updateStatus(ThingStatus.UNKNOWN); updateStatus(ThingStatus.UNKNOWN);
@ -78,14 +88,16 @@ abstract class HusdataHandler extends BaseThingHandler {
public void dispose() { public void dispose() {
super.dispose(); super.dispose();
ScheduledFuture<?> scheduledRefreshFuture = this.scheduledRefreshFuture;
this.scheduledRefreshFuture = null;
if (scheduledRefreshFuture != null) { if (scheduledRefreshFuture != null) {
scheduledRefreshFuture.cancel(true); scheduledRefreshFuture.cancel(true);
scheduledRefreshFuture = null;
} }
RegoConnection connection = this.connection;
this.connection = null;
if (connection != null) { if (connection != null) {
connection.close(); connection.close();
connection = null;
} }
} }
@ -112,8 +124,10 @@ abstract class HusdataHandler extends BaseThingHandler {
outputStream.flush(); outputStream.flush();
} }
BufferedReader bufferedReader = this.bufferedReader;
if (bufferedReader == null) { if (bufferedReader == null) {
bufferedReader = new BufferedReader(new InputStreamReader(connection.inputStream())); bufferedReader = new BufferedReader(new InputStreamReader(connection.inputStream()));
this.bufferedReader = bufferedReader;
} }
final String line = bufferedReader.readLine(); final String line = bufferedReader.readLine();

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.regoheatpump.internal.handler; 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.RegoHeatPumpBindingConstants;
import org.openhab.binding.regoheatpump.internal.protocol.IpRegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.IpRegoConnection;
import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection;
@ -23,6 +24,7 @@ import org.openhab.core.thing.Thing;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class IpHusdataHandler extends HusdataHandler { public class IpHusdataHandler extends HusdataHandler {
public IpHusdataHandler(Thing thing) { public IpHusdataHandler(Thing thing) {
super(thing); super(thing);

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.regoheatpump.internal.handler; 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.RegoHeatPumpBindingConstants;
import org.openhab.binding.regoheatpump.internal.protocol.IpRegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.IpRegoConnection;
import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection;
@ -23,6 +24,7 @@ import org.openhab.core.thing.Thing;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class IpRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler { public class IpRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler {
public IpRego6xxHeatPumpHandler(Thing thing) { public IpRego6xxHeatPumpHandler(Thing thing) {
super(thing); super(thing);

View File

@ -29,6 +29,8 @@ import java.util.stream.Collectors;
import javax.measure.Unit; 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.protocol.RegoConnection;
import org.openhab.binding.regoheatpump.internal.rego6xx.CommandFactory; import org.openhab.binding.regoheatpump.internal.rego6xx.CommandFactory;
import org.openhab.binding.regoheatpump.internal.rego6xx.ErrorLine; import org.openhab.binding.regoheatpump.internal.rego6xx.ErrorLine;
@ -60,13 +62,15 @@ import org.slf4j.LoggerFactory;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
abstract class Rego6xxHeatPumpHandler extends BaseThingHandler { abstract class Rego6xxHeatPumpHandler extends BaseThingHandler {
private static final class ChannelDescriptor { private static final class ChannelDescriptor {
private Date lastUpdate; private @Nullable Date lastUpdate;
private byte[] cachedValue; 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())) { if (lastUpdate == null || (lastUpdate.getTime() + refreshTime * 900 < new Date().getTime())) {
return null; return null;
} }
@ -82,23 +86,27 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(Rego6xxHeatPumpHandler.class); private final Logger logger = LoggerFactory.getLogger(Rego6xxHeatPumpHandler.class);
private final Map<String, ChannelDescriptor> channelDescriptors = new HashMap<>(); 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 int refreshInterval;
private RegoConnection connection;
private RegoRegisterMapper mapper;
private ScheduledFuture<?> scheduledRefreshFuture;
protected Rego6xxHeatPumpHandler(Thing thing) { protected Rego6xxHeatPumpHandler(Thing thing) {
super(thing); super(thing);
} }
protected abstract RegoConnection createConnection(); protected abstract RegoConnection createConnection() throws IOException;
@Override @Override
public void initialize() { public void initialize() {
mapper = RegoRegisterMapper.REGO600;
refreshInterval = ((Number) getConfig().get(REFRESH_INTERVAL)).intValue(); refreshInterval = ((Number) getConfig().get(REFRESH_INTERVAL)).intValue();
try {
connection = createConnection(); connection = createConnection();
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
return;
}
scheduledRefreshFuture = scheduler.scheduleWithFixedDelay(this::refresh, 2, refreshInterval, TimeUnit.SECONDS); scheduledRefreshFuture = scheduler.scheduleWithFixedDelay(this::refresh, 2, refreshInterval, TimeUnit.SECONDS);
@ -109,21 +117,21 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler {
public void dispose() { public void dispose() {
super.dispose(); super.dispose();
RegoConnection connection = this.connection;
this.connection = null;
if (connection != null) { if (connection != null) {
connection.close(); connection.close();
} }
ScheduledFuture<?> scheduledRefreshFuture = this.scheduledRefreshFuture;
this.scheduledRefreshFuture = null;
if (scheduledRefreshFuture != null) { if (scheduledRefreshFuture != null) {
scheduledRefreshFuture.cancel(true); scheduledRefreshFuture.cancel(true);
scheduledRefreshFuture = null;
} }
synchronized (channelDescriptors) { synchronized (channelDescriptors) {
channelDescriptors.clear(); channelDescriptors.clear();
} }
connection = null;
mapper = null;
} }
@Override @Override
@ -220,7 +228,7 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler {
private void readAndUpdateLastError(String channelIID, Function<ErrorLine, State> converter) { private void readAndUpdateLastError(String channelIID, Function<ErrorLine, State> converter) {
executeCommandAndUpdateState(channelIID, CommandFactory.createReadLastErrorCommand(), executeCommandAndUpdateState(channelIID, CommandFactory.createReadLastErrorCommand(),
ResponseParserFactory.ERROR_LINE, e -> { 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) { Consumer<T> resultProcessor) {
try { try {
T result = executeCommandWithRetry(channelIID, command, parser, 5); T result = executeCommandWithRetry(channelIID, command, parser, 5);
@ -265,17 +273,19 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler {
} }
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
} catch (Rego6xxProtocolException e) { } catch (Rego6xxProtocolException | RuntimeException e) {
logger.warn("Executing command for channel '{}' failed.", channelIID, e); logger.warn("Executing command for channel '{}' failed.", channelIID, e);
if (channelIID != null) {
updateState(channelIID, UnDefType.UNDEF); updateState(channelIID, UnDefType.UNDEF);
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.debug("Execution interrupted when accessing value for channel '{}'.", channelIID, e); logger.debug("Execution interrupted when accessing value for channel '{}'.", channelIID, e);
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
} }
private <T> T executeCommandWithRetry(String channelIID, byte[] command, ResponseParser<T> parser, int retry) private <T> T executeCommandWithRetry(@Nullable String channelIID, byte[] command, ResponseParser<T> parser,
throws Rego6xxProtocolException, IOException, InterruptedException { int retry) throws Rego6xxProtocolException, IOException, InterruptedException {
try { try {
checkRegoDevice(); checkRegoDevice();
return executeCommand(channelIID, command, parser); 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 { throws Rego6xxProtocolException, IOException, InterruptedException {
try { try {
return executeCommandInternal(channelIID, command, parser); return executeCommandInternal(channelIID, command, parser);
} catch (IOException e) { } catch (IOException e) {
RegoConnection connection = this.connection;
if (connection != null) { if (connection != null) {
connection.close(); 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 { throws Rego6xxProtocolException, IOException, InterruptedException {
// CHANNEL_LAST_ERROR_CODE and CHANNEL_LAST_ERROR_TIMESTAMP are read from same // 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, // 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; || CHANNEL_LAST_ERROR_TIMESTAMP.equals(channelIID)) ? CHANNEL_LAST_ERROR : channelIID;
// Use transient channel descriptor for null (not cached) channels. // Use transient channel descriptor for null (not cached) channels.
ChannelDescriptor descriptor = channelIID == null ? new ChannelDescriptor() ChannelDescriptor descriptor;
: channelDescriptorForChannel(mappedChannelIID); if (mappedChannelIID == null) {
descriptor = new ChannelDescriptor();
} else {
descriptor = channelDescriptorForChannel(mappedChannelIID);
}
byte[] cachedValue = descriptor.cachedValueIfNotExpired(refreshInterval); byte[] cachedValue = descriptor.cachedValueIfNotExpired(refreshInterval);
if (cachedValue != null) { if (cachedValue != null) {
@ -348,6 +363,11 @@ abstract class Rego6xxHeatPumpHandler extends BaseThingHandler {
} }
// Send command to device and wait for response. // 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()) { if (!connection.isConnected()) {
connection.connect(); connection.connect();
} }

View File

@ -12,6 +12,10 @@
*/ */
package org.openhab.binding.regoheatpump.internal.handler; 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.RegoHeatPumpBindingConstants;
import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection;
import org.openhab.binding.regoheatpump.internal.protocol.SerialRegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.SerialRegoConnection;
@ -27,9 +31,10 @@ import org.openhab.core.thing.ThingStatusDetail;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class SerialHusdataHandler extends HusdataHandler { public class SerialHusdataHandler extends HusdataHandler {
private final SerialPortManager serialPortManager; private final SerialPortManager serialPortManager;
private SerialPortIdentifier serialPortIdentifier; private @Nullable SerialPortIdentifier serialPortIdentifier;
public SerialHusdataHandler(Thing thing, SerialPortManager serialPortManager) { public SerialHusdataHandler(Thing thing, SerialPortManager serialPortManager) {
super(thing); super(thing);
@ -49,7 +54,11 @@ public class SerialHusdataHandler extends HusdataHandler {
} }
@Override @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); return new SerialRegoConnection(serialPortIdentifier, 19200);
} }
} }

View File

@ -12,6 +12,10 @@
*/ */
package org.openhab.binding.regoheatpump.internal.handler; 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.RegoHeatPumpBindingConstants;
import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.RegoConnection;
import org.openhab.binding.regoheatpump.internal.protocol.SerialRegoConnection; import org.openhab.binding.regoheatpump.internal.protocol.SerialRegoConnection;
@ -27,9 +31,10 @@ import org.openhab.core.thing.ThingStatusDetail;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class SerialRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler { public class SerialRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler {
private final SerialPortManager serialPortManager; private final SerialPortManager serialPortManager;
private SerialPortIdentifier serialPortIdentifier; private @Nullable SerialPortIdentifier serialPortIdentifier;
public SerialRego6xxHeatPumpHandler(Thing thing, SerialPortManager serialPortManager) { public SerialRego6xxHeatPumpHandler(Thing thing, SerialPortManager serialPortManager) {
super(thing); super(thing);
@ -49,7 +54,11 @@ public class SerialRego6xxHeatPumpHandler extends Rego6xxHeatPumpHandler {
} }
@Override @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); return new SerialRegoConnection(serialPortIdentifier, 19200);
} }
} }

View File

@ -18,6 +18,8 @@ import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -26,6 +28,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class IpRegoConnection implements RegoConnection { public class IpRegoConnection implements RegoConnection {
/** /**
* Connection timeout in milliseconds * Connection timeout in milliseconds
@ -40,7 +43,7 @@ public class IpRegoConnection implements RegoConnection {
private final Logger logger = LoggerFactory.getLogger(IpRegoConnection.class); private final Logger logger = LoggerFactory.getLogger(IpRegoConnection.class);
private final String address; private final String address;
private final int port; private final int port;
private Socket clientSocket; private @Nullable Socket clientSocket;
public IpRegoConnection(String address, int port) { public IpRegoConnection(String address, int port) {
this.address = address; this.address = address;
@ -50,10 +53,12 @@ public class IpRegoConnection implements RegoConnection {
@Override @Override
public void connect() throws IOException { public void connect() throws IOException {
logger.debug("Connecting to '{}', port = {}.", address, port); logger.debug("Connecting to '{}', port = {}.", address, port);
Socket clientSocket = this.clientSocket;
if (clientSocket == null) { if (clientSocket == null) {
clientSocket = new Socket(); clientSocket = new Socket();
clientSocket.setSoTimeout(SOCKET_READ_TIMEOUT); clientSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
clientSocket.setKeepAlive(true); clientSocket.setKeepAlive(true);
this.clientSocket = clientSocket;
} }
clientSocket.connect(new InetSocketAddress(address, port), CONNECTION_TIMEOUT); clientSocket.connect(new InetSocketAddress(address, port), CONNECTION_TIMEOUT);
logger.debug("Connected to '{}', port = {}.", address, port); logger.debug("Connected to '{}', port = {}.", address, port);
@ -61,12 +66,15 @@ public class IpRegoConnection implements RegoConnection {
@Override @Override
public boolean isConnected() { public boolean isConnected() {
Socket clientSocket = this.clientSocket;
return clientSocket != null && clientSocket.isConnected(); return clientSocket != null && clientSocket.isConnected();
} }
@Override @Override
public void close() { public void close() {
try { try {
Socket clientSocket = this.clientSocket;
this.clientSocket = null;
if (clientSocket != null) { if (clientSocket != null) {
clientSocket.close(); 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. // There is really not much we can do here, ignore the error and continue execution.
logger.warn("Closing socket failed", e); logger.warn("Closing socket failed", e);
} }
clientSocket = null;
} }
@Override @Override
public OutputStream outputStream() throws IOException { public OutputStream outputStream() throws IOException {
return clientSocket.getOutputStream(); return getClientSocket().getOutputStream();
} }
@Override @Override
public InputStream inputStream() throws IOException { 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;
} }
} }

View File

@ -16,11 +16,14 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link RegoConnection} is responsible for creating connections to clients. * The {@link RegoConnection} is responsible for creating connections to clients.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public interface RegoConnection { public interface RegoConnection {
/** /**
* Connect to the receiver. Return true if the connection has succeeded or if already connected. * Connect to the receiver. Return true if the connection has succeeded or if already connected.

View File

@ -16,6 +16,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; 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.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortIdentifier; 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 * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class SerialRegoConnection implements RegoConnection { public class SerialRegoConnection implements RegoConnection {
private final int baudRate; private final int baudRate;
private final String portName; private final String portName;
private SerialPort serialPort; private @Nullable SerialPort serialPort;
private final SerialPortIdentifier serialPortIdentifier; private final SerialPortIdentifier serialPortIdentifier;
public SerialRegoConnection(SerialPortIdentifier serialPortIdentifier, int baudRate) { public SerialRegoConnection(SerialPortIdentifier serialPortIdentifier, int baudRate) {
@ -41,10 +44,11 @@ public class SerialRegoConnection implements RegoConnection {
@Override @Override
public void connect() throws IOException { public void connect() throws IOException {
try { try {
serialPort = serialPortIdentifier.open(SerialRegoConnection.class.getCanonicalName(), 2000); SerialPort serialPort = serialPortIdentifier.open(SerialRegoConnection.class.getCanonicalName(), 2000);
serialPort.enableReceiveTimeout(100); serialPort.enableReceiveTimeout(100);
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE); SerialPort.PARITY_NONE);
this.serialPort = serialPort;
} catch (PortInUseException e) { } catch (PortInUseException e) {
throw new IOException("Serial port already used: " + portName, e); throw new IOException("Serial port already used: " + portName, e);
} catch (UnsupportedCommOperationException e) { } catch (UnsupportedCommOperationException e) {
@ -59,19 +63,36 @@ public class SerialRegoConnection implements RegoConnection {
@Override @Override
public void close() { public void close() {
SerialPort serialPort = this.serialPort;
this.serialPort = null;
if (serialPort != null) { if (serialPort != null) {
serialPort.close(); serialPort.close();
serialPort = null;
} }
} }
@Override @Override
public OutputStream outputStream() throws IOException { 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 @Override
public InputStream inputStream() throws IOException { 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;
} }
} }

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; package org.openhab.binding.regoheatpump.internal.rego6xx;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link AbstractLongResponseParser} is responsible for parsing long form responses. * The {@link AbstractLongResponseParser} is responsible for parsing long form responses.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
abstract class AbstractLongResponseParser<T> extends AbstractResponseParser<T> { abstract class AbstractLongResponseParser<T> extends AbstractResponseParser<T> {
@Override @Override
public int responseLength() { public int responseLength() {

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; package org.openhab.binding.regoheatpump.internal.rego6xx;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.util.HexUtils; import org.openhab.core.util.HexUtils;
/** /**
@ -20,6 +21,7 @@ import org.openhab.core.util.HexUtils;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
abstract class AbstractResponseParser<T> implements ResponseParser<T> { abstract class AbstractResponseParser<T> implements ResponseParser<T> {
private static final byte COMPUTER_ADDRESS = (byte) 0x01; private static final byte COMPUTER_ADDRESS = (byte) 0x01;

View File

@ -14,11 +14,14 @@ package org.openhab.binding.regoheatpump.internal.rego6xx;
import java.util.Arrays; import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link Checksum} is responsible for calculating checksum of given data. * The {@link Checksum} is responsible for calculating checksum of given data.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
class Checksum { class Checksum {
static byte calculate(byte[]... lists) { static byte calculate(byte[]... lists) {
return Arrays.stream(lists).reduce((byte) 0, Checksum::calculate, (a, b) -> b); return Arrays.stream(lists).reduce((byte) 0, Checksum::calculate, (a, b) -> b);

View File

@ -12,12 +12,15 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; package org.openhab.binding.regoheatpump.internal.rego6xx;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link CommandFactory} is responsible for creating different commands that can * The {@link CommandFactory} is responsible for creating different commands that can
* be send to a rego 6xx unit. * be send to a rego 6xx unit.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class CommandFactory { public class CommandFactory {
private static final byte DEVICE_ADDRESS = (byte) 0x81; private static final byte DEVICE_ADDRESS = (byte) 0x81;

View File

@ -15,15 +15,20 @@ package org.openhab.binding.regoheatpump.internal.rego6xx;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link ErrorLine} is responsible for holding information about a single error line. * The {@link ErrorLine} is responsible for holding information about a single error line.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class ErrorLine { public class ErrorLine {
private final byte error; private final byte error;
private final String timestamp; private final String timestamp;
public static final ErrorLine NO_ERROR = new ErrorLine((byte) 0, "");
public ErrorLine(byte error, String timestamp) { public ErrorLine(byte error, String timestamp) {
this.error = error; this.error = error;
this.timestamp = timestamp; this.timestamp = timestamp;

View File

@ -12,18 +12,21 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; package org.openhab.binding.regoheatpump.internal.rego6xx;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link ErrorLineResponseParser} is responsible for parsing error information (log) entry. * The {@link ErrorLineResponseParser} is responsible for parsing error information (log) entry.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
class ErrorLineResponseParser extends AbstractLongResponseParser<ErrorLine> { class ErrorLineResponseParser extends AbstractLongResponseParser<ErrorLine> {
@Override @Override
protected ErrorLine convert(byte[] responseBytes) { protected ErrorLine convert(byte[] responseBytes) {
// 255 marks no error. // 255 marks no error.
if (responseBytes[1] == (byte) 255) { if (responseBytes[1] == (byte) 255) {
return null; return ErrorLine.NO_ERROR;
} }
return new ErrorLine(ValueConverter.arrayToByte(responseBytes, 1), return new ErrorLine(ValueConverter.arrayToByte(responseBytes, 1),

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; 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. * The {@link Rego6xxProtocolException} is responsible for holding information about an Rego6xx protocol error.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class Rego6xxProtocolException extends Exception { public class Rego6xxProtocolException extends Exception {
private static final long serialVersionUID = 7556083982084149686L; private static final long serialVersionUID = 7556083982084149686L;

View File

@ -19,6 +19,8 @@ import java.util.Map;
import javax.measure.Unit; 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.SIUnits;
import org.openhab.core.library.unit.Units; import org.openhab.core.library.unit.Units;
@ -27,6 +29,7 @@ import org.openhab.core.library.unit.Units;
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class RegoRegisterMapper { public class RegoRegisterMapper {
public static final RegoRegisterMapper REGO600; public static final RegoRegisterMapper REGO600;
@ -35,7 +38,7 @@ public class RegoRegisterMapper {
public double scaleFactor(); public double scaleFactor();
public Unit<?> unit(); public @Nullable Unit<?> unit();
public int convertValue(short value); public int convertValue(short value);
} }
@ -44,9 +47,9 @@ public class RegoRegisterMapper {
private static class ChannelImpl implements Channel { private static class ChannelImpl implements Channel {
private final short address; private final short address;
private final double scaleFactor; 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.address = address;
this.scaleFactor = scaleFactor; this.scaleFactor = scaleFactor;
this.unit = unit; this.unit = unit;
@ -63,7 +66,7 @@ public class RegoRegisterMapper {
} }
@Override @Override
public Unit<?> unit() { public @Nullable Unit<?> unit() {
return unit; return unit;
} }
@ -104,7 +107,7 @@ public class RegoRegisterMapper {
this.mappings = mappings; this.mappings = mappings;
} }
public Channel map(String channelIID) { public @Nullable Channel map(String channelIID) {
return mappings.get(channelIID); return mappings.get(channelIID);
} }

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; 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. * The {@link ResponseParser} is responsible for parsing arbitrary data coming from a rego 6xx unit.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public interface ResponseParser<T> { public interface ResponseParser<T> {
public int responseLength(); public int responseLength();

View File

@ -12,15 +12,18 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; 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 * The {@link ResponseParserFactory} is responsible for providing parsers for all known data
* forms coming from the rego 6xx unit. * forms coming from the rego 6xx unit.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
public class ResponseParserFactory { public class ResponseParserFactory {
public static final ResponseParser<Short> SHORT = new ShortResponseParser(); public static final ResponseParser<Short> SHORT = new ShortResponseParser();
public static final ResponseParser<String> STRING = new StringResponseParser(); public static final ResponseParser<String> STRING = new StringResponseParser();
public static final ResponseParser<ErrorLine> ERROR_LINE = new ErrorLineResponseParser(); 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();
} }

View File

@ -12,12 +12,15 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; package org.openhab.binding.regoheatpump.internal.rego6xx;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link ShortResponseParser} is responsible for parsing short form data format * The {@link ShortResponseParser} is responsible for parsing short form data format
* coming from the rego 6xx unit. * coming from the rego 6xx unit.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
class ShortResponseParser extends AbstractResponseParser<Short> { class ShortResponseParser extends AbstractResponseParser<Short> {
@Override @Override

View File

@ -12,12 +12,15 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; 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 * The {@link StringResponseParser} is responsible for parsing long (text) form data format
* coming from the rego 6xx unit. * coming from the rego 6xx unit.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
class StringResponseParser extends AbstractLongResponseParser<String> { class StringResponseParser extends AbstractLongResponseParser<String> {
@Override @Override

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; 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. * The {@link ValueConverter} is responsible for converting various rego 6xx specific data types.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
@NonNullByDefault
class ValueConverter { class ValueConverter {
public static byte[] shortToSevenBitFormat(short value) { public static byte[] shortToSevenBitFormat(short value) {
byte b1 = (byte) ((value & 0xC000) >> 14); byte b1 = (byte) ((value & 0xC000) >> 14);

View File

@ -12,20 +12,23 @@
*/ */
package org.openhab.binding.regoheatpump.internal.rego6xx; package org.openhab.binding.regoheatpump.internal.rego6xx;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link WriteResponse} is responsible for parsing write responses * The {@link WriteResponse} is responsible for parsing write responses
* coming from the rego 6xx unit. * coming from the rego 6xx unit.
* *
* @author Boris Krivonog - Initial contribution * @author Boris Krivonog - Initial contribution
*/ */
class WriteResponse extends AbstractResponseParser<Void> { @NonNullByDefault
class WriteResponse extends AbstractResponseParser<String> {
@Override @Override
public int responseLength() { public int responseLength() {
return 1; return 1;
} }
@Override @Override
protected Void convert(byte[] responseBytes) { protected String convert(byte[] responseBytes) {
return null; return "";
} }
} }