[powermax] Add null annotations (#11275)

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2021-10-10 10:04:37 +02:00 committed by GitHub
parent 2b5431df00
commit 99f595450a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 723 additions and 484 deletions

View File

@ -12,21 +12,24 @@
*/ */
package org.openhab.binding.powermax.internal.config; package org.openhab.binding.powermax.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link PowermaxIpConfiguration} is responsible for holding * The {@link PowermaxIpConfiguration} is responsible for holding
* configuration informations associated to a Powermax IP thing type * configuration informations associated to a Powermax IP thing type
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxIpConfiguration { public class PowermaxIpConfiguration {
public String ip; public String ip = "";
public Integer tcpPort; public int tcpPort = 0;
public Integer motionOffDelay; public int motionOffDelay = 3;
public Boolean allowArming; public boolean allowArming = false;
public Boolean allowDisarming; public boolean allowDisarming = false;
public String pinCode; public String pinCode = "";
public Boolean forceStandardMode; public boolean forceStandardMode = false;
public String panelType; public String panelType = "PowerMaxPro";
public Boolean autoSyncTime; public boolean autoSyncTime = false;
} }

View File

@ -12,20 +12,23 @@
*/ */
package org.openhab.binding.powermax.internal.config; package org.openhab.binding.powermax.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link PowermaxSerialConfiguration} is responsible for holding * The {@link PowermaxSerialConfiguration} is responsible for holding
* configuration informations associated to a Powermax serial thing type * configuration informations associated to a Powermax serial thing type
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxSerialConfiguration { public class PowermaxSerialConfiguration {
public String serialPort; public String serialPort = "";
public Integer motionOffDelay; public int motionOffDelay = 3;
public Boolean allowArming; public boolean allowArming = false;
public Boolean allowDisarming; public boolean allowDisarming = false;
public String pinCode; public String pinCode = "";
public Boolean forceStandardMode; public boolean forceStandardMode = false;
public String panelType; public String panelType = "PowerMaxPro";
public Boolean autoSyncTime; public boolean autoSyncTime = false;
} }

View File

@ -12,15 +12,18 @@
*/ */
package org.openhab.binding.powermax.internal.config; package org.openhab.binding.powermax.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link PowermaxX10Configuration} is responsible for holding * The {@link PowermaxX10Configuration} is responsible for holding
* configuration informations associated to a Powermax IP thing type * configuration informations associated to a Powermax IP thing type
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxX10Configuration { public class PowermaxX10Configuration {
public static final String DEVICE_NUMBER = "deviceNumber"; public static final String DEVICE_NUMBER = "deviceNumber";
public Integer deviceNumber; public int deviceNumber = -1;
} }

View File

@ -12,15 +12,18 @@
*/ */
package org.openhab.binding.powermax.internal.config; package org.openhab.binding.powermax.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* The {@link PowermaxZoneConfiguration} is responsible for holding * The {@link PowermaxZoneConfiguration} is responsible for holding
* configuration informations associated to a Powermax IP thing type * configuration informations associated to a Powermax IP thing type
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxZoneConfiguration { public class PowermaxZoneConfiguration {
public static final String ZONE_NUMBER = "zoneNumber"; public static final String ZONE_NUMBER = "zoneNumber";
public Integer zoneNumber; public int zoneNumber = -1;
} }

View File

@ -18,6 +18,8 @@ import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.message.PowermaxBaseMessage; import org.openhab.binding.powermax.internal.message.PowermaxBaseMessage;
import org.openhab.binding.powermax.internal.message.PowermaxMessageEvent; import org.openhab.binding.powermax.internal.message.PowermaxMessageEvent;
import org.openhab.binding.powermax.internal.message.PowermaxMessageEventListener; import org.openhab.binding.powermax.internal.message.PowermaxMessageEventListener;
@ -30,17 +32,19 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public abstract class PowermaxConnector implements PowermaxConnectorInterface { public abstract class PowermaxConnector implements PowermaxConnectorInterface {
private final Logger logger = LoggerFactory.getLogger(PowermaxConnector.class); private final Logger logger = LoggerFactory.getLogger(PowermaxConnector.class);
private InputStream input; protected final String readerThreadName;
private OutputStream output; private final List<PowermaxMessageEventListener> listeners = new ArrayList<>();
private @Nullable InputStream input;
private @Nullable OutputStream output;
private boolean connected; private boolean connected;
protected String readerThreadName; private @Nullable Thread readerThread;
private Thread readerThread;
private long waitingForResponse; private long waitingForResponse;
private List<PowermaxMessageEventListener> listeners = new ArrayList<>();
public PowermaxConnector(String readerThreadName) { public PowermaxConnector(String readerThreadName) {
this.readerThreadName = readerThreadName; this.readerThreadName = readerThreadName;
@ -58,26 +62,29 @@ public abstract class PowermaxConnector implements PowermaxConnectorInterface {
protected void cleanup(boolean closeStreams) { protected void cleanup(boolean closeStreams) {
logger.debug("cleanup(): cleaning up Connection"); logger.debug("cleanup(): cleaning up Connection");
if (readerThread != null) { Thread thread = readerThread;
readerThread.interrupt(); if (thread != null) {
thread.interrupt();
try { try {
readerThread.join(); thread.join();
} catch (InterruptedException e) { } catch (InterruptedException e) {
} }
} }
if (closeStreams) { if (closeStreams) {
if (output != null) { OutputStream out = output;
if (out != null) {
try { try {
output.close(); out.close();
} catch (IOException e) { } catch (IOException e) {
logger.debug("Error while closing the output stream: {}", e.getMessage()); logger.debug("Error while closing the output stream: {}", e.getMessage());
} }
} }
if (input != null) { InputStream in = input;
if (in != null) {
try { try {
input.close(); in.close();
} catch (IOException e) { } catch (IOException e) {
logger.debug("Error while closing the input stream: {}", e.getMessage()); logger.debug("Error while closing the input stream: {}", e.getMessage());
} }
@ -107,16 +114,20 @@ public abstract class PowermaxConnector implements PowermaxConnectorInterface {
/** /**
* Handles a communication failure * Handles a communication failure
*/ */
public void handleCommunicationFailure(String message) { public void handleCommunicationFailure(@Nullable String message) {
close(); close();
listeners.forEach(listener -> listener.onCommunicationFailure(message)); listeners.forEach(listener -> listener.onCommunicationFailure(message != null ? message : ""));
} }
@Override @Override
public void sendMessage(byte[] data) { public void sendMessage(byte[] data) {
try { try {
output.write(data); OutputStream out = output;
output.flush(); if (out == null) {
throw new IOException("output stream is undefined");
}
out.write(data);
out.flush();
} catch (IOException e) { } catch (IOException e) {
logger.debug("sendMessage(): Writing error: {}", e.getMessage(), e); logger.debug("sendMessage(): Writing error: {}", e.getMessage(), e);
handleCommunicationFailure(e.getMessage()); handleCommunicationFailure(e.getMessage());
@ -125,7 +136,11 @@ public abstract class PowermaxConnector implements PowermaxConnectorInterface {
@Override @Override
public int read(byte[] buffer) throws IOException { public int read(byte[] buffer) throws IOException {
return input.read(buffer); InputStream in = input;
if (in == null) {
throw new IOException("input stream is undefined");
}
return in.read(buffer);
} }
@Override @Override
@ -141,7 +156,7 @@ public abstract class PowermaxConnector implements PowermaxConnectorInterface {
/** /**
* @return the input stream * @return the input stream
*/ */
public InputStream getInput() { public @Nullable InputStream getInput() {
return input; return input;
} }
@ -150,14 +165,14 @@ public abstract class PowermaxConnector implements PowermaxConnectorInterface {
* *
* @param input the input stream * @param input the input stream
*/ */
public void setInput(InputStream input) { public void setInput(@Nullable InputStream input) {
this.input = input; this.input = input;
} }
/** /**
* @return the output stream * @return the output stream
*/ */
public OutputStream getOutput() { public @Nullable OutputStream getOutput() {
return output; return output;
} }
@ -166,7 +181,7 @@ public abstract class PowermaxConnector implements PowermaxConnectorInterface {
* *
* @param output the output stream * @param output the output stream
*/ */
public void setOutput(OutputStream output) { public void setOutput(@Nullable OutputStream output) {
this.output = output; this.output = output;
} }
@ -190,7 +205,7 @@ public abstract class PowermaxConnector implements PowermaxConnectorInterface {
/** /**
* @return the thread that handles the message reading * @return the thread that handles the message reading
*/ */
public Thread getReaderThread() { public @Nullable Thread getReaderThread() {
return readerThread; return readerThread;
} }

View File

@ -14,6 +14,7 @@ package org.openhab.binding.powermax.internal.connector;
import java.io.IOException; import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.powermax.internal.message.PowermaxMessageEventListener; import org.openhab.binding.powermax.internal.message.PowermaxMessageEventListener;
/** /**
@ -21,6 +22,7 @@ import org.openhab.binding.powermax.internal.message.PowermaxMessageEventListene
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public interface PowermaxConnectorInterface { public interface PowermaxConnectorInterface {
/** /**

View File

@ -16,6 +16,7 @@ import java.io.IOException;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.util.Arrays; import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.powermax.internal.message.PowermaxCommManager; import org.openhab.binding.powermax.internal.message.PowermaxCommManager;
import org.openhab.binding.powermax.internal.message.PowermaxReceiveType; import org.openhab.binding.powermax.internal.message.PowermaxReceiveType;
import org.openhab.core.util.HexUtils; import org.openhab.core.util.HexUtils;
@ -27,14 +28,15 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxReaderThread extends Thread { public class PowermaxReaderThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(PowermaxReaderThread.class);
private static final int READ_BUFFER_SIZE = 20; private static final int READ_BUFFER_SIZE = 20;
private static final int MAX_MSG_SIZE = 0xC0; private static final int MAX_MSG_SIZE = 0xC0;
private PowermaxConnector connector; private final Logger logger = LoggerFactory.getLogger(PowermaxReaderThread.class);
private final PowermaxConnector connector;
/** /**
* Constructor * Constructor

View File

@ -13,7 +13,11 @@
package org.openhab.binding.powermax.internal.connector; package org.openhab.binding.powermax.internal.connector;
import java.io.IOException; 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.SerialPort; import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortEvent; import org.openhab.core.io.transport.serial.SerialPortEvent;
import org.openhab.core.io.transport.serial.SerialPortEventListener; import org.openhab.core.io.transport.serial.SerialPortEventListener;
@ -27,6 +31,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxSerialConnector extends PowermaxConnector implements SerialPortEventListener { public class PowermaxSerialConnector extends PowermaxConnector implements SerialPortEventListener {
private final Logger logger = LoggerFactory.getLogger(PowermaxSerialConnector.class); private final Logger logger = LoggerFactory.getLogger(PowermaxSerialConnector.class);
@ -34,7 +39,7 @@ public class PowermaxSerialConnector extends PowermaxConnector implements Serial
private final String serialPortName; private final String serialPortName;
private final int baudRate; private final int baudRate;
private final SerialPortManager serialPortManager; private final SerialPortManager serialPortManager;
private SerialPort serialPort; private @Nullable SerialPort serialPort;
/** /**
* Constructor * Constructor
@ -50,7 +55,6 @@ public class PowermaxSerialConnector extends PowermaxConnector implements Serial
this.serialPortManager = serialPortManager; this.serialPortManager = serialPortManager;
this.serialPortName = serialPortName; this.serialPortName = serialPortName;
this.baudRate = baudRate; this.baudRate = baudRate;
this.serialPort = null;
} }
@Override @Override
@ -65,26 +69,31 @@ public class PowermaxSerialConnector extends PowermaxConnector implements Serial
SerialPort commPort = portIdentifier.open(this.getClass().getName(), 2000); SerialPort commPort = portIdentifier.open(this.getClass().getName(), 2000);
serialPort = commPort; serialPort = commPort;
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); commPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialPort.enableReceiveThreshold(1); commPort.enableReceiveThreshold(1);
serialPort.enableReceiveTimeout(250); commPort.enableReceiveTimeout(250);
setInput(serialPort.getInputStream()); InputStream inputStream = commPort.getInputStream();
setOutput(serialPort.getOutputStream()); setInput(inputStream);
OutputStream outputStream = commPort.getOutputStream();
setOutput(outputStream);
getOutput().flush(); if (outputStream != null) {
if (getInput().markSupported()) { outputStream.flush();
getInput().reset(); }
if (inputStream != null && inputStream.markSupported()) {
inputStream.reset();
} }
// RXTX serial port library causes high CPU load // RXTX serial port library causes high CPU load
// Start event listener, which will just sleep and slow down event // Start event listener, which will just sleep and slow down event
// loop // loop
serialPort.addEventListener(this); commPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true); commPort.notifyOnDataAvailable(true);
setReaderThread(new PowermaxReaderThread(this, readerThreadName)); PowermaxReaderThread readerThread = new PowermaxReaderThread(this, readerThreadName);
getReaderThread().start(); setReaderThread(readerThread);
readerThread.start();
setConnected(true); setConnected(true);
} }
@ -93,14 +102,15 @@ public class PowermaxSerialConnector extends PowermaxConnector implements Serial
public void close() { public void close() {
logger.debug("close(): Closing Serial Connection"); logger.debug("close(): Closing Serial Connection");
if (serialPort != null) { SerialPort commPort = serialPort;
serialPort.removeEventListener(); if (commPort != null) {
commPort.removeEventListener();
} }
super.cleanup(true); super.cleanup(true);
if (serialPort != null) { if (commPort != null) {
serialPort.close(); commPort.close();
} }
serialPort = null; serialPort = null;

View File

@ -18,6 +18,8 @@ import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
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 Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxTcpConnector extends PowermaxConnector { public class PowermaxTcpConnector extends PowermaxConnector {
private final Logger logger = LoggerFactory.getLogger(PowermaxTcpConnector.class); private final Logger logger = LoggerFactory.getLogger(PowermaxTcpConnector.class);
@ -33,7 +36,7 @@ public class PowermaxTcpConnector extends PowermaxConnector {
private final String ipAddress; private final String ipAddress;
private final int tcpPort; private final int tcpPort;
private final int connectTimeout; private final int connectTimeout;
private Socket tcpSocket; private @Nullable Socket tcpSocket;
/** /**
* Constructor. * Constructor.
@ -54,16 +57,18 @@ public class PowermaxTcpConnector extends PowermaxConnector {
public void open() throws Exception { public void open() throws Exception {
logger.debug("open(): Opening TCP Connection"); logger.debug("open(): Opening TCP Connection");
tcpSocket = new Socket(); Socket socket = new Socket();
tcpSocket.setSoTimeout(250); tcpSocket = socket;
socket.setSoTimeout(250);
SocketAddress socketAddress = new InetSocketAddress(ipAddress, tcpPort); SocketAddress socketAddress = new InetSocketAddress(ipAddress, tcpPort);
tcpSocket.connect(socketAddress, connectTimeout); socket.connect(socketAddress, connectTimeout);
setInput(tcpSocket.getInputStream()); setInput(socket.getInputStream());
setOutput(tcpSocket.getOutputStream()); setOutput(socket.getOutputStream());
setReaderThread(new PowermaxReaderThread(this, readerThreadName)); PowermaxReaderThread readerThread = new PowermaxReaderThread(this, readerThreadName);
getReaderThread().start(); setReaderThread(readerThread);
readerThread.start();
setConnected(true); setConnected(true);
} }
@ -74,9 +79,10 @@ public class PowermaxTcpConnector extends PowermaxConnector {
super.cleanup(false); super.cleanup(false);
if (tcpSocket != null) { Socket socket = tcpSocket;
if (socket != null) {
try { try {
tcpSocket.close(); socket.close();
} catch (IOException e) { } catch (IOException e) {
logger.debug("Error while closing the socket: {}", e.getMessage()); logger.debug("Error while closing the socket: {}", e.getMessage());
} }

View File

@ -15,7 +15,9 @@ package org.openhab.binding.powermax.internal.console;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.powermax.internal.handler.PowermaxBridgeHandler; import org.openhab.binding.powermax.internal.handler.PowermaxBridgeHandler;
import org.openhab.binding.powermax.internal.state.PowermaxState;
import org.openhab.core.io.console.Console; import org.openhab.core.io.console.Console;
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension; import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
import org.openhab.core.io.console.extensions.ConsoleCommandExtension; import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
@ -23,6 +25,7 @@ import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry; import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandler;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
@ -31,6 +34,7 @@ import org.osgi.service.component.annotations.Reference;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
@Component(service = ConsoleCommandExtension.class) @Component(service = ConsoleCommandExtension.class)
public class PowermaxCommandExtension extends AbstractConsoleCommandExtension { public class PowermaxCommandExtension extends AbstractConsoleCommandExtension {
@ -38,10 +42,12 @@ public class PowermaxCommandExtension extends AbstractConsoleCommandExtension {
private static final String DOWNLOAD_SETUP = "download_setup"; private static final String DOWNLOAD_SETUP = "download_setup";
private static final String BRIDGE_STATE = "bridge_state"; private static final String BRIDGE_STATE = "bridge_state";
private ThingRegistry thingRegistry; private final ThingRegistry thingRegistry;
public PowermaxCommandExtension() { @Activate
public PowermaxCommandExtension(final @Reference ThingRegistry thingRegistry) {
super("powermax", "Interact with the Powermax binding."); super("powermax", "Interact with the Powermax binding.");
this.thingRegistry = thingRegistry;
} }
@Override @Override
@ -83,9 +89,12 @@ public class PowermaxCommandExtension extends AbstractConsoleCommandExtension {
console.println("Command '" + args[1] + "' handled."); console.println("Command '" + args[1] + "' handled.");
break; break;
case BRIDGE_STATE: case BRIDGE_STATE:
for (String line : handler.getCurrentState().toString().split("\n")) { PowermaxState state = handler.getCurrentState();
if (state != null) {
for (String line : state.toString().split("\n")) {
console.println(line); console.println(line);
} }
}
break; break;
default: default:
console.println("Unknown Powermax sub command '" + args[1] + "'"); console.println("Unknown Powermax sub command '" + args[1] + "'");
@ -104,13 +113,4 @@ public class PowermaxCommandExtension extends AbstractConsoleCommandExtension {
buildCommandUsage("<bridgeUID> " + DOWNLOAD_SETUP, "download setup"), buildCommandUsage("<bridgeUID> " + DOWNLOAD_SETUP, "download setup"),
buildCommandUsage("<bridgeUID> " + BRIDGE_STATE, "show current state") }); buildCommandUsage("<bridgeUID> " + BRIDGE_STATE, "show current state") });
} }
@Reference
protected void setThingRegistry(ThingRegistry thingRegistry) {
this.thingRegistry = thingRegistry;
}
protected void unsetThingRegistry(ThingRegistry thingRegistry) {
this.thingRegistry = null;
}
} }

View File

@ -48,10 +48,10 @@ import org.slf4j.LoggerFactory;
public class PowermaxDiscoveryService extends AbstractDiscoveryService public class PowermaxDiscoveryService extends AbstractDiscoveryService
implements PowermaxPanelSettingsListener, ThingHandlerService { implements PowermaxPanelSettingsListener, ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(PowermaxDiscoveryService.class);
private static final int SEARCH_TIME = 5; private static final int SEARCH_TIME = 5;
private final Logger logger = LoggerFactory.getLogger(PowermaxDiscoveryService.class);
private @Nullable PowermaxBridgeHandler bridgeHandler; private @Nullable PowermaxBridgeHandler bridgeHandler;
/** /**

View File

@ -23,6 +23,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
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.powermax.internal.config.PowermaxIpConfiguration; import org.openhab.binding.powermax.internal.config.PowermaxIpConfiguration;
import org.openhab.binding.powermax.internal.config.PowermaxSerialConfiguration; import org.openhab.binding.powermax.internal.config.PowermaxSerialConfiguration;
import org.openhab.binding.powermax.internal.discovery.PowermaxDiscoveryService; import org.openhab.binding.powermax.internal.discovery.PowermaxDiscoveryService;
@ -36,6 +38,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
import org.openhab.binding.powermax.internal.state.PowermaxStateContainer.Value; import org.openhab.binding.powermax.internal.state.PowermaxStateContainer.Value;
import org.openhab.binding.powermax.internal.state.PowermaxStateEvent; import org.openhab.binding.powermax.internal.state.PowermaxStateEvent;
import org.openhab.binding.powermax.internal.state.PowermaxStateEventListener; import org.openhab.binding.powermax.internal.state.PowermaxStateEventListener;
import org.openhab.binding.powermax.internal.state.PowermaxZoneSettings;
import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.transport.serial.SerialPortManager; import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
@ -59,18 +62,12 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxBridgeHandler extends BaseBridgeHandler implements PowermaxStateEventListener { public class PowermaxBridgeHandler extends BaseBridgeHandler implements PowermaxStateEventListener {
private final Logger logger = LoggerFactory.getLogger(PowermaxBridgeHandler.class);
private final SerialPortManager serialPortManager;
private final TimeZoneProvider timeZoneProvider;
private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1); private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
private static final long FIVE_MINUTES = TimeUnit.MINUTES.toMillis(5); private static final long FIVE_MINUTES = TimeUnit.MINUTES.toMillis(5);
/** Default delay in milliseconds to reset a motion detection */
private static final long DEFAULT_MOTION_OFF_DELAY = TimeUnit.MINUTES.toMillis(3);
private static final int NB_EVENT_LOG = 10; private static final int NB_EVENT_LOG = 10;
private static final PowermaxPanelType DEFAULT_PANEL_TYPE = PowermaxPanelType.POWERMAX_PRO; private static final PowermaxPanelType DEFAULT_PANEL_TYPE = PowermaxPanelType.POWERMAX_PRO;
@ -79,9 +76,14 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
private static final int MAX_DOWNLOAD_ATTEMPTS = 3; private static final int MAX_DOWNLOAD_ATTEMPTS = 3;
private ScheduledFuture<?> globalJob; private final Logger logger = LoggerFactory.getLogger(PowermaxBridgeHandler.class);
private List<PowermaxPanelSettingsListener> listeners = new CopyOnWriteArrayList<>(); private final SerialPortManager serialPortManager;
private final TimeZoneProvider timeZoneProvider;
private final List<PowermaxPanelSettingsListener> listeners = new CopyOnWriteArrayList<>();
private @Nullable ScheduledFuture<?> globalJob;
/** The delay in milliseconds to reset a motion detection */ /** The delay in milliseconds to reset a motion detection */
private long motionOffDelay; private long motionOffDelay;
@ -96,7 +98,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
private PowermaxState currentState; private PowermaxState currentState;
/** The object in charge of the communication with the Powermax alarm system */ /** The object in charge of the communication with the Powermax alarm system */
private PowermaxCommManager commManager; private @Nullable PowermaxCommManager commManager;
private int remainingDownloadAttempts; private int remainingDownloadAttempts;
@ -104,6 +106,8 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
super(thing); super(thing);
this.serialPortManager = serialPortManager; this.serialPortManager = serialPortManager;
this.timeZoneProvider = timeZoneProvider; this.timeZoneProvider = timeZoneProvider;
this.pinCode = "";
this.currentState = new PowermaxState(new PowermaxPanelSettings(DEFAULT_PANEL_TYPE), timeZoneProvider);
} }
@Override @Override
@ -111,12 +115,14 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
return Collections.singleton(PowermaxDiscoveryService.class); return Collections.singleton(PowermaxDiscoveryService.class);
} }
public PowermaxState getCurrentState() { public @Nullable PowermaxState getCurrentState() {
return currentState; PowermaxCommManager localCommManager = commManager;
return (localCommManager == null) ? null : currentState;
} }
public PowermaxPanelSettings getPanelSettings() { public @Nullable PowermaxPanelSettings getPanelSettings() {
return (commManager == null) ? null : commManager.getPanelSettings(); PowermaxCommManager localCommManager = commManager;
return (localCommManager == null) ? null : localCommManager.getPanelSettings();
} }
@Override @Override
@ -137,7 +143,8 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
} }
if (errorMsg == null) { if (errorMsg == null) {
if (globalJob == null || globalJob.isCancelled()) { ScheduledFuture<?> job = globalJob;
if (job == null || job.isCancelled()) {
// Delay the startup in case the handler is restarted immediately // Delay the startup in case the handler is restarted immediately
globalJob = scheduler.scheduleWithFixedDelay(() -> { globalJob = scheduler.scheduleWithFixedDelay(() -> {
try { try {
@ -146,7 +153,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
updateRingingState(); updateRingingState();
if (isConnected()) { if (isConnected()) {
checkKeepAlive(); checkKeepAlive();
commManager.retryDownloadSetup(remainingDownloadAttempts); retryDownloadSetup();
} else { } else {
tryReconnect(); tryReconnect();
} }
@ -160,30 +167,27 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
} }
} }
private String initializeBridgeSerial(PowermaxSerialConfiguration config, String threadName) { private @Nullable String initializeBridgeSerial(PowermaxSerialConfiguration config, String threadName) {
String errorMsg = null; String errorMsg = null;
if (config.serialPort != null && !config.serialPort.trim().isEmpty() String serialPort = config.serialPort.trim();
&& !config.serialPort.trim().startsWith("rfc2217")) { if (!serialPort.isEmpty() && !serialPort.startsWith("rfc2217")) {
motionOffDelay = getMotionOffDelaySetting(config.motionOffDelay, DEFAULT_MOTION_OFF_DELAY); motionOffDelay = config.motionOffDelay * ONE_MINUTE;
boolean allowArming = getBooleanSetting(config.allowArming, false);
boolean allowDisarming = getBooleanSetting(config.allowDisarming, false);
pinCode = config.pinCode; pinCode = config.pinCode;
forceStandardMode = getBooleanSetting(config.forceStandardMode, false); forceStandardMode = config.forceStandardMode;
PowermaxPanelType panelType = getPanelTypeSetting(config.panelType, DEFAULT_PANEL_TYPE); PowermaxPanelType panelType = getPanelTypeSetting(config.panelType, DEFAULT_PANEL_TYPE);
boolean autoSyncTime = getBooleanSetting(config.autoSyncTime, false);
PowermaxArmMode.DISARMED.setAllowedCommand(allowDisarming); PowermaxArmMode.DISARMED.setAllowedCommand(config.allowDisarming);
PowermaxArmMode.ARMED_HOME.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_HOME.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_AWAY.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_AWAY.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_HOME_INSTANT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_HOME_INSTANT.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_AWAY_INSTANT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_AWAY_INSTANT.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_NIGHT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_NIGHT.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_NIGHT_INSTANT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_NIGHT_INSTANT.setAllowedCommand(config.allowArming);
commManager = new PowermaxCommManager(config.serialPort, panelType, forceStandardMode, autoSyncTime, commManager = new PowermaxCommManager(serialPort, panelType, forceStandardMode, config.autoSyncTime,
serialPortManager, threadName, timeZoneProvider); serialPortManager, threadName, timeZoneProvider);
} else { } else {
if (config.serialPort != null && config.serialPort.trim().startsWith("rfc2217")) { if (serialPort.startsWith("rfc2217")) {
errorMsg = "Please use the IP Connection thing type for a serial over IP connection."; errorMsg = "Please use the IP Connection thing type for a serial over IP connection.";
} else { } else {
errorMsg = "serialPort setting must be defined in thing configuration"; errorMsg = "serialPort setting must be defined in thing configuration";
@ -192,26 +196,24 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
return errorMsg; return errorMsg;
} }
private String initializeBridgeIp(PowermaxIpConfiguration config, String threadName) { private @Nullable String initializeBridgeIp(PowermaxIpConfiguration config, String threadName) {
String errorMsg = null; String errorMsg = null;
if (config.ip != null && !config.ip.trim().isEmpty() && config.tcpPort != null) { String ip = config.ip.trim();
motionOffDelay = getMotionOffDelaySetting(config.motionOffDelay, DEFAULT_MOTION_OFF_DELAY); if (!ip.isEmpty() && config.tcpPort > 0) {
boolean allowArming = getBooleanSetting(config.allowArming, false); motionOffDelay = config.motionOffDelay * ONE_MINUTE;
boolean allowDisarming = getBooleanSetting(config.allowDisarming, false);
pinCode = config.pinCode; pinCode = config.pinCode;
forceStandardMode = getBooleanSetting(config.forceStandardMode, false); forceStandardMode = config.forceStandardMode;
PowermaxPanelType panelType = getPanelTypeSetting(config.panelType, DEFAULT_PANEL_TYPE); PowermaxPanelType panelType = getPanelTypeSetting(config.panelType, DEFAULT_PANEL_TYPE);
boolean autoSyncTime = getBooleanSetting(config.autoSyncTime, false);
PowermaxArmMode.DISARMED.setAllowedCommand(allowDisarming); PowermaxArmMode.DISARMED.setAllowedCommand(config.allowDisarming);
PowermaxArmMode.ARMED_HOME.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_HOME.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_AWAY.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_AWAY.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_HOME_INSTANT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_HOME_INSTANT.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_AWAY_INSTANT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_AWAY_INSTANT.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_NIGHT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_NIGHT.setAllowedCommand(config.allowArming);
PowermaxArmMode.ARMED_NIGHT_INSTANT.setAllowedCommand(allowArming); PowermaxArmMode.ARMED_NIGHT_INSTANT.setAllowedCommand(config.allowArming);
commManager = new PowermaxCommManager(config.ip, config.tcpPort, panelType, forceStandardMode, autoSyncTime, commManager = new PowermaxCommManager(ip, config.tcpPort, panelType, forceStandardMode, config.autoSyncTime,
threadName, timeZoneProvider); threadName, timeZoneProvider);
} else { } else {
errorMsg = "ip and port settings must be defined in thing configuration"; errorMsg = "ip and port settings must be defined in thing configuration";
@ -222,8 +224,9 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
@Override @Override
public void dispose() { public void dispose() {
logger.debug("Handler disposed for thing {}", getThing().getUID()); logger.debug("Handler disposed for thing {}", getThing().getUID());
if (globalJob != null && !globalJob.isCancelled()) { ScheduledFuture<?> job = globalJob;
globalJob.cancel(true); if (job != null && !job.isCancelled()) {
job.cancel(true);
globalJob = null; globalJob = null;
} }
closeConnection(); closeConnection();
@ -236,13 +239,15 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* than the value defined by the variable motionOffDelay * than the value defined by the variable motionOffDelay
*/ */
private void updateMotionSensorState() { private void updateMotionSensorState() {
PowermaxCommManager localCommManager = commManager;
if (localCommManager != null) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (currentState != null) {
boolean update = false; boolean update = false;
PowermaxState updateState = commManager.createNewState(); PowermaxState updateState = localCommManager.createNewState();
PowermaxPanelSettings panelSettings = getPanelSettings(); PowermaxPanelSettings panelSettings = localCommManager.getPanelSettings();
for (int i = 1; i <= panelSettings.getNbZones(); i++) { for (int i = 1; i <= panelSettings.getNbZones(); i++) {
if (panelSettings.getZoneSettings(i) != null && panelSettings.getZoneSettings(i).isMotionSensor() PowermaxZoneSettings zoneSettings = panelSettings.getZoneSettings(i);
if (zoneSettings != null && zoneSettings.isMotionSensor()
&& currentState.getZone(i).isLastTripBeforeTime(now - motionOffDelay)) { && currentState.getZone(i).isLastTripBeforeTime(now - motionOffDelay)) {
update = true; update = true;
updateState.getZone(i).tripped.setValue(false); updateState.getZone(i).tripped.setValue(false);
@ -259,12 +264,14 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* Turn off the Ringing flag when the bell time expires * Turn off the Ringing flag when the bell time expires
*/ */
private void updateRingingState() { private void updateRingingState() {
if (currentState != null && Boolean.TRUE.equals(currentState.ringing.getValue())) { PowermaxCommManager localCommManager = commManager;
if (localCommManager != null && Boolean.TRUE.equals(currentState.ringing.getValue())) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
long bellTime = getPanelSettings().getBellTime() * ONE_MINUTE; long bellTime = localCommManager.getPanelSettings().getBellTime() * ONE_MINUTE;
if ((currentState.ringingSince.getValue() + bellTime) < now) { Long ringingSince = currentState.ringingSince.getValue();
PowermaxState updateState = commManager.createNewState(); if (ringingSince != null && (ringingSince + bellTime) < now) {
PowermaxState updateState = localCommManager.createNewState();
updateState.ringing.setValue(false); updateState.ringing.setValue(false);
updateChannelsFromAlarmState(RINGING, updateState); updateChannelsFromAlarmState(RINGING, updateState);
currentState.merge(updateState); currentState.merge(updateState);
@ -276,25 +283,33 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* Check that we're actively communicating with the panel * Check that we're actively communicating with the panel
*/ */
private void checkKeepAlive() { private void checkKeepAlive() {
PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
return;
}
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (Boolean.TRUE.equals(currentState.powerlinkMode.getValue()) Long lastKeepAlive = currentState.lastKeepAlive.getValue();
&& (currentState.lastKeepAlive.getValue() != null) Long lastMessageTime = currentState.lastMessageTime.getValue();
&& ((now - currentState.lastKeepAlive.getValue()) > ONE_MINUTE)) { if (Boolean.TRUE.equals(currentState.powerlinkMode.getValue()) && (lastKeepAlive != null)
&& ((now - lastKeepAlive) > ONE_MINUTE)) {
// In Powerlink mode: let Powermax know we are alive // In Powerlink mode: let Powermax know we are alive
commManager.sendRestoreMessage(); localCommManager.sendRestoreMessage();
currentState.lastKeepAlive.setValue(now); currentState.lastKeepAlive.setValue(now);
} else if (!Boolean.TRUE.equals(currentState.downloadMode.getValue()) } else if (!Boolean.TRUE.equals(currentState.downloadMode.getValue()) && (lastMessageTime != null)
&& (currentState.lastMessageTime.getValue() != null) && ((now - lastMessageTime) > FIVE_MINUTES)) {
&& ((now - currentState.lastMessageTime.getValue()) > FIVE_MINUTES)) {
// In Standard mode: ping the panel every so often to detect disconnects // In Standard mode: ping the panel every so often to detect disconnects
commManager.sendMessage(PowermaxSendType.STATUS); localCommManager.sendMessage(PowermaxSendType.STATUS);
} }
} }
private void tryReconnect() { private void tryReconnect() {
PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
return;
}
logger.info("Trying to connect or reconnect..."); logger.info("Trying to connect or reconnect...");
closeConnection(); closeConnection();
currentState = commManager.createNewState(); currentState = localCommManager.createNewState();
try { try {
openConnection(); openConnection();
logger.debug("openConnection(): connected"); logger.debug("openConnection(): connected");
@ -305,7 +320,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
updateChannelsFromAlarmState(MODE, currentState); updateChannelsFromAlarmState(MODE, currentState);
processPanelSettings(); processPanelSettings();
} else { } else {
commManager.startDownload(); localCommManager.startDownload();
} }
} catch (Exception e) { } catch (Exception e) {
logger.debug("openConnection(): {}", e.getMessage(), e); logger.debug("openConnection(): {}", e.getMessage(), e);
@ -320,9 +335,10 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* @return true if the connection has been opened * @return true if the connection has been opened
*/ */
private synchronized void openConnection() throws Exception { private synchronized void openConnection() throws Exception {
if (commManager != null) { PowermaxCommManager localCommManager = commManager;
commManager.addEventListener(this); if (localCommManager != null) {
commManager.open(); localCommManager.addEventListener(this);
localCommManager.open();
} }
remainingDownloadAttempts = MAX_DOWNLOAD_ATTEMPTS; remainingDownloadAttempts = MAX_DOWNLOAD_ATTEMPTS;
} }
@ -331,15 +347,24 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* Close TCP or Serial connection to the Powermax Alarm Panel and remove the Event Listener * Close TCP or Serial connection to the Powermax Alarm Panel and remove the Event Listener
*/ */
private synchronized void closeConnection() { private synchronized void closeConnection() {
if (commManager != null) { PowermaxCommManager localCommManager = commManager;
commManager.close(); if (localCommManager != null) {
commManager.removeEventListener(this); localCommManager.close();
localCommManager.removeEventListener(this);
} }
logger.debug("closeConnection(): disconnected"); logger.debug("closeConnection(): disconnected");
} }
private boolean isConnected() { private boolean isConnected() {
return commManager == null ? false : commManager.isConnected(); PowermaxCommManager localCommManager = commManager;
return localCommManager == null ? false : localCommManager.isConnected();
}
private void retryDownloadSetup() {
PowermaxCommManager localCommManager = commManager;
if (localCommManager != null) {
localCommManager.retryDownloadSetup(remainingDownloadAttempts);
}
} }
@Override @Override
@ -347,7 +372,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
logger.debug("Received command {} from channel {}", command, channelUID.getId()); logger.debug("Received command {} from channel {}", command, channelUID.getId());
if (command instanceof RefreshType) { if (command instanceof RefreshType) {
updateChannelsFromAlarmState(channelUID.getId(), currentState); updateChannelsFromAlarmState(channelUID.getId(), getCurrentState());
} else { } else {
switch (channelUID.getId()) { switch (channelUID.getId()) {
case ARM_MODE: case ARM_MODE:
@ -384,62 +409,81 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
} }
private void armCommand(PowermaxArmMode armMode) { private void armCommand(PowermaxArmMode armMode) {
if (!isConnected()) { PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
logger.debug("Powermax alarm binding not correctly initialized. Arm command is ignored.");
} else if (!isConnected()) {
logger.debug("Powermax alarm binding not connected. Arm command is ignored."); logger.debug("Powermax alarm binding not connected. Arm command is ignored.");
} else { } else {
commManager.requestArmMode(armMode, localCommManager.requestArmMode(armMode,
Boolean.TRUE.equals(currentState.powerlinkMode.getValue()) ? getPanelSettings().getFirstPinCode() Boolean.TRUE.equals(currentState.powerlinkMode.getValue())
? localCommManager.getPanelSettings().getFirstPinCode()
: pinCode); : pinCode);
} }
} }
private void pgmCommand(Command command) { private void pgmCommand(Command command) {
if (!isConnected()) { PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
logger.debug("Powermax alarm binding not correctly initialized. PGM command is ignored.");
} else if (!isConnected()) {
logger.debug("Powermax alarm binding not connected. PGM command is ignored."); logger.debug("Powermax alarm binding not connected. PGM command is ignored.");
} else { } else {
commManager.sendPGMX10(command, null); localCommManager.sendPGMX10(command, null);
} }
} }
public void x10Command(Byte deviceNr, Command command) { public void x10Command(Byte deviceNr, Command command) {
if (!isConnected()) { PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
logger.debug("Powermax alarm binding not correctly initialized. X10 command is ignored.");
} else if (!isConnected()) {
logger.debug("Powermax alarm binding not connected. X10 command is ignored."); logger.debug("Powermax alarm binding not connected. X10 command is ignored.");
} else { } else {
commManager.sendPGMX10(command, deviceNr); localCommManager.sendPGMX10(command, deviceNr);
} }
} }
public void zoneBypassed(byte zoneNr, boolean bypassed) { public void zoneBypassed(byte zoneNr, boolean bypassed) {
if (!isConnected()) { PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
logger.debug("Powermax alarm binding not correctly initialized. Zone bypass command is ignored.");
} else if (!isConnected()) {
logger.debug("Powermax alarm binding not connected. Zone bypass command is ignored."); logger.debug("Powermax alarm binding not connected. Zone bypass command is ignored.");
} else if (!Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) { } else if (!Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) {
logger.debug("Powermax alarm binding: Bypass option only supported in Powerlink mode"); logger.debug("Powermax alarm binding: Bypass option only supported in Powerlink mode");
} else if (!getPanelSettings().isBypassEnabled()) { } else if (!localCommManager.getPanelSettings().isBypassEnabled()) {
logger.debug("Powermax alarm binding: Bypass option not enabled in panel settings"); logger.debug("Powermax alarm binding: Bypass option not enabled in panel settings");
} else { } else {
commManager.sendZoneBypass(bypassed, zoneNr, getPanelSettings().getFirstPinCode()); localCommManager.sendZoneBypass(bypassed, zoneNr, localCommManager.getPanelSettings().getFirstPinCode());
} }
} }
private void downloadEventLog() { private void downloadEventLog() {
if (!isConnected()) { PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
logger.debug("Powermax alarm binding not correctly initialized. Event logs command is ignored.");
} else if (!isConnected()) {
logger.debug("Powermax alarm binding not connected. Event logs command is ignored."); logger.debug("Powermax alarm binding not connected. Event logs command is ignored.");
} else { } else {
commManager.requestEventLog( localCommManager.requestEventLog(Boolean.TRUE.equals(currentState.powerlinkMode.getValue())
Boolean.TRUE.equals(currentState.powerlinkMode.getValue()) ? getPanelSettings().getFirstPinCode() ? localCommManager.getPanelSettings().getFirstPinCode()
: pinCode); : pinCode);
} }
} }
public void downloadSetup() { public void downloadSetup() {
if (!isConnected()) { PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
logger.debug("Powermax alarm binding not correctly initialized. Download setup command is ignored.");
} else if (!isConnected()) {
logger.debug("Powermax alarm binding not connected. Download setup command is ignored."); logger.debug("Powermax alarm binding not connected. Download setup command is ignored.");
} else if (!Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) { } else if (!Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) {
logger.debug("Powermax alarm binding: download setup only supported in Powerlink mode"); logger.debug("Powermax alarm binding: download setup only supported in Powerlink mode");
} else if (commManager.isDownloadRunning()) { } else if (localCommManager.isDownloadRunning()) {
logger.debug("Powermax alarm binding: download setup not started as one is in progress"); logger.debug("Powermax alarm binding: download setup not started as one is in progress");
} else { } else {
commManager.startDownload(); localCommManager.startDownload();
if (currentState.lastKeepAlive.getValue() != null) { if (currentState.lastKeepAlive.getValue() != null) {
currentState.lastKeepAlive.setValue(System.currentTimeMillis()); currentState.lastKeepAlive.setValue(System.currentTimeMillis());
} }
@ -447,11 +491,17 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
} }
public String getInfoSetup() { public String getInfoSetup() {
return (getPanelSettings() == null) ? "" : getPanelSettings().getInfo(); PowermaxPanelSettings panelSettings = getPanelSettings();
return (panelSettings == null) ? "" : panelSettings.getInfo();
} }
@Override @Override
public void onNewStateEvent(EventObject event) { public void onNewStateEvent(EventObject event) {
PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
return;
}
PowermaxStateEvent stateEvent = (PowermaxStateEvent) event; PowermaxStateEvent stateEvent = (PowermaxStateEvent) event;
PowermaxState updateState = stateEvent.getState(); PowermaxState updateState = stateEvent.getState();
@ -459,7 +509,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
&& Boolean.TRUE.equals(updateState.downloadSetupRequired.getValue())) { && Boolean.TRUE.equals(updateState.downloadSetupRequired.getValue())) {
// After Enrolling Powerlink or if a reset is required // After Enrolling Powerlink or if a reset is required
logger.debug("Powermax alarm binding: Reset"); logger.debug("Powermax alarm binding: Reset");
commManager.startDownload(); localCommManager.startDownload();
updateState.downloadSetupRequired.setValue(false); updateState.downloadSetupRequired.setValue(false);
if (currentState.lastKeepAlive.getValue() != null) { if (currentState.lastKeepAlive.getValue() != null) {
currentState.lastKeepAlive.setValue(System.currentTimeMillis()); currentState.lastKeepAlive.setValue(System.currentTimeMillis());
@ -469,12 +519,13 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
// Were are in standard mode but received a keep alive message // Were are in standard mode but received a keep alive message
// so we switch in PowerLink mode // so we switch in PowerLink mode
logger.debug("Powermax alarm binding: Switching to Powerlink mode"); logger.debug("Powermax alarm binding: Switching to Powerlink mode");
commManager.startDownload(); localCommManager.startDownload();
} }
boolean doProcessSettings = (updateState.powerlinkMode.getValue() != null); boolean doProcessSettings = (updateState.powerlinkMode.getValue() != null);
getPanelSettings().getZoneRange().forEach(i -> { PowermaxPanelSettings panelSettings = localCommManager.getPanelSettings();
panelSettings.getZoneRange().forEach(i -> {
if (Boolean.TRUE.equals(updateState.getZone(i).armed.getValue()) if (Boolean.TRUE.equals(updateState.getZone(i).armed.getValue())
&& Boolean.TRUE.equals(currentState.getZone(i).bypassed.getValue())) { && Boolean.TRUE.equals(currentState.getZone(i).bypassed.getValue())) {
updateState.getZone(i).armed.setValue(false); updateState.getZone(i).armed.setValue(false);
@ -485,7 +536,6 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
updateChannelsFromAlarmState(updateState); updateChannelsFromAlarmState(updateState);
currentState.merge(updateState); currentState.merge(updateState);
PowermaxPanelSettings panelSettings = getPanelSettings();
if (!updateState.getUpdatedZoneNames().isEmpty()) { if (!updateState.getUpdatedZoneNames().isEmpty()) {
for (Integer zoneIdx : updateState.getUpdatedZoneNames().keySet()) { for (Integer zoneIdx : updateState.getUpdatedZoneNames().keySet()) {
if (panelSettings.getZoneSettings(zoneIdx) != null) { if (panelSettings.getZoneSettings(zoneIdx) != null) {
@ -499,7 +549,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
if (doProcessSettings) { if (doProcessSettings) {
// There is a change of mode (standard or Powerlink) // There is a change of mode (standard or Powerlink)
processPanelSettings(); processPanelSettings();
commManager.exitDownload(); localCommManager.exitDownload();
} }
} }
@ -510,9 +560,14 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
} }
private void processPanelSettings() { private void processPanelSettings() {
if (commManager.processPanelSettings(Boolean.TRUE.equals(currentState.powerlinkMode.getValue()))) { PowermaxCommManager localCommManager = commManager;
if (localCommManager == null) {
return;
}
if (localCommManager.processPanelSettings(Boolean.TRUE.equals(currentState.powerlinkMode.getValue()))) {
for (PowermaxPanelSettingsListener listener : listeners) { for (PowermaxPanelSettingsListener listener : listeners) {
listener.onPanelSettingsUpdated(getPanelSettings()); listener.onPanelSettingsUpdated(localCommManager.getPanelSettings());
} }
remainingDownloadAttempts = 0; remainingDownloadAttempts = 0;
} else { } else {
@ -525,10 +580,10 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
updatePropertiesFromPanelSettings(); updatePropertiesFromPanelSettings();
if (Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) { if (Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) {
logger.info("Powermax alarm binding: running in Powerlink mode"); logger.info("Powermax alarm binding: running in Powerlink mode");
commManager.sendRestoreMessage(); localCommManager.sendRestoreMessage();
} else { } else {
logger.info("Powermax alarm binding: running in Standard mode"); logger.info("Powermax alarm binding: running in Standard mode");
commManager.getInfosWhenInStandardMode(); localCommManager.getInfosWhenInStandardMode();
} }
} }
@ -537,7 +592,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* *
* @param state: the alarm system state * @param state: the alarm system state
*/ */
private void updateChannelsFromAlarmState(PowermaxState state) { private void updateChannelsFromAlarmState(@Nullable PowermaxState state) {
updateChannelsFromAlarmState(null, state); updateChannelsFromAlarmState(null, state);
} }
@ -547,7 +602,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* @param channel: filter on a particular channel; if null, consider all channels * @param channel: filter on a particular channel; if null, consider all channels
* @param state: the alarm system state * @param state: the alarm system state
*/ */
private synchronized void updateChannelsFromAlarmState(String channel, PowermaxState state) { private synchronized void updateChannelsFromAlarmState(@Nullable String channel, @Nullable PowermaxState state) {
if (state == null || !isConnected()) { if (state == null || !isConnected()) {
return; return;
} }
@ -555,17 +610,16 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
for (Value<?> value : state.getValues()) { for (Value<?> value : state.getValues()) {
String vChannel = value.getChannel(); String vChannel = value.getChannel();
if (((channel == null) || channel.equals(vChannel)) && (vChannel != null) && isLinked(vChannel) if (((channel == null) || channel.equals(vChannel)) && isLinked(vChannel) && (value.getValue() != null)) {
&& (value.getValue() != null)) {
updateState(vChannel, value.getState()); updateState(vChannel, value.getState());
} }
} }
for (int i = 1; i <= NB_EVENT_LOG; i++) { for (int i = 1; i <= NB_EVENT_LOG; i++) {
String channel2 = String.format(EVENT_LOG, i); String channel2 = String.format(EVENT_LOG, i);
if (((channel == null) || channel.equals(channel2)) && isLinked(channel2) String log = state.getEventLog(i);
&& (state.getEventLog(i) != null)) { if (((channel == null) || channel.equals(channel2)) && isLinked(channel2) && (log != null)) {
updateState(channel2, new StringType(state.getEventLog(i))); updateState(channel2, new StringType(log));
} }
} }
@ -574,14 +628,13 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
PowermaxThingHandler handler = (PowermaxThingHandler) thing.getHandler(); PowermaxThingHandler handler = (PowermaxThingHandler) thing.getHandler();
if (handler != null) { if (handler != null) {
if (thing.getThingTypeUID().equals(THING_TYPE_ZONE)) { if (thing.getThingTypeUID().equals(THING_TYPE_ZONE)) {
// All of the zone state objects will have the same list of values. // All of the zone state objects will have the same list of values.
// The use of getZone(1) here is just to get any PowermaxZoneState // The use of getZone(1) here is just to get any PowermaxZoneState
// and use it to get the list of zone channels. // and use it to get the list of zone channels.
for (Value<?> value : state.getZone(1).getValues()) { for (Value<?> value : state.getZone(1).getValues()) {
String channelId = value.getChannel(); String channelId = value.getChannel();
if ((channelId != null) && ((channel == null) || channel.equals(channelId))) { if ((channel == null) || channel.equals(channelId)) {
handler.updateChannelFromAlarmState(channelId, state); handler.updateChannelFromAlarmState(channelId, state);
} }
} }
@ -606,11 +659,14 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
* Update properties to match the alarm panel settings * Update properties to match the alarm panel settings
*/ */
private void updatePropertiesFromPanelSettings() { private void updatePropertiesFromPanelSettings() {
PowermaxPanelSettings panelSettings = getPanelSettings();
if (panelSettings == null) {
return;
}
String value; String value;
Map<String, String> properties = editProperties(); Map<String, String> properties = editProperties();
PowermaxPanelSettings panelSettings = getPanelSettings(); value = panelSettings.getPanelType().getLabel();
value = (panelSettings.getPanelType() != null) ? panelSettings.getPanelType().getLabel() : null; if (!value.isEmpty()) {
if (value != null && !value.isEmpty()) {
properties.put(Thing.PROPERTY_MODEL_ID, value); properties.put(Thing.PROPERTY_MODEL_ID, value);
} }
value = panelSettings.getPanelSerial(); value = panelSettings.getPanelSerial();
@ -640,26 +696,14 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
return listeners.remove(listener); return listeners.remove(listener);
} }
private boolean getBooleanSetting(Boolean value, boolean defaultValue) {
return value != null ? value.booleanValue() : defaultValue;
}
private long getMotionOffDelaySetting(Integer value, long defaultValue) {
return value != null ? value.intValue() * ONE_MINUTE : defaultValue;
}
private PowermaxPanelType getPanelTypeSetting(String value, PowermaxPanelType defaultValue) { private PowermaxPanelType getPanelTypeSetting(String value, PowermaxPanelType defaultValue) {
PowermaxPanelType result; PowermaxPanelType result;
if (value != null) {
try { try {
result = PowermaxPanelType.fromLabel(value); result = PowermaxPanelType.fromLabel(value);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
result = defaultValue; result = defaultValue;
logger.debug("Powermax alarm binding: panel type not configured correctly"); logger.debug("Powermax alarm binding: panel type not configured correctly");
} }
} else {
result = defaultValue;
}
return result; return result;
} }
} }

View File

@ -14,6 +14,7 @@ package org.openhab.binding.powermax.internal.handler;
import static org.openhab.binding.powermax.internal.PowermaxBindingConstants.*; import static org.openhab.binding.powermax.internal.PowermaxBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.config.PowermaxX10Configuration; import org.openhab.binding.powermax.internal.config.PowermaxX10Configuration;
import org.openhab.binding.powermax.internal.config.PowermaxZoneConfiguration; import org.openhab.binding.powermax.internal.config.PowermaxZoneConfiguration;
@ -44,16 +45,17 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPanelSettingsListener { public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPanelSettingsListener {
private final Logger logger = LoggerFactory.getLogger(PowermaxThingHandler.class);
private static final int ZONE_NR_MIN = 1; private static final int ZONE_NR_MIN = 1;
private static final int ZONE_NR_MAX = 64; private static final int ZONE_NR_MAX = 64;
private static final int X10_NR_MIN = 1; private static final int X10_NR_MIN = 1;
private static final int X10_NR_MAX = 16; private static final int X10_NR_MAX = 16;
private PowermaxBridgeHandler bridgeHandler; private final Logger logger = LoggerFactory.getLogger(PowermaxThingHandler.class);
private @Nullable PowermaxBridgeHandler bridgeHandler;
public PowermaxThingHandler(Thing thing) { public PowermaxThingHandler(Thing thing) {
super(thing); super(thing);
@ -77,7 +79,7 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
initializeThingState((bridge == null) ? null : bridge.getHandler(), bridgeStatusInfo.getStatus()); initializeThingState((bridge == null) ? null : bridge.getHandler(), bridgeStatusInfo.getStatus());
} }
private void initializeThingState(ThingHandler bridgeHandler, ThingStatus bridgeStatus) { private void initializeThingState(@Nullable ThingHandler bridgeHandler, @Nullable ThingStatus bridgeStatus) {
if (bridgeHandler != null && bridgeStatus != null) { if (bridgeHandler != null && bridgeStatus != null) {
if (bridgeStatus == ThingStatus.ONLINE) { if (bridgeStatus == ThingStatus.ONLINE) {
boolean validConfig = false; boolean validConfig = false;
@ -85,8 +87,7 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) { if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) {
PowermaxZoneConfiguration config = getConfigAs(PowermaxZoneConfiguration.class); PowermaxZoneConfiguration config = getConfigAs(PowermaxZoneConfiguration.class);
if (config.zoneNumber != null && config.zoneNumber >= ZONE_NR_MIN if (config.zoneNumber >= ZONE_NR_MIN && config.zoneNumber <= ZONE_NR_MAX) {
&& config.zoneNumber <= ZONE_NR_MAX) {
validConfig = true; validConfig = true;
} else { } else {
errorMsg = "zoneNumber setting must be defined in thing configuration and set between " errorMsg = "zoneNumber setting must be defined in thing configuration and set between "
@ -94,8 +95,7 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
} }
} else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) { } else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) {
PowermaxX10Configuration config = getConfigAs(PowermaxX10Configuration.class); PowermaxX10Configuration config = getConfigAs(PowermaxX10Configuration.class);
if (config.deviceNumber != null && config.deviceNumber >= X10_NR_MIN if (config.deviceNumber >= X10_NR_MIN && config.deviceNumber <= X10_NR_MAX) {
&& config.deviceNumber <= X10_NR_MAX) {
validConfig = true; validConfig = true;
} else { } else {
errorMsg = "deviceNumber setting must be defined in thing configuration and set between " errorMsg = "deviceNumber setting must be defined in thing configuration and set between "
@ -106,9 +106,10 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
if (validConfig) { if (validConfig) {
updateStatus(ThingStatus.UNKNOWN); updateStatus(ThingStatus.UNKNOWN);
logger.debug("Set handler status to UNKNOWN for thing {} (bridge ONLINE)", getThing().getUID()); logger.debug("Set handler status to UNKNOWN for thing {} (bridge ONLINE)", getThing().getUID());
this.bridgeHandler = (PowermaxBridgeHandler) bridgeHandler; PowermaxBridgeHandler powermaxBridgeHandler = (PowermaxBridgeHandler) bridgeHandler;
this.bridgeHandler.registerPanelSettingsListener(this); this.bridgeHandler = powermaxBridgeHandler;
onPanelSettingsUpdated(this.bridgeHandler.getPanelSettings()); powermaxBridgeHandler.registerPanelSettingsListener(this);
onPanelSettingsUpdated(powermaxBridgeHandler.getPanelSettings());
} else { } else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg);
} }
@ -133,8 +134,9 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
@Override @Override
public void dispose() { public void dispose() {
logger.debug("Handler disposed for thing {}", getThing().getUID()); logger.debug("Handler disposed for thing {}", getThing().getUID());
if (bridgeHandler != null) { PowermaxBridgeHandler powermaxBridgeHandler = bridgeHandler;
bridgeHandler.unregisterPanelSettingsListener(this); if (powermaxBridgeHandler != null) {
powermaxBridgeHandler.unregisterPanelSettingsListener(this);
} }
super.dispose(); super.dispose();
} }
@ -143,15 +145,18 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
public void handleCommand(ChannelUID channelUID, Command command) { public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Received command {} from channel {}", command, channelUID.getId()); logger.debug("Received command {} from channel {}", command, channelUID.getId());
if (bridgeHandler == null) { PowermaxBridgeHandler powermaxBridgeHandler = bridgeHandler;
if (powermaxBridgeHandler == null) {
return; return;
} else if (command instanceof RefreshType) { } else if (command instanceof RefreshType) {
updateChannelFromAlarmState(channelUID.getId(), bridgeHandler.getCurrentState()); updateChannelFromAlarmState(channelUID.getId(), powermaxBridgeHandler.getCurrentState());
} else { } else {
switch (channelUID.getId()) { switch (channelUID.getId()) {
case BYPASSED: case BYPASSED:
if (command instanceof OnOffType) { if (command instanceof OnOffType) {
bridgeHandler.zoneBypassed(getConfigAs(PowermaxZoneConfiguration.class).zoneNumber.byteValue(), powermaxBridgeHandler.zoneBypassed(
Byte.valueOf(
(byte) (getConfigAs(PowermaxZoneConfiguration.class).zoneNumber & 0x000000FF)),
command.equals(OnOffType.ON)); command.equals(OnOffType.ON));
} else { } else {
logger.debug("Command of type {} while OnOffType is expected. Command is ignored.", logger.debug("Command of type {} while OnOffType is expected. Command is ignored.",
@ -159,7 +164,9 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
} }
break; break;
case X10_STATUS: case X10_STATUS:
bridgeHandler.x10Command(getConfigAs(PowermaxX10Configuration.class).deviceNumber.byteValue(), powermaxBridgeHandler.x10Command(
Byte.valueOf(
(byte) (getConfigAs(PowermaxX10Configuration.class).deviceNumber & 0x000000FF)),
command); command);
break; break;
default: default:
@ -175,13 +182,13 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
* @param channel: the channel * @param channel: the channel
* @param state: the alarm system state * @param state: the alarm system state
*/ */
public void updateChannelFromAlarmState(String channel, PowermaxState state) { public void updateChannelFromAlarmState(String channel, @Nullable PowermaxState state) {
if (state == null || channel == null || !isLinked(channel)) { if (state == null || !isLinked(channel)) {
return; return;
} }
if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) { if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) {
int num = getConfigAs(PowermaxZoneConfiguration.class).zoneNumber.intValue(); int num = getConfigAs(PowermaxZoneConfiguration.class).zoneNumber;
for (Value<?> value : state.getZone(num).getValues()) { for (Value<?> value : state.getZone(num).getValues()) {
String vChannel = value.getChannel(); String vChannel = value.getChannel();
@ -191,9 +198,10 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
} }
} }
} else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) { } else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) {
int num = getConfigAs(PowermaxX10Configuration.class).deviceNumber.intValue(); int num = getConfigAs(PowermaxX10Configuration.class).deviceNumber;
if (channel.equals(X10_STATUS) && (state.getPGMX10DeviceStatus(num) != null)) { Boolean status = state.getPGMX10DeviceStatus(num);
updateState(X10_STATUS, state.getPGMX10DeviceStatus(num) ? OnOffType.ON : OnOffType.OFF); if (channel.equals(X10_STATUS) && (status != null)) {
updateState(X10_STATUS, status ? OnOffType.ON : OnOffType.OFF);
} }
} }
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
/** /**
@ -19,6 +21,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxAckMessage extends PowermaxBaseMessage { public class PowermaxAckMessage extends PowermaxBaseMessage {
/** /**
@ -32,14 +35,15 @@ public class PowermaxAckMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }
PowermaxState updatedState = null; PowermaxState updatedState = null;
if (commManager.getLastSendMsg().getSendType() == PowermaxSendType.EXIT) { PowermaxBaseMessage lastSendMsg = commManager.getLastSendMsg();
if (lastSendMsg != null && lastSendMsg.getSendType() == PowermaxSendType.EXIT) {
updatedState = commManager.createNewState(); updatedState = commManager.createNewState();
updatedState.powerlinkMode.setValue(true); updatedState.powerlinkMode.setValue(true);
updatedState.downloadMode.setValue(false); updatedState.downloadMode.setValue(false);

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
import org.openhab.core.util.HexUtils; import org.openhab.core.util.HexUtils;
@ -23,14 +24,15 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxBaseMessage { public class PowermaxBaseMessage {
private final Logger logger = LoggerFactory.getLogger(PowermaxBaseMessage.class); private final Logger logger = LoggerFactory.getLogger(PowermaxBaseMessage.class);
private byte[] rawData; private final byte[] rawData;
private int code; private int code;
private PowermaxSendType sendType; private @Nullable PowermaxSendType sendType;
private PowermaxReceiveType receiveType; private @Nullable PowermaxReceiveType receiveType;
private Object messageType; private Object messageType;
/** /**
@ -39,9 +41,9 @@ public class PowermaxBaseMessage {
* @param message the message as a buffer of bytes * @param message the message as a buffer of bytes
*/ */
public PowermaxBaseMessage(byte[] message) { public PowermaxBaseMessage(byte[] message) {
this.sendType = null; this.rawData = message;
this.messageType = "UNKNOWN"; this.messageType = "UNKNOWN";
decodeMessage(message); decodeMessage();
} }
/** /**
@ -59,40 +61,41 @@ public class PowermaxBaseMessage {
* @param sendType the type of a message to be sent * @param sendType the type of a message to be sent
* @param param the dynamic part of a message to be sent; null if no dynamic part * @param param the dynamic part of a message to be sent; null if no dynamic part
*/ */
public PowermaxBaseMessage(PowermaxSendType sendType, byte[] param) { public PowermaxBaseMessage(PowermaxSendType sendType, byte @Nullable [] param) {
this.sendType = sendType; this.sendType = sendType;
this.messageType = "UNKNOWN"; this.messageType = "UNKNOWN";
byte[] message = new byte[sendType.getMessage().length + 3]; this.rawData = new byte[sendType.getMessage().length + 3];
int index = 0; int index = 0;
message[index++] = 0x0D; rawData[index++] = 0x0D;
for (int i = 0; i < sendType.getMessage().length; i++) { for (int i = 0; i < sendType.getMessage().length; i++) {
if ((param != null) && (sendType.getParamPosition() != null) && (i >= sendType.getParamPosition()) Integer paramPosition = sendType.getParamPosition();
&& (i < (sendType.getParamPosition() + param.length))) { if ((param != null) && (paramPosition != null) && (i >= paramPosition)
message[index++] = param[i - sendType.getParamPosition()]; && (i < (paramPosition + param.length))) {
rawData[index++] = param[i - paramPosition];
} else { } else {
message[index++] = sendType.getMessage()[i]; rawData[index++] = sendType.getMessage()[i];
} }
} }
message[index++] = 0x00; rawData[index++] = 0x00;
message[index++] = 0x0A; rawData[index++] = 0x0A;
decodeMessage(message); decodeMessage();
} }
/** /**
* Extract information from the buffer of bytes and set class attributes * Extract information from the buffer of bytes and set class attributes
*
* @param data the message as a buffer of bytes
*/ */
private void decodeMessage(byte[] data) { private void decodeMessage() {
rawData = data;
code = rawData[1] & 0x000000FF; code = rawData[1] & 0x000000FF;
PowermaxReceiveType localReceiveType;
try { try {
receiveType = PowermaxReceiveType.fromCode((byte) code); localReceiveType = PowermaxReceiveType.fromCode((byte) code);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
receiveType = null; localReceiveType = null;
} }
receiveType = localReceiveType;
messageType = sendType != null ? sendType : receiveType != null ? receiveType : "UNKNOWN"; PowermaxSendType localSendType = sendType;
messageType = localSendType != null ? localSendType : localReceiveType != null ? localReceiveType : "UNKNOWN";
} }
/** /**
@ -100,7 +103,7 @@ public class PowermaxBaseMessage {
* *
* @return a new state containing all changes driven by the message * @return a new state containing all changes driven by the message
*/ */
public final PowermaxState handleMessage(PowermaxCommManager commManager) { public final @Nullable PowermaxState handleMessage(@Nullable PowermaxCommManager commManager) {
// Send an ACK if needed // Send an ACK if needed
if (isAckRequired() && commManager != null) { if (isAckRequired() && commManager != null) {
commManager.sendAck(this, (byte) 0x02); commManager.sendAck(this, (byte) 0x02);
@ -119,7 +122,7 @@ public class PowermaxBaseMessage {
return newState; return newState;
} }
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
return null; return null;
} }
@ -140,18 +143,18 @@ public class PowermaxBaseMessage {
/** /**
* @return the type of the message to be sent * @return the type of the message to be sent
*/ */
public PowermaxSendType getSendType() { public @Nullable PowermaxSendType getSendType() {
return sendType; return sendType;
} }
public void setSendType(PowermaxSendType sendType) { public void setSendType(@Nullable PowermaxSendType sendType) {
this.sendType = sendType; this.sendType = sendType;
} }
/** /**
* @return the type of the received message * @return the type of the received message
*/ */
public PowermaxReceiveType getReceiveType() { public @Nullable PowermaxReceiveType getReceiveType() {
return receiveType; return receiveType;
} }
@ -159,7 +162,8 @@ public class PowermaxBaseMessage {
* @return true if the received message requires the sending of an ACK * @return true if the received message requires the sending of an ACK
*/ */
public boolean isAckRequired() { public boolean isAckRequired() {
return receiveType == null || receiveType.isAckRequired(); PowermaxReceiveType localReceiveType = receiveType;
return localReceiveType == null || localReceiveType.isAckRequired();
} }
// Debugging helpers // Debugging helpers

View File

@ -23,6 +23,8 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
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.powermax.internal.connector.PowermaxConnector; import org.openhab.binding.powermax.internal.connector.PowermaxConnector;
import org.openhab.binding.powermax.internal.connector.PowermaxSerialConnector; import org.openhab.binding.powermax.internal.connector.PowermaxSerialConnector;
import org.openhab.binding.powermax.internal.connector.PowermaxTcpConnector; import org.openhab.binding.powermax.internal.connector.PowermaxTcpConnector;
@ -49,47 +51,49 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxCommManager implements PowermaxMessageEventListener { public class PowermaxCommManager implements PowermaxMessageEventListener {
private final Logger logger = LoggerFactory.getLogger(PowermaxCommManager.class);
private static final int DEFAULT_TCP_PORT = 80; private static final int DEFAULT_TCP_PORT = 80;
private static final int TCP_CONNECTION_TIMEOUT = 5000; private static final int TCP_CONNECTION_TIMEOUT = 5000;
private static final int DEFAULT_BAUD_RATE = 9600; private static final int DEFAULT_BAUD_RATE = 9600;
private static final int WAITING_DELAY_FOR_RESPONSE = 750; private static final int WAITING_DELAY_FOR_RESPONSE = 750;
private static final long DELAY_BETWEEN_SETUP_DOWNLOADS = TimeUnit.SECONDS.toMillis(45); private static final long DELAY_BETWEEN_SETUP_DOWNLOADS = TimeUnit.SECONDS.toMillis(45);
private final Logger logger = LoggerFactory.getLogger(PowermaxCommManager.class);
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
/** The object to store the current settings of the Powermax alarm system */
private PowermaxPanelSettings panelSettings;
/** Panel type used when in standard mode */
private PowermaxPanelType panelType;
private boolean forceStandardMode;
private boolean autoSyncTime;
private final TimeZoneProvider timeZoneProvider; private final TimeZoneProvider timeZoneProvider;
private List<PowermaxStateEventListener> listeners = new ArrayList<>(); /** The object to store the current settings of the Powermax alarm system */
private final PowermaxPanelSettings panelSettings;
/** Panel type used when in standard mode */
private final PowermaxPanelType panelType;
private final boolean forceStandardMode;
private final boolean autoSyncTime;
private final List<PowermaxStateEventListener> listeners = new ArrayList<>();
/** The serial or TCP connecter used to communicate with the Powermax alarm system */ /** The serial or TCP connecter used to communicate with the Powermax alarm system */
private PowermaxConnector connector; private final PowermaxConnector connector;
/** The last message sent to the the Powermax alarm system */ /** The last message sent to the the Powermax alarm system */
private PowermaxBaseMessage lastSendMsg; private @Nullable PowermaxBaseMessage lastSendMsg;
/** The message queue of messages to be sent to the the Powermax alarm system */ /** The message queue of messages to be sent to the the Powermax alarm system */
private ConcurrentLinkedQueue<PowermaxBaseMessage> msgQueue = new ConcurrentLinkedQueue<>(); private ConcurrentLinkedQueue<PowermaxBaseMessage> msgQueue = new ConcurrentLinkedQueue<>();
/** The time in milliseconds the last download of the panel setup was requested */ /** The time in milliseconds the last download of the panel setup was requested */
private Long lastTimeDownloadRequested; private long lastTimeDownloadRequested;
/** The boolean indicating if the download of the panel setup is in progress or not */ /** The boolean indicating if the download of the panel setup is in progress or not */
private boolean downloadRunning; private boolean downloadRunning;
/** The time in milliseconds used to set time and date */ /** The time in milliseconds used to set time and date */
private Long syncTimeCheck; private long syncTimeCheck;
/** /**
* Constructor for Serial Connection * Constructor for Serial Connection
@ -110,13 +114,8 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
this.timeZoneProvider = timeZoneProvider; this.timeZoneProvider = timeZoneProvider;
this.panelSettings = new PowermaxPanelSettings(panelType); this.panelSettings = new PowermaxPanelSettings(panelType);
this.scheduler = ThreadPoolManager.getScheduledPool(threadName + "-sender"); this.scheduler = ThreadPoolManager.getScheduledPool(threadName + "-sender");
String serialPort = (sPort != null && !sPort.trim().isEmpty()) ? sPort.trim() : null; this.connector = new PowermaxSerialConnector(serialPortManager, sPort.trim(), DEFAULT_BAUD_RATE,
if (serialPort != null) {
connector = new PowermaxSerialConnector(serialPortManager, serialPort, DEFAULT_BAUD_RATE,
threadName + "-reader"); threadName + "-reader");
} else {
connector = null;
}
} }
/** /**
@ -138,13 +137,8 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
this.timeZoneProvider = timeZoneProvider; this.timeZoneProvider = timeZoneProvider;
this.panelSettings = new PowermaxPanelSettings(panelType); this.panelSettings = new PowermaxPanelSettings(panelType);
this.scheduler = ThreadPoolManager.getScheduledPool(threadName + "-sender"); this.scheduler = ThreadPoolManager.getScheduledPool(threadName + "-sender");
String ipAddress = (ip != null && !ip.trim().isEmpty()) ? ip.trim() : null; this.connector = new PowermaxTcpConnector(ip.trim(), port > 0 ? port : DEFAULT_TCP_PORT, TCP_CONNECTION_TIMEOUT,
int tcpPort = (port > 0) ? port : DEFAULT_TCP_PORT; threadName + "-reader");
if (ipAddress != null) {
connector = new PowermaxTcpConnector(ipAddress, tcpPort, TCP_CONNECTION_TIMEOUT, threadName + "-reader");
} else {
connector = null;
}
} }
/** /**
@ -154,10 +148,8 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
*/ */
public synchronized void addEventListener(PowermaxStateEventListener listener) { public synchronized void addEventListener(PowermaxStateEventListener listener) {
listeners.add(listener); listeners.add(listener);
if (connector != null) {
connector.addEventListener(this); connector.addEventListener(this);
} }
}
/** /**
* Remove event listener * Remove event listener
@ -165,9 +157,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
* @param listener the listener to be removed * @param listener the listener to be removed
*/ */
public synchronized void removeEventListener(PowermaxStateEventListener listener) { public synchronized void removeEventListener(PowermaxStateEventListener listener) {
if (connector != null) {
connector.removeEventListener(this); connector.removeEventListener(this);
}
listeners.remove(listener); listeners.remove(listener);
} }
@ -177,9 +167,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
* @return true if connected or false if not * @return true if connected or false if not
*/ */
public void open() throws Exception { public void open() throws Exception {
if (connector != null) {
connector.open(); connector.open();
}
lastSendMsg = null; lastSendMsg = null;
msgQueue = new ConcurrentLinkedQueue<>(); msgQueue = new ConcurrentLinkedQueue<>();
} }
@ -190,10 +178,8 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
* @return true if connected or false if not * @return true if connected or false if not
*/ */
public boolean close() { public boolean close() {
if (connector != null) {
connector.close(); connector.close();
} lastTimeDownloadRequested = 0;
lastTimeDownloadRequested = null;
downloadRunning = false; downloadRunning = false;
return isConnected(); return isConnected();
} }
@ -202,7 +188,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
* @return true if connected to the Powermax alarm system or false if not * @return true if connected to the Powermax alarm system or false if not
*/ */
public boolean isConnected() { public boolean isConnected() {
return (connector != null) && connector.isConnected(); return connector.isConnected();
} }
/** /**
@ -220,7 +206,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
* @return true if no problem encountered to get all the settings; false if not * @return true if no problem encountered to get all the settings; false if not
*/ */
public boolean processPanelSettings(boolean powerlinkMode) { public boolean processPanelSettings(boolean powerlinkMode) {
return panelSettings.process(powerlinkMode, panelType, powerlinkMode ? syncTimeCheck : null); return panelSettings.process(powerlinkMode, panelType, powerlinkMode ? syncTimeCheck : 0);
} }
/** /**
@ -233,7 +219,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
/** /**
* @return the last message sent to the Powermax alarm system * @return the last message sent to the Powermax alarm system
*/ */
public synchronized PowermaxBaseMessage getLastSendMsg() { public synchronized @Nullable PowermaxBaseMessage getLastSendMsg() {
return lastSendMsg; return lastSendMsg;
} }
@ -261,8 +247,9 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
updateState.lastMessageTime.setValue(System.currentTimeMillis()); updateState.lastMessageTime.setValue(System.currentTimeMillis());
if (updateState.getUpdateSettings() != null) { byte[] buffer = updateState.getUpdateSettings();
panelSettings.updateRawSettings(updateState.getUpdateSettings()); if (buffer != null) {
panelSettings.updateRawSettings(buffer);
} }
if (!updateState.getUpdatedZoneNames().isEmpty()) { if (!updateState.getUpdatedZoneNames().isEmpty()) {
for (Integer zoneIdx : updateState.getUpdatedZoneNames().keySet()) { for (Integer zoneIdx : updateState.getUpdatedZoneNames().keySet()) {
@ -349,7 +336,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
boolean done = false; boolean done = false;
if (!armMode.isAllowedCommand()) { if (!armMode.isAllowedCommand()) {
logger.debug("Powermax alarm binding: requested arm mode {} rejected", armMode.getShortName()); logger.debug("Powermax alarm binding: requested arm mode {} rejected", armMode.getShortName());
} else if ((pinCode == null) || (pinCode.length() != 4)) { } else if (pinCode.length() != 4) {
logger.debug("Powermax alarm binding: requested arm mode {} rejected due to invalid PIN code", logger.debug("Powermax alarm binding: requested arm mode {} rejected due to invalid PIN code",
armMode.getShortName()); armMode.getShortName());
} else { } else {
@ -376,7 +363,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
* *
* @return true if the message was sent or false if not * @return true if the message was sent or false if not
*/ */
public boolean sendPGMX10(Command action, Byte device) { public boolean sendPGMX10(Command action, @Nullable Byte device) {
logger.debug("sendPGMX10(): action = {}, device = {}", action, device); logger.debug("sendPGMX10(): action = {}, device = {}", action, device);
boolean done = false; boolean done = false;
@ -418,7 +405,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
boolean done = false; boolean done = false;
if ((pinCode == null) || (pinCode.length() != 4)) { if (pinCode.length() != 4) {
logger.debug("Powermax alarm binding: zone bypass rejected due to invalid PIN code"); logger.debug("Powermax alarm binding: zone bypass rejected due to invalid PIN code");
} else if ((zone < 1) || (zone > panelSettings.getNbZones())) { } else if ((zone < 1) || (zone > panelSettings.getNbZones())) {
logger.debug("Powermax alarm binding: invalid zone number: {}", zone); logger.debug("Powermax alarm binding: invalid zone number: {}", zone);
@ -483,10 +470,10 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
} else { } else {
logger.info( logger.info(
"Powermax alarm binding: time not synchronized; please correct the date/time of your openHAB server"); "Powermax alarm binding: time not synchronized; please correct the date/time of your openHAB server");
syncTimeCheck = null; syncTimeCheck = 0;
} }
} else { } else {
syncTimeCheck = null; syncTimeCheck = 0;
} }
return done; return done;
} }
@ -503,7 +490,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
boolean done = false; boolean done = false;
if ((pinCode == null) || (pinCode.length() != 4)) { if (pinCode.length() != 4) {
logger.debug("Powermax alarm binding: requested event log rejected due to invalid PIN code"); logger.debug("Powermax alarm binding: requested event log rejected due to invalid PIN code");
} else { } else {
try { try {
@ -543,7 +530,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
public void retryDownloadSetup(int remainingAttempts) { public void retryDownloadSetup(int remainingAttempts) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if ((remainingAttempts > 0) && !isDownloadRunning() && ((lastTimeDownloadRequested == null) if ((remainingAttempts > 0) && !isDownloadRunning() && ((lastTimeDownloadRequested == 0)
|| ((now - lastTimeDownloadRequested) >= DELAY_BETWEEN_SETUP_DOWNLOADS))) { || ((now - lastTimeDownloadRequested) >= DELAY_BETWEEN_SETUP_DOWNLOADS))) {
// We wait at least 45 seconds before each retry to download the panel setup // We wait at least 45 seconds before each retry to download the panel setup
logger.debug("Powermax alarm binding: try again downloading setup"); logger.debug("Powermax alarm binding: try again downloading setup");
@ -569,9 +556,9 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
} }
/** /**
* @return the time in milliseconds the last download of the panel setup was requested or null if not yet requested * @return the time in milliseconds the last download of the panel setup was requested or 0 if not yet requested
*/ */
public Long getLastTimeDownloadRequested() { public long getLastTimeDownloadRequested() {
return lastTimeDownloadRequested; return lastTimeDownloadRequested;
} }
@ -607,7 +594,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
return sendMessage(new PowermaxBaseMessage(msgType), false, waitTime); return sendMessage(new PowermaxBaseMessage(msgType), false, waitTime);
} }
private synchronized boolean sendMessage(PowermaxBaseMessage msg, boolean immediate, int waitTime) { private synchronized boolean sendMessage(@Nullable PowermaxBaseMessage msg, boolean immediate, int waitTime) {
return sendMessage(msg, immediate, waitTime, false); return sendMessage(msg, immediate, waitTime, false);
} }
@ -622,7 +609,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
* @return true if the message was sent or the sending is delayed; false in other cases * @return true if the message was sent or the sending is delayed; false in other cases
*/ */
@SuppressWarnings("PMD.CompareObjectsWithEquals") @SuppressWarnings("PMD.CompareObjectsWithEquals")
private synchronized boolean sendMessage(PowermaxBaseMessage msg, boolean immediate, int waitTime, private synchronized boolean sendMessage(@Nullable PowermaxBaseMessage msg, boolean immediate, int waitTime,
boolean doNotLog) { boolean doNotLog) {
if ((waitTime > 0) && (msg != null)) { if ((waitTime > 0) && (msg != null)) {
logger.debug("sendMessage(): delay ({} s) sending message (type {})", waitTime, msg.getSendType()); logger.debug("sendMessage(): delay ({} s) sending message (type {})", waitTime, msg.getSendType());

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -21,6 +23,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxDeniedMessage extends PowermaxBaseMessage { public class PowermaxDeniedMessage extends PowermaxBaseMessage {
private final Logger logger = LoggerFactory.getLogger(PowermaxDeniedMessage.class); private final Logger logger = LoggerFactory.getLogger(PowermaxDeniedMessage.class);
@ -36,14 +39,15 @@ public class PowermaxDeniedMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }
PowermaxState updatedState = null; PowermaxState updatedState = null;
PowermaxSendType lastSendType = commManager.getLastSendMsg().getSendType(); PowermaxBaseMessage lastSendMsg = commManager.getLastSendMsg();
PowermaxSendType lastSendType = lastSendMsg == null ? null : lastSendMsg.getSendType();
if (lastSendType == PowermaxSendType.EVENTLOG || lastSendType == PowermaxSendType.ARM if (lastSendType == PowermaxSendType.EVENTLOG || lastSendType == PowermaxSendType.ARM
|| lastSendType == PowermaxSendType.BYPASS) { || lastSendType == PowermaxSendType.BYPASS) {
logger.debug("Powermax alarm binding: invalid PIN code"); logger.debug("Powermax alarm binding: invalid PIN code");

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
/** /**
@ -19,6 +21,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxDownloadRetryMessage extends PowermaxBaseMessage { public class PowermaxDownloadRetryMessage extends PowermaxBaseMessage {
/** /**
@ -32,7 +35,7 @@ public class PowermaxDownloadRetryMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings; import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
@ -20,6 +22,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxEventLogMessage extends PowermaxBaseMessage { public class PowermaxEventLogMessage extends PowermaxBaseMessage {
/** /**
@ -33,7 +36,7 @@ public class PowermaxEventLogMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxPanelType; import org.openhab.binding.powermax.internal.state.PowermaxPanelType;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -22,6 +24,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxInfoMessage extends PowermaxBaseMessage { public class PowermaxInfoMessage extends PowermaxBaseMessage {
private final Logger logger = LoggerFactory.getLogger(PowermaxInfoMessage.class); private final Logger logger = LoggerFactory.getLogger(PowermaxInfoMessage.class);
@ -37,7 +40,7 @@ public class PowermaxInfoMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -16,11 +16,14 @@ import static java.util.Map.entry;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Constants used in Powermax messages * Constants used in Powermax messages
* *
* @author Ron Isaacson - Initial contribution * @author Ron Isaacson - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxMessageConstants { public class PowermaxMessageConstants {
private PowermaxMessageConstants() { private PowermaxMessageConstants() {

View File

@ -14,15 +14,18 @@ package org.openhab.binding.powermax.internal.message;
import java.util.EventObject; import java.util.EventObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Event for messages received from the Visonic alarm panel * Event for messages received from the Visonic alarm panel
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxMessageEvent extends EventObject { public class PowermaxMessageEvent extends EventObject {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private PowermaxBaseMessage message; private final PowermaxBaseMessage message;
public PowermaxMessageEvent(Object source, PowermaxBaseMessage message) { public PowermaxMessageEvent(Object source, PowermaxBaseMessage message) {
super(source); super(source);

View File

@ -15,11 +15,14 @@ package org.openhab.binding.powermax.internal.message;
import java.util.EventListener; import java.util.EventListener;
import java.util.EventObject; import java.util.EventObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Powermax Alarm Event Listener interface. Handles incoming Powermax Alarm message events * Powermax Alarm Event Listener interface. Handles incoming Powermax Alarm message events
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public interface PowermaxMessageEventListener extends EventListener { public interface PowermaxMessageEventListener extends EventListener {
/** /**

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants.PowermaxSysEvent; import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants.PowermaxSysEvent;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
@ -20,6 +22,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxPanelMessage extends PowermaxBaseMessage { public class PowermaxPanelMessage extends PowermaxBaseMessage {
/** /**
@ -33,7 +36,7 @@ public class PowermaxPanelMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
/** /**
@ -19,6 +21,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxPowerMasterMessage extends PowermaxBaseMessage { public class PowermaxPowerMasterMessage extends PowermaxBaseMessage {
/** /**
@ -32,7 +35,7 @@ public class PowermaxPowerMasterMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -21,6 +23,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxPowerlinkMessage extends PowermaxBaseMessage { public class PowermaxPowerlinkMessage extends PowermaxBaseMessage {
private final Logger logger = LoggerFactory.getLogger(PowermaxPowerlinkMessage.class); private final Logger logger = LoggerFactory.getLogger(PowermaxPowerlinkMessage.class);
@ -36,7 +39,7 @@ public class PowermaxPowerlinkMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Used to map received messages from the Visonic alarm panel to a ENUM value * Used to map received messages from the Visonic alarm panel to a ENUM value
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public enum PowermaxReceiveType { public enum PowermaxReceiveType {
ACK((byte) 0x02, 0, false), ACK((byte) 0x02, 0, false),
@ -36,9 +39,9 @@ public enum PowermaxReceiveType {
POWERMASTER((byte) 0xB0, 0, true), POWERMASTER((byte) 0xB0, 0, true),
F1((byte) 0xF1, 9, false); F1((byte) 0xF1, 9, false);
private byte code; private final byte code;
private int length; private final int length;
private boolean ackRequired; private final boolean ackRequired;
private PowermaxReceiveType(byte code, int length, boolean ackRequired) { private PowermaxReceiveType(byte code, int length, boolean ackRequired) {
this.code = code; this.code = code;

View File

@ -12,11 +12,15 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/** /**
* Used to map messages to be sent to the Visonic alarm panel to a ENUM value * Used to map messages to be sent to the Visonic alarm panel to a ENUM value
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public enum PowermaxSendType { public enum PowermaxSendType {
INIT(new byte[] { (byte) 0xAB, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 }, null, null), INIT(new byte[] { (byte) 0xAB, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 }, null, null),
@ -107,11 +111,12 @@ public enum PowermaxSendType {
POWERMASTER_ZONE_TYPE1(new byte[] { (byte) 0xB0, 0x01, 0x2D, 0x02, 0x05, 0x00, 0x43 }, null, POWERMASTER_ZONE_TYPE1(new byte[] { (byte) 0xB0, 0x01, 0x2D, 0x02, 0x05, 0x00, 0x43 }, null,
PowermaxReceiveType.POWERMASTER); PowermaxReceiveType.POWERMASTER);
private byte[] message; private final byte[] message;
private Integer paramPosition; private final @Nullable Integer paramPosition;
private PowermaxReceiveType expectedResponseType; private final @Nullable PowermaxReceiveType expectedResponseType;
private PowermaxSendType(byte[] message, Integer paramPosition, PowermaxReceiveType expectedResponseType) { private PowermaxSendType(byte[] message, @Nullable Integer paramPosition,
@Nullable PowermaxReceiveType expectedResponseType) {
this.message = message; this.message = message;
this.paramPosition = paramPosition; this.paramPosition = paramPosition;
this.expectedResponseType = expectedResponseType; this.expectedResponseType = expectedResponseType;
@ -127,14 +132,14 @@ public enum PowermaxSendType {
/** /**
* @return the position of the parameter in the message buffer * @return the position of the parameter in the message buffer
*/ */
public Integer getParamPosition() { public @Nullable Integer getParamPosition() {
return paramPosition; return paramPosition;
} }
/** /**
* @return the ENUM value of the expected message as response * @return the ENUM value of the expected message as response
*/ */
public PowermaxReceiveType getExpectedResponseType() { public @Nullable PowermaxReceiveType getExpectedResponseType() {
return expectedResponseType; return expectedResponseType;
} }
} }

View File

@ -14,6 +14,8 @@ package org.openhab.binding.powermax.internal.message;
import java.util.Arrays; import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -23,6 +25,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxSettingsMessage extends PowermaxBaseMessage { public class PowermaxSettingsMessage extends PowermaxBaseMessage {
private final Logger logger = LoggerFactory.getLogger(PowermaxSettingsMessage.class); private final Logger logger = LoggerFactory.getLogger(PowermaxSettingsMessage.class);
@ -38,7 +41,7 @@ public class PowermaxSettingsMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -16,6 +16,8 @@ import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxArmMode; import org.openhab.binding.powermax.internal.state.PowermaxArmMode;
import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings; import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings;
import org.openhab.binding.powermax.internal.state.PowermaxSensorType; import org.openhab.binding.powermax.internal.state.PowermaxSensorType;
@ -27,6 +29,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxZoneSettings;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxStatusMessage extends PowermaxBaseMessage { public class PowermaxStatusMessage extends PowermaxBaseMessage {
private static byte[] zoneBytes(byte zones1, byte zones9, byte zones17, byte zones25) { private static byte[] zoneBytes(byte zones1, byte zones9, byte zones17, byte zones25) {
@ -68,7 +71,7 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
/** /**
@ -19,6 +21,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxTimeoutMessage extends PowermaxBaseMessage { public class PowermaxTimeoutMessage extends PowermaxBaseMessage {
/** /**
@ -32,7 +35,7 @@ public class PowermaxTimeoutMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager != null) { if (commManager != null) {
commManager.sendMessage(PowermaxSendType.EXIT); commManager.sendMessage(PowermaxSendType.EXIT);
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
/** /**
@ -19,6 +21,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxZonesNameMessage extends PowermaxBaseMessage { public class PowermaxZonesNameMessage extends PowermaxBaseMessage {
/** /**
@ -32,7 +35,7 @@ public class PowermaxZonesNameMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.powermax.internal.message; package org.openhab.binding.powermax.internal.message;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.state.PowermaxState; import org.openhab.binding.powermax.internal.state.PowermaxState;
/** /**
@ -19,6 +21,7 @@ import org.openhab.binding.powermax.internal.state.PowermaxState;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxZonesTypeMessage extends PowermaxBaseMessage { public class PowermaxZonesTypeMessage extends PowermaxBaseMessage {
/** /**
@ -32,7 +35,7 @@ public class PowermaxZonesTypeMessage extends PowermaxBaseMessage {
} }
@Override @Override
protected PowermaxState handleMessageInternal(PowermaxCommManager commManager) { protected @Nullable PowermaxState handleMessageInternal(@Nullable PowermaxCommManager commManager) {
if (commManager == null) { if (commManager == null) {
return null; return null;
} }

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.powermax.internal.state; package org.openhab.binding.powermax.internal.state;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* All defined sensor types for Master panels * All defined sensor types for Master panels
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public enum PowermasterSensorType { public enum PowermasterSensorType {
SENSOR_TYPE_1((byte) 0x01, "Motion"), SENSOR_TYPE_1((byte) 0x01, "Motion"),
@ -26,8 +29,8 @@ public enum PowermasterSensorType {
SENSOR_TYPE_5((byte) 0x2A, "Magnet"), SENSOR_TYPE_5((byte) 0x2A, "Magnet"),
SENSOR_TYPE_6((byte) 0xFE, "Wired"); SENSOR_TYPE_6((byte) 0xFE, "Wired");
private byte code; private final byte code;
private String label; private final String label;
private PowermasterSensorType(byte code, String label) { private PowermasterSensorType(byte code, String label) {
this.code = code; this.code = code;

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.powermax.internal.state; package org.openhab.binding.powermax.internal.state;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* All defined arm modes * All defined arm modes
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public enum PowermaxArmMode { public enum PowermaxArmMode {
DISARMED(0, "Disarmed", "Disarmed", false, (byte) 0x00, false), DISARMED(0, "Disarmed", "Disarmed", false, (byte) 0x00, false),
@ -42,11 +45,11 @@ public enum PowermaxArmMode {
ARMED_HOME_INSTANT(20, "Armed Home Instant", "StayInstant", true, (byte) 0x14, false), ARMED_HOME_INSTANT(20, "Armed Home Instant", "StayInstant", true, (byte) 0x14, false),
ARMED_AWAY_INSTANT(21, "Armed Away Instant", "ArmedInstant", true, (byte) 0x15, false); ARMED_AWAY_INSTANT(21, "Armed Away Instant", "ArmedInstant", true, (byte) 0x15, false);
private int code; private final int code;
private String name; private final String name;
private String shortName; private final String shortName;
private boolean armed; private final boolean armed;
private byte commandCode; private final byte commandCode;
private boolean allowedCommand; private boolean allowedCommand;
private PowermaxArmMode(int code, String name, String shortName, boolean armed, byte commandCode, private PowermaxArmMode(int code, String name, String shortName, boolean armed, byte commandCode,

View File

@ -18,6 +18,8 @@ import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants; import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants;
import org.openhab.binding.powermax.internal.message.PowermaxSendType; import org.openhab.binding.powermax.internal.message.PowermaxSendType;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -28,13 +30,14 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxPanelSettings { public class PowermaxPanelSettings {
private final Logger logger = LoggerFactory.getLogger(PowermaxPanelSettings.class);
/** Number of PGM and X10 devices managed by the system */ /** Number of PGM and X10 devices managed by the system */
private static final int NB_PGM_X10_DEVICES = 16; private static final int NB_PGM_X10_DEVICES = 16;
private final Logger logger = LoggerFactory.getLogger(PowermaxPanelSettings.class);
/** Raw buffers for settings */ /** Raw buffers for settings */
private Byte[][] rawSettings; private Byte[][] rawSettings;
@ -45,15 +48,15 @@ public class PowermaxPanelSettings {
private boolean quickArm; private boolean quickArm;
private boolean bypassEnabled; private boolean bypassEnabled;
private boolean partitionsEnabled; private boolean partitionsEnabled;
private String[] pinCodes; private String @Nullable [] pinCodes;
private String panelEprom; private @Nullable String panelEprom;
private String panelSoftware; private @Nullable String panelSoftware;
private String panelSerial; private @Nullable String panelSerial;
private PowermaxZoneSettings[] zoneSettings; private PowermaxZoneSettings[] zoneSettings;
private PowermaxX10Settings[] x10Settings; private PowermaxX10Settings[] x10Settings;
private boolean[] keypad1wEnrolled; private boolean @Nullable [] keypad1wEnrolled;
private boolean[] keypad2wEnrolled; private boolean @Nullable [] keypad2wEnrolled;
private boolean[] sirensEnrolled; private boolean @Nullable [] sirensEnrolled;
/** /**
* Constructor * Constructor
@ -108,21 +111,21 @@ public class PowermaxPanelSettings {
/** /**
* @return the panel EEPROM version * @return the panel EEPROM version
*/ */
public String getPanelEprom() { public @Nullable String getPanelEprom() {
return panelEprom; return panelEprom;
} }
/** /**
* @return the panel software version * @return the panel software version
*/ */
public String getPanelSoftware() { public @Nullable String getPanelSoftware() {
return panelSoftware; return panelSoftware;
} }
/** /**
* @return the panel serial ID * @return the panel serial ID
*/ */
public String getPanelSerial() { public @Nullable String getPanelSerial() {
return panelSerial; return panelSerial;
} }
@ -147,7 +150,7 @@ public class PowermaxPanelSettings {
* *
* @return the settings of the zone * @return the settings of the zone
*/ */
public PowermaxZoneSettings getZoneSettings(int zone) { public @Nullable PowermaxZoneSettings getZoneSettings(int zone) {
return ((zone < 1) || (zone > zoneSettings.length)) ? null : zoneSettings[zone - 1]; return ((zone < 1) || (zone > zoneSettings.length)) ? null : zoneSettings[zone - 1];
} }
@ -158,7 +161,7 @@ public class PowermaxPanelSettings {
* *
* @return the name of the zone * @return the name of the zone
*/ */
public String getZoneName(int zone) { public @Nullable String getZoneName(int zone) {
PowermaxZoneSettings zoneSettings = getZoneSettings(zone); PowermaxZoneSettings zoneSettings = getZoneSettings(zone);
return (zoneSettings == null) ? null : zoneSettings.getName(); return (zoneSettings == null) ? null : zoneSettings.getName();
} }
@ -204,7 +207,7 @@ public class PowermaxPanelSettings {
* *
* @return the settings of the X10 device * @return the settings of the X10 device
*/ */
public PowermaxX10Settings getX10Settings(int idx) { public @Nullable PowermaxX10Settings getX10Settings(int idx) {
return ((idx < 1) || (idx >= x10Settings.length)) ? null : x10Settings[idx]; return ((idx < 1) || (idx >= x10Settings.length)) ? null : x10Settings[idx];
} }
@ -214,8 +217,9 @@ public class PowermaxPanelSettings {
* @return true if the 1 way keypad is enrolled; false if not * @return true if the 1 way keypad is enrolled; false if not
*/ */
public boolean isKeypad1wEnrolled(int idx) { public boolean isKeypad1wEnrolled(int idx) {
return ((keypad1wEnrolled == null) || (idx < 1) || (idx >= keypad1wEnrolled.length)) ? false boolean @Nullable [] localKeypad1wEnrolled = keypad1wEnrolled;
: keypad1wEnrolled[idx - 1]; return ((localKeypad1wEnrolled == null) || (idx < 1) || (idx >= localKeypad1wEnrolled.length)) ? false
: localKeypad1wEnrolled[idx - 1];
} }
/** /**
@ -224,8 +228,9 @@ public class PowermaxPanelSettings {
* @return true if the 2 way keypad is enrolled; false if not * @return true if the 2 way keypad is enrolled; false if not
*/ */
public boolean isKeypad2wEnrolled(int idx) { public boolean isKeypad2wEnrolled(int idx) {
return ((keypad2wEnrolled == null) || (idx < 1) || (idx >= keypad2wEnrolled.length)) ? false boolean @Nullable [] localKeypad2wEnrolled = keypad2wEnrolled;
: keypad2wEnrolled[idx - 1]; return ((localKeypad2wEnrolled == null) || (idx < 1) || (idx >= localKeypad2wEnrolled.length)) ? false
: localKeypad2wEnrolled[idx - 1];
} }
/** /**
@ -234,19 +239,21 @@ public class PowermaxPanelSettings {
* @return true if the siren is enrolled; false if not * @return true if the siren is enrolled; false if not
*/ */
public boolean isSirenEnrolled(int idx) { public boolean isSirenEnrolled(int idx) {
return ((sirensEnrolled == null) || (idx < 1) || (idx >= sirensEnrolled.length)) ? false boolean @Nullable [] localSirensEnrolled = sirensEnrolled;
: sirensEnrolled[idx - 1]; return ((localSirensEnrolled == null) || (idx < 1) || (idx >= localSirensEnrolled.length)) ? false
: localSirensEnrolled[idx - 1];
} }
/** /**
* @return the PIN code of the first user of null if unknown (standard mode) * @return the PIN code of the first user of an empty string if unknown (standard mode)
*/ */
public String getFirstPinCode() { public String getFirstPinCode() {
return (pinCodes == null) ? null : pinCodes[0]; String @Nullable [] localPinCodes = pinCodes;
return (localPinCodes == null || localPinCodes.length == 0) ? "" : localPinCodes[0];
} }
public void updateRawSettings(byte[] data) { public void updateRawSettings(byte[] data) {
if ((data == null) || (data.length < 3)) { if (data.length < 3) {
return; return;
} }
int start = 0; int start = 0;
@ -276,14 +283,14 @@ public class PowermaxPanelSettings {
} }
} }
private byte[] readSettings(PowermaxSendType msgType, int start, int end) { private byte @Nullable [] readSettings(PowermaxSendType msgType, int start, int end) {
byte[] message = msgType.getMessage(); byte[] message = msgType.getMessage();
int page = message[2] & 0x000000FF; int page = message[2] & 0x000000FF;
int index = message[1] & 0x000000FF; int index = message[1] & 0x000000FF;
return readSettings(page, index + start, index + end); return readSettings(page, index + start, index + end);
} }
private byte[] readSettings(int page, int start, int end) { private byte @Nullable [] readSettings(int page, int start, int end) {
int pageMin = page + start / 0x100; int pageMin = page + start / 0x100;
int indexPageMin = start % 0x100; int indexPageMin = start % 0x100;
int pageMax = page + end / 0x100; int pageMax = page + end / 0x100;
@ -332,7 +339,7 @@ public class PowermaxPanelSettings {
return result; return result;
} }
private String readSettingsAsString(PowermaxSendType msgType, int start, int end) { private @Nullable String readSettingsAsString(PowermaxSendType msgType, int start, int end) {
byte[] message = msgType.getMessage(); byte[] message = msgType.getMessage();
int page = message[2] & 0x000000FF; int page = message[2] & 0x000000FF;
int index = message[1] & 0x000000FF; int index = message[1] & 0x000000FF;
@ -381,12 +388,11 @@ public class PowermaxPanelSettings {
* *
* @param PowerlinkMode true if in Powerlink mode or false if in standard mode * @param PowerlinkMode true if in Powerlink mode or false if in standard mode
* @param defaultPanelType the default panel type to consider if not found in the raw buffers * @param defaultPanelType the default panel type to consider if not found in the raw buffers
* @param timeSet the time in milliseconds used to set time and date; null if no sync time requested * @param timeSet the time in milliseconds used to set time and date; 0 if no sync time requested
* *
* @return true if no problem encountered to get all the settings; false if not * @return true if no problem encountered to get all the settings; false if not
*/ */
@SuppressWarnings("null") public boolean process(boolean PowerlinkMode, PowermaxPanelType defaultPanelType, long timeSet) {
public boolean process(boolean PowerlinkMode, PowermaxPanelType defaultPanelType, Long timeSet) {
logger.debug("Process settings Powerlink {}", PowerlinkMode); logger.debug("Process settings Powerlink {}", PowerlinkMode);
boolean result = true; boolean result = true;
@ -424,15 +430,15 @@ public class PowermaxPanelSettings {
quickArm = false; quickArm = false;
bypassEnabled = false; bypassEnabled = false;
partitionsEnabled = false; partitionsEnabled = false;
pinCodes = new String[userCnt]; String[] localPinCodes = new String[userCnt];
panelEprom = null; panelEprom = null;
panelSoftware = null; panelSoftware = null;
panelSerial = null; panelSerial = null;
zoneSettings = new PowermaxZoneSettings[zoneCnt]; zoneSettings = new PowermaxZoneSettings[zoneCnt];
x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES]; x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES];
keypad1wEnrolled = new boolean[keypad1wCnt]; boolean[] localKeypad1wEnrolled = new boolean[keypad1wCnt];
keypad2wEnrolled = new boolean[keypad2wCnt]; boolean[] localKeypad2wEnrolled = new boolean[keypad2wCnt];
sirensEnrolled = new boolean[sirenCnt]; boolean[] localSirensEnrolled = new boolean[sirenCnt];
if (PowerlinkMode) { if (PowerlinkMode) {
// Check time and date // Check time and date
@ -453,7 +459,7 @@ public class PowermaxPanelSettings {
cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND))); cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)));
// Check if time sync was OK // Check if time sync was OK
if (timeSet != null) { if (timeSet > 0) {
long delta = (timeRead - timeSet) / 1000; long delta = (timeRead - timeSet) / 1000;
if (delta <= 5) { if (delta <= 5) {
logger.debug("Powermax alarm binding: time sync OK (delta {} s)", delta); logger.debug("Powermax alarm binding: time sync OK (delta {} s)", delta);
@ -528,7 +534,8 @@ public class PowermaxPanelSettings {
2 * userCnt - 1); 2 * userCnt - 1);
if (data != null) { if (data != null) {
for (int i = 0; i < userCnt; i++) { for (int i = 0; i < userCnt; i++) {
pinCodes[i] = String.format("%02X%02X", data[i * 2] & 0x000000FF, data[i * 2 + 1] & 0x000000FF); localPinCodes[i] = String.format("%02X%02X", data[i * 2] & 0x000000FF,
data[i * 2 + 1] & 0x000000FF);
} }
} else { } else {
logger.debug("Cannot get PIN codes"); logger.debug("Cannot get PIN codes");
@ -606,22 +613,26 @@ public class PowermaxPanelSettings {
boolean zoneEnrolled; boolean zoneEnrolled;
byte zoneInfo; byte zoneInfo;
byte sensorTypeCode;
String sensorTypeStr; String sensorTypeStr;
if (panelType.isPowerMaster()) { if (panelType.isPowerMaster()) {
zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(dataMr, i * 10 + 4, i * 10 + 9), zero5);
zoneInfo = data[i]; zoneInfo = data[i];
sensorTypeCode = dataMr[i * 10 + 5]; if (dataMr != null) {
zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(dataMr, i * 10 + 4, i * 10 + 9), zero5);
byte sensorTypeCode = dataMr[i * 10 + 5];
try { try {
PowermasterSensorType sensorType = PowermasterSensorType.fromCode(sensorTypeCode); PowermasterSensorType sensorType = PowermasterSensorType.fromCode(sensorTypeCode);
sensorTypeStr = sensorType.getLabel(); sensorTypeStr = sensorType.getLabel();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
sensorTypeStr = null; sensorTypeStr = null;
} }
} else {
zoneEnrolled = false;
sensorTypeStr = null;
}
} else { } else {
zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3); zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
zoneInfo = data[i * 4 + 3]; zoneInfo = data[i * 4 + 3];
sensorTypeCode = data[i * 4 + 2]; byte sensorTypeCode = data[i * 4 + 2];
try { try {
PowermaxSensorType sensorType = PowermaxSensorType PowermaxSensorType sensorType = PowermaxSensorType
.fromCode((byte) (sensorTypeCode & 0x0000000F)); .fromCode((byte) (sensorTypeCode & 0x0000000F));
@ -686,7 +697,8 @@ public class PowermaxPanelSettings {
byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 }; byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
for (int i = 0; i < keypad2wCnt; i++) { for (int i = 0; i < keypad2wCnt; i++) {
keypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9), zero5); localKeypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9),
zero5);
} }
} else { } else {
logger.debug("Cannot get 2 way keypad settings"); logger.debug("Cannot get 2 way keypad settings");
@ -698,7 +710,8 @@ public class PowermaxPanelSettings {
byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 }; byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
for (int i = 0; i < sirenCnt; i++) { for (int i = 0; i < sirenCnt; i++) {
sirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9), zero5); localSirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9),
zero5);
} }
} else { } else {
logger.debug("Cannot get siren settings"); logger.debug("Cannot get siren settings");
@ -711,7 +724,7 @@ public class PowermaxPanelSettings {
byte[] zero2 = new byte[] { 0, 0 }; byte[] zero2 = new byte[] { 0, 0 };
for (int i = 0; i < keypad1wCnt; i++) { for (int i = 0; i < keypad1wCnt; i++) {
keypad1wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 2), zero2); localKeypad1wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 2), zero2);
} }
} else { } else {
logger.debug("Cannot get 1 way keypad settings"); logger.debug("Cannot get 1 way keypad settings");
@ -723,7 +736,7 @@ public class PowermaxPanelSettings {
byte[] zero3 = new byte[] { 0, 0, 0 }; byte[] zero3 = new byte[] { 0, 0, 0 };
for (int i = 0; i < keypad2wCnt; i++) { for (int i = 0; i < keypad2wCnt; i++) {
keypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3); localKeypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
} }
} else { } else {
logger.debug("Cannot get 2 way keypad settings"); logger.debug("Cannot get 2 way keypad settings");
@ -735,7 +748,7 @@ public class PowermaxPanelSettings {
byte[] zero3 = new byte[] { 0, 0, 0 }; byte[] zero3 = new byte[] { 0, 0, 0 };
for (int i = 0; i < sirenCnt; i++) { for (int i = 0; i < sirenCnt; i++) {
sirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3); localSirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
} }
} else { } else {
logger.debug("Cannot get siren settings"); logger.debug("Cannot get siren settings");
@ -758,6 +771,11 @@ public class PowermaxPanelSettings {
} }
} }
pinCodes = localPinCodes;
keypad1wEnrolled = localKeypad1wEnrolled;
keypad2wEnrolled = localKeypad2wEnrolled;
sirensEnrolled = localSirensEnrolled;
return result; return result;
} }

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.powermax.internal.state; package org.openhab.binding.powermax.internal.state;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Used to store main characteristics of each Visonic alarm panel type in an ENUM * Used to store main characteristics of each Visonic alarm panel type in an ENUM
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public enum PowermaxPanelType { public enum PowermaxPanelType {
POWERMAX((byte) 0, "PowerMax", 1, 250, 8, 8, 2, 2, 8, 0, 28, 2, 0), POWERMAX((byte) 0, "PowerMax", 1, 250, 8, 8, 2, 2, 8, 0, 28, 2, 0),
@ -29,19 +32,19 @@ public enum PowermaxPanelType {
POWERMASTER_10((byte) 7, "PowerMaster10", 3, 250, 8, 0, 8, 4, 8, 8, 29, 1, 5), POWERMASTER_10((byte) 7, "PowerMaster10", 3, 250, 8, 0, 8, 4, 8, 8, 29, 1, 5),
POWERMASTER_30((byte) 8, "PowerMaster30", 3, 1000, 32, 0, 32, 8, 48, 32, 62, 2, 5); POWERMASTER_30((byte) 8, "PowerMaster30", 3, 1000, 32, 0, 32, 8, 48, 32, 62, 2, 5);
private byte code; private final byte code;
private String label; private final String label;
private int partitions; private final int partitions;
private int events; private final int events;
private int keyfobs; private final int keyfobs;
private int keypads1w; private final int keypads1w;
private int keypads2w; private final int keypads2w;
private int sirens; private final int sirens;
private int userCodes; private final int userCodes;
private int prontags; private final int prontags;
private int wireless; private final int wireless;
private int wired; private final int wired;
private int customZones; private final int customZones;
private PowermaxPanelType(byte code, String label, int partitions, int events, int keyfobs, int keypads1w, private PowermaxPanelType(byte code, String label, int partitions, int events, int keyfobs, int keypads1w,
int keypads2w, int sirens, int userCodes, int prontags, int wireless, int wired, int customZones) { int keypads2w, int sirens, int userCodes, int prontags, int wireless, int wired, int customZones) {

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.powermax.internal.state; package org.openhab.binding.powermax.internal.state;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* All defined sensor types for all panels except Master panels * All defined sensor types for all panels except Master panels
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public enum PowermaxSensorType { public enum PowermaxSensorType {
MOTION_SENSOR_1((byte) 0x03, "Motion"), MOTION_SENSOR_1((byte) 0x03, "Motion"),
@ -29,8 +32,8 @@ public enum PowermaxSensorType {
MOTION_SENSOR_3((byte) 0x0C, "Motion"), MOTION_SENSOR_3((byte) 0x0C, "Motion"),
WIRED_SENSOR((byte) 0x0F, "Wired"); WIRED_SENSOR((byte) 0x0F, "Wired");
private byte code; private final byte code;
private String label; private final String label;
private PowermaxSensorType(byte code, String label) { private PowermaxSensorType(byte code, String label) {
this.code = code; this.code = code;

View File

@ -19,11 +19,13 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants; import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants;
import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.StringType;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -32,6 +34,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxState extends PowermaxStateContainer { public class PowermaxState extends PowermaxStateContainer {
private final Logger logger = LoggerFactory.getLogger(PowermaxState.class); private final Logger logger = LoggerFactory.getLogger(PowermaxState.class);
@ -58,19 +61,31 @@ public class PowermaxState extends PowermaxStateContainer {
public DynamicValue<Boolean> isArmed = new DynamicValue<>(this, SYSTEM_ARMED, () -> { public DynamicValue<Boolean> isArmed = new DynamicValue<>(this, SYSTEM_ARMED, () -> {
return isArmed(); return isArmed();
}, () -> { }, () -> {
return isArmed() ? OnOffType.ON : OnOffType.OFF; Boolean isArmed = isArmed();
if (isArmed == null) {
return UnDefType.NULL;
}
return isArmed ? OnOffType.ON : OnOffType.OFF;
}); });
public DynamicValue<String> panelMode = new DynamicValue<>(this, MODE, () -> { public DynamicValue<String> panelMode = new DynamicValue<>(this, MODE, () -> {
return getPanelMode(); return getPanelMode();
}, () -> { }, () -> {
return new StringType(getPanelMode()); String mode = getPanelMode();
if (mode == null) {
return UnDefType.NULL;
}
return new StringType(mode);
}); });
public DynamicValue<String> shortArmMode = new DynamicValue<>(this, ARM_MODE, () -> { public DynamicValue<String> shortArmMode = new DynamicValue<>(this, ARM_MODE, () -> {
return getShortArmMode(); return getShortArmMode();
}, () -> { }, () -> {
return new StringType(getShortArmMode()); String mode = getShortArmMode();
if (mode == null) {
return UnDefType.NULL;
}
return new StringType(mode);
}); });
public DynamicValue<String> activeAlerts = new DynamicValue<>(this, ACTIVE_ALERTS, () -> { public DynamicValue<String> activeAlerts = new DynamicValue<>(this, ACTIVE_ALERTS, () -> {
@ -82,14 +97,18 @@ public class PowermaxState extends PowermaxStateContainer {
public DynamicValue<Boolean> pgmStatus = new DynamicValue<>(this, PGM_STATUS, () -> { public DynamicValue<Boolean> pgmStatus = new DynamicValue<>(this, PGM_STATUS, () -> {
return getPGMX10DeviceStatus(0); return getPGMX10DeviceStatus(0);
}, () -> { }, () -> {
return getPGMX10DeviceStatus(0) ? OnOffType.ON : OnOffType.OFF; Boolean status = getPGMX10DeviceStatus(0);
if (status == null) {
return UnDefType.NULL;
}
return status ? OnOffType.ON : OnOffType.OFF;
}); });
private PowermaxPanelSettings panelSettings; private PowermaxPanelSettings panelSettings;
private PowermaxZoneState[] zones; private PowermaxZoneState[] zones;
private Boolean[] pgmX10DevicesStatus; private Boolean[] pgmX10DevicesStatus;
private byte[] updateSettings; private byte @Nullable [] updateSettings;
private String[] eventLog; private String @Nullable [] eventLog;
private Map<Integer, Byte> updatedZoneNames; private Map<Integer, Byte> updatedZoneNames;
private Map<Integer, Integer> updatedZoneInfos; private Map<Integer, Integer> updatedZoneInfos;
private List<PowermaxActiveAlert> activeAlertList; private List<PowermaxActiveAlert> activeAlertList;
@ -163,7 +182,7 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return the status (true or false) * @return the status (true or false)
*/ */
public Boolean getPGMX10DeviceStatus(int device) { public @Nullable Boolean getPGMX10DeviceStatus(int device) {
return ((device < 0) || (device >= pgmX10DevicesStatus.length)) ? null : pgmX10DevicesStatus[device]; return ((device < 0) || (device >= pgmX10DevicesStatus.length)) ? null : pgmX10DevicesStatus[device];
} }
@ -173,7 +192,7 @@ public class PowermaxState extends PowermaxStateContainer {
* @param device the index of the PGM/X10 device (0 s for PGM; for X10 device is index 1) * @param device the index of the PGM/X10 device (0 s for PGM; for X10 device is index 1)
* @param status true or false * @param status true or false
*/ */
public void setPGMX10DeviceStatus(int device, Boolean status) { public void setPGMX10DeviceStatus(int device, @Nullable Boolean status) {
if ((device >= 0) && (device < pgmX10DevicesStatus.length)) { if ((device >= 0) && (device < pgmX10DevicesStatus.length)) {
this.pgmX10DevicesStatus[device] = status; this.pgmX10DevicesStatus[device] = status;
} }
@ -184,7 +203,7 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return the raw buffer as a table of bytes * @return the raw buffer as a table of bytes
*/ */
public byte[] getUpdateSettings() { public byte @Nullable [] getUpdateSettings() {
return updateSettings; return updateSettings;
} }
@ -203,7 +222,8 @@ public class PowermaxState extends PowermaxStateContainer {
* @return the number of entries * @return the number of entries
*/ */
public int getEventLogSize() { public int getEventLogSize() {
return (eventLog == null) ? 0 : eventLog.length; String @Nullable [] localEventLog = eventLog;
return (localEventLog == null) ? 0 : localEventLog.length;
} }
/** /**
@ -222,8 +242,10 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return the entry value (event) * @return the entry value (event)
*/ */
public String getEventLog(int index) { public @Nullable String getEventLog(int index) {
return ((index < 1) || (index > getEventLogSize())) ? null : eventLog[index - 1]; String @Nullable [] localEventLog = eventLog;
return ((localEventLog == null) || (index < 1) || (index > getEventLogSize())) ? null
: localEventLog[index - 1];
} }
/** /**
@ -233,8 +255,9 @@ public class PowermaxState extends PowermaxStateContainer {
* @param event the entry value (event) * @param event the entry value (event)
*/ */
public void setEventLog(int index, String event) { public void setEventLog(int index, String event) {
if ((index >= 1) && (index <= getEventLogSize())) { String @Nullable [] localEventLog = eventLog;
this.eventLog[index - 1] = event; if ((localEventLog != null) && (index >= 1) && (index <= getEventLogSize())) {
localEventLog[index - 1] = event;
} }
} }
@ -329,7 +352,7 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return either Download or Powerlink or Standard * @return either Download or Powerlink or Standard
*/ */
public String getPanelMode() { public @Nullable String getPanelMode() {
String mode = null; String mode = null;
if (Boolean.TRUE.equals(downloadMode.getValue())) { if (Boolean.TRUE.equals(downloadMode.getValue())) {
mode = "Download"; mode = "Download";
@ -346,7 +369,7 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return true or false * @return true or false
*/ */
public Boolean isArmed() { public @Nullable Boolean isArmed() {
return isArmed(armMode.getValue()); return isArmed(armMode.getValue());
} }
@ -357,7 +380,7 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return true or false; null if mode is unexpected * @return true or false; null if mode is unexpected
*/ */
private static Boolean isArmed(String armMode) { private static @Nullable Boolean isArmed(@Nullable String armMode) {
Boolean result = null; Boolean result = null;
if (armMode != null) { if (armMode != null) {
try { try {
@ -375,7 +398,7 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return the short description * @return the short description
*/ */
public String getShortArmMode() { public @Nullable String getShortArmMode() {
return getShortArmMode(armMode.getValue()); return getShortArmMode(armMode.getValue());
} }
@ -386,7 +409,7 @@ public class PowermaxState extends PowermaxStateContainer {
* *
* @return the short name or null if mode is unexpected * @return the short name or null if mode is unexpected
*/ */
private static String getShortArmMode(String armMode) { private static @Nullable String getShortArmMode(@Nullable String armMode) {
String result = null; String result = null;
if (armMode != null) { if (armMode != null) {
try { try {
@ -420,8 +443,8 @@ public class PowermaxState extends PowermaxStateContainer {
} }
for (int i = 0; i < pgmX10DevicesStatus.length; i++) { for (int i = 0; i < pgmX10DevicesStatus.length; i++) {
if ((getPGMX10DeviceStatus(i) != null) Boolean status = getPGMX10DeviceStatus(i);
&& getPGMX10DeviceStatus(i).equals(otherState.getPGMX10DeviceStatus(i))) { if ((status != null) && status.equals(otherState.getPGMX10DeviceStatus(i))) {
setPGMX10DeviceStatus(i, null); setPGMX10DeviceStatus(i, null);
} }
} }
@ -462,8 +485,9 @@ public class PowermaxState extends PowermaxStateContainer {
} }
for (int i = 0; i < pgmX10DevicesStatus.length; i++) { for (int i = 0; i < pgmX10DevicesStatus.length; i++) {
if (update.getPGMX10DeviceStatus(i) != null) { Boolean status = update.getPGMX10DeviceStatus(i);
setPGMX10DeviceStatus(i, update.getPGMX10DeviceStatus(i)); if (status != null) {
setPGMX10DeviceStatus(i, status);
} }
} }
@ -480,8 +504,9 @@ public class PowermaxState extends PowermaxStateContainer {
setEventLogSize(update.getEventLogSize()); setEventLogSize(update.getEventLogSize());
} }
for (int i = 1; i <= getEventLogSize(); i++) { for (int i = 1; i <= getEventLogSize(); i++) {
if (update.getEventLog(i) != null) { String log = update.getEventLog(i);
setEventLog(i, update.getEventLog(i)); if (log != null) {
setEventLog(i, log);
} }
} }
@ -495,7 +520,7 @@ public class PowermaxState extends PowermaxStateContainer {
String str = "Bridge state:"; String str = "Bridge state:";
for (Value<?> value : getValues()) { for (Value<?> value : getValues()) {
if ((value.getChannel() != null) && (value.getValue() != null)) { if (value.getValue() != null) {
String channel = value.getChannel(); String channel = value.getChannel();
String vStr = value.getValue().toString(); String vStr = value.getValue().toString();
String state = value.getState().toString(); String state = value.getState().toString();
@ -508,15 +533,16 @@ public class PowermaxState extends PowermaxStateContainer {
} }
for (int i = 0; i < pgmX10DevicesStatus.length; i++) { for (int i = 0; i < pgmX10DevicesStatus.length; i++) {
if (getPGMX10DeviceStatus(i) != null) { Boolean status = getPGMX10DeviceStatus(i);
if (status != null) {
str += String.format("\n - %s status = %s", (i == 0) ? "PGM device" : String.format("X10 device %d", i), str += String.format("\n - %s status = %s", (i == 0) ? "PGM device" : String.format("X10 device %d", i),
getPGMX10DeviceStatus(i) ? "ON" : "OFF"); status ? "ON" : "OFF");
} }
} }
for (int i = 1; i <= zones.length; i++) { for (int i = 1; i <= zones.length; i++) {
for (Value<?> value : zones[i - 1].getValues()) { for (Value<?> value : zones[i - 1].getValues()) {
if ((value.getChannel() != null) && (value.getValue() != null)) { if (value.getValue() != null) {
String channel = value.getChannel(); String channel = value.getChannel();
String vStr = value.getValue().toString(); String vStr = value.getValue().toString();
String state = value.getState().toString(); String state = value.getState().toString();
@ -530,13 +556,13 @@ public class PowermaxState extends PowermaxStateContainer {
} }
for (int i = 1; i <= getEventLogSize(); i++) { for (int i = 1; i <= getEventLogSize(); i++) {
if (getEventLog(i) != null) { String log = getEventLog(i);
str += "\n - event log " + i + " = " + getEventLog(i); if (log != null) {
str += "\n - event log " + i + " = " + log;
} }
} }
String alarms = getActiveAlerts(); str += "\n - active alarms/alerts = " + getActiveAlerts();
str += "\n - active alarms/alerts = " + (alarms == null ? "null" : alarms);
return str; return str;
} }

View File

@ -18,18 +18,21 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State; import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/** /**
* Base class for extensible state objects * Base class for extensible state objects
* *
* @author Ron Isaacson - Initial contribution * @author Ron Isaacson - Initial contribution
*/ */
@NonNullByDefault
public abstract class PowermaxStateContainer { public abstract class PowermaxStateContainer {
protected final TimeZoneProvider timeZoneProvider; protected final TimeZoneProvider timeZoneProvider;
@ -46,7 +49,7 @@ public abstract class PowermaxStateContainer {
parent.getValues().add(this); parent.getValues().add(this);
} }
public T getValue() { public @Nullable T getValue() {
return value; return value;
} }
@ -67,10 +70,10 @@ public abstract class PowermaxStateContainer {
} }
public class DynamicValue<T> extends Value<T> { public class DynamicValue<T> extends Value<T> {
Supplier<T> valueFunction; Supplier<@Nullable T> valueFunction;
Supplier<State> stateFunction; Supplier<State> stateFunction;
public DynamicValue(PowermaxStateContainer parent, String channel, Supplier<T> valueFunction, public DynamicValue(PowermaxStateContainer parent, String channel, Supplier<@Nullable T> valueFunction,
Supplier<State> stateFunction) { Supplier<State> stateFunction) {
super(parent, channel); super(parent, channel);
this.valueFunction = valueFunction; this.valueFunction = valueFunction;
@ -80,7 +83,7 @@ public abstract class PowermaxStateContainer {
// Note: setValue() is still valid, but the saved value will be ignored // Note: setValue() is still valid, but the saved value will be ignored
@Override @Override
public T getValue() { public @Nullable T getValue() {
return valueFunction.get(); return valueFunction.get();
} }
@ -106,7 +109,8 @@ public abstract class PowermaxStateContainer {
@Override @Override
public State getState() { public State getState() {
return value ? trueState : falseState; Boolean val = value;
return val == null ? UnDefType.NULL : (val ? trueState : falseState);
} }
} }
@ -117,7 +121,8 @@ public abstract class PowermaxStateContainer {
@Override @Override
public State getState() { public State getState() {
return new StringType(value); String val = value;
return val == null ? UnDefType.NULL : new StringType(val);
} }
} }
@ -128,7 +133,11 @@ public abstract class PowermaxStateContainer {
@Override @Override
public State getState() { public State getState() {
ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochMilli(value), timeZoneProvider.getTimeZone()); Long val = value;
if (val == null) {
return UnDefType.NULL;
}
ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochMilli(val), timeZoneProvider.getTimeZone());
return new DateTimeType(zoned); return new DateTimeType(zoned);
} }
} }

View File

@ -14,15 +14,18 @@ package org.openhab.binding.powermax.internal.state;
import java.util.EventObject; import java.util.EventObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Event for state received from the Visonic alarm panel * Event for state received from the Visonic alarm panel
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxStateEvent extends EventObject { public class PowermaxStateEvent extends EventObject {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private PowermaxState state; private final PowermaxState state;
public PowermaxStateEvent(Object source, PowermaxState state) { public PowermaxStateEvent(Object source, PowermaxState state) {
super(source); super(source);

View File

@ -15,11 +15,14 @@ package org.openhab.binding.powermax.internal.state;
import java.util.EventListener; import java.util.EventListener;
import java.util.EventObject; import java.util.EventObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Powermax Alarm state Listener interface. Handles Powermax Alarm state events * Powermax Alarm state Listener interface. Handles Powermax Alarm state events
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public interface PowermaxStateEventListener extends EventListener { public interface PowermaxStateEventListener extends EventListener {
/** /**

View File

@ -12,17 +12,21 @@
*/ */
package org.openhab.binding.powermax.internal.state; package org.openhab.binding.powermax.internal.state;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/** /**
* A class to store the settings of an X10 device * A class to store the settings of an X10 device
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxX10Settings { public class PowermaxX10Settings {
private String name; private final @Nullable String name;
private boolean enabled; private final boolean enabled;
public PowermaxX10Settings(String name, boolean enabled) { public PowermaxX10Settings(@Nullable String name, boolean enabled) {
this.name = name; this.name = name;
this.enabled = enabled; this.enabled = enabled;
} }
@ -30,7 +34,7 @@ public class PowermaxX10Settings {
/** /**
* @return the name of the X10 device * @return the name of the X10 device
*/ */
public String getName() { public @Nullable String getName() {
return name; return name;
} }

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.powermax.internal.state; package org.openhab.binding.powermax.internal.state;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* All panel zone names * All panel zone names
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public enum PowermaxZoneName { public enum PowermaxZoneName {
ZONE_0(0, "Attic"), ZONE_0(0, "Attic"),
@ -52,7 +55,7 @@ public enum PowermaxZoneName {
ZONE_30(30, "Custom 5"), ZONE_30(30, "Custom 5"),
ZONE_31(31, "Not Installed"); ZONE_31(31, "Not Installed");
private int id; private final int id;
private String name; private String name;
private PowermaxZoneName(int id, String name) { private PowermaxZoneName(int id, String name) {

View File

@ -12,11 +12,15 @@
*/ */
package org.openhab.binding.powermax.internal.state; package org.openhab.binding.powermax.internal.state;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/** /**
* A class to store the settings of a zone * A class to store the settings of a zone
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxZoneSettings { public class PowermaxZoneSettings {
// Note: PowermaxStatusMessage contains hardcoded references to some of these strings // Note: PowermaxStatusMessage contains hardcoded references to some of these strings
@ -27,14 +31,16 @@ public class PowermaxZoneSettings {
private static final String[] ZONE_CHIMES = { "Off", "Melody", "Zone" }; private static final String[] ZONE_CHIMES = { "Off", "Melody", "Zone" };
private String name; private final @Nullable String chime;
private String type; private final boolean[] partitions;
private String chime;
private String sensorType; private @Nullable String name;
private boolean[] partitions; private @Nullable String type;
private @Nullable String sensorType;
private boolean alwaysInAlarm; private boolean alwaysInAlarm;
public PowermaxZoneSettings(String name, byte type, byte chime, String sensorType, boolean[] partitions) { public PowermaxZoneSettings(@Nullable String name, byte type, byte chime, @Nullable String sensorType,
boolean[] partitions) {
this.name = name; this.name = name;
this.type = ((type & 0x000000FF) < ZONE_TYPES.length) ? ZONE_TYPES[type & 0x000000FF] : null; this.type = ((type & 0x000000FF) < ZONE_TYPES.length) ? ZONE_TYPES[type & 0x000000FF] : null;
this.chime = ((chime & 0x000000FF) < ZONE_CHIMES.length) ? ZONE_CHIMES[chime & 0x000000FF] : null; this.chime = ((chime & 0x000000FF) < ZONE_CHIMES.length) ? ZONE_CHIMES[chime & 0x000000FF] : null;
@ -48,7 +54,8 @@ public class PowermaxZoneSettings {
* @return the zone name * @return the zone name
*/ */
public String getName() { public String getName() {
return (name == null) ? "Unknown" : name; String localName = name;
return (localName == null) ? "Unknown" : localName;
} }
/** /**
@ -56,7 +63,7 @@ public class PowermaxZoneSettings {
* *
* @param name the zone name * @param name the zone name
*/ */
public void setName(String name) { public void setName(@Nullable String name) {
this.name = name; this.name = name;
} }
@ -64,7 +71,8 @@ public class PowermaxZoneSettings {
* @return the zone type * @return the zone type
*/ */
public String getType() { public String getType() {
return (type == null) ? "Unknown" : type; String localType = type;
return (localType == null) ? "Unknown" : localType;
} }
/** /**
@ -79,14 +87,16 @@ public class PowermaxZoneSettings {
} }
public String getChime() { public String getChime() {
return (chime == null) ? "Unknown" : chime; String localChime = chime;
return (localChime == null) ? "Unknown" : localChime;
} }
/** /**
* @return the sensor type of this zone * @return the sensor type of this zone
*/ */
public String getSensorType() { public String getSensorType() {
return (sensorType == null) ? "Unknown" : sensorType; String localSensorType = sensorType;
return (localSensorType == null) ? "Unknown" : localSensorType;
} }
/** /**

View File

@ -14,14 +14,17 @@ package org.openhab.binding.powermax.internal.state;
import static org.openhab.binding.powermax.internal.PowermaxBindingConstants.*; import static org.openhab.binding.powermax.internal.PowermaxBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.types.UnDefType;
/** /**
* A class to store the state of a zone * A class to store the state of a zone
* *
* @author Laurent Garnier - Initial contribution * @author Laurent Garnier - Initial contribution
*/ */
@NonNullByDefault
public class PowermaxZoneState extends PowermaxStateContainer { public class PowermaxZoneState extends PowermaxStateContainer {
public BooleanValue tripped = new BooleanValue(this, TRIPPED, OpenClosedType.OPEN, OpenClosedType.CLOSED); public BooleanValue tripped = new BooleanValue(this, TRIPPED, OpenClosedType.OPEN, OpenClosedType.CLOSED);
@ -41,7 +44,7 @@ public class PowermaxZoneState extends PowermaxStateContainer {
}, () -> { }, () -> {
Boolean isArmed = armed.getValue(); Boolean isArmed = armed.getValue();
if (isArmed == null) { if (isArmed == null) {
return null; return UnDefType.NULL;
} }
return isArmed ? OpenClosedType.CLOSED : OpenClosedType.OPEN; return isArmed ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
}); });
@ -51,7 +54,7 @@ public class PowermaxZoneState extends PowermaxStateContainer {
} }
public boolean isLastTripBeforeTime(long refTime) { public boolean isLastTripBeforeTime(long refTime) {
return Boolean.TRUE.equals(tripped.getValue()) && (lastTripped.getValue() != null) Long lastTrippedValue = lastTripped.getValue();
&& (lastTripped.getValue() < refTime); return Boolean.TRUE.equals(tripped.getValue()) && (lastTrippedValue != null) && (lastTrippedValue < refTime);
} }
} }