added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.caddx-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-caddx" description="Caddx Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-serial</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.caddx/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link CaddxBindingConstants} class is responsible for creating things and thing
* handlers.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxBindingConstants {
// Binding ID
private static final String BINDING_ID = "caddx";
// List of bridge device types
public static final String CADDX_BRIDGE = "bridge";
// List of device types
public static final String PANEL = "panel";
public static final String PARTITION = "partition";
public static final String ZONE = "zone";
public static final String KEYPAD = "keypad";
// List of all Bridge Thing Type UIDs
public static final ThingTypeUID CADDXBRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, CADDX_BRIDGE);
// List of all Thing Type UIDs
public static final ThingTypeUID PANEL_THING_TYPE = new ThingTypeUID(BINDING_ID, PANEL);
public static final ThingTypeUID PARTITION_THING_TYPE = new ThingTypeUID(BINDING_ID, PARTITION);
public static final ThingTypeUID ZONE_THING_TYPE = new ThingTypeUID(BINDING_ID, ZONE);
public static final ThingTypeUID KEYPAD_THING_TYPE = new ThingTypeUID(BINDING_ID, KEYPAD);
// Bridge
// Commands
// Channels
public static final String SEND_COMMAND = "send_command";
// Panel
// Commands
public static final String PANEL_INTERFACE_CONFIGURATION_REQUEST = "panel_interface_configuration_request";
public static final String PANEL_SYSTEM_STATUS_REQUEST = "panel_system_status_request";
public static final String PANEL_LOG_EVENT_REQUEST = "panel_log_event_request";
// Channels
public static final String PANEL_FIRMWARE_VERSION = "panel_firmware_version";
public static final String PANEL_LOG_MESSAGE_N_0 = "panel_log_message_n_0";
// Partition
// Commands
public static final String PARTITION_STATUS_REQUEST = "partition_status_request";
public static final String PARTITION_PRIMARY_COMMAND_WITH_PIN = "partition_primary_command_with_pin";
public static final String PARTITION_SECONDARY_COMMAND = "partition_secondary_command";
// Channels
public static final String PARTITION_ARMED = "partition_armed";
public static final String PARTITION_PRIMARY = "partition_primary";
public static final String PARTITION_SECONDARY = "partition_secondary";
// Zone
// Commands
public static final String ZONE_STATUS_REQUEST = "zone_status_request";
public static final String ZONE_NAME_REQUEST = "zone_name_request";
// Channels
public static final String ZONE_NAME = "zone_name";
public static final String ZONE_FAULTED = "zone_faulted";
public static final String ZONE_BYPASSED = "zone_bypassed";
// Keypad
// Set of all supported Thing Type UIDs
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream
.of(CADDXBRIDGE_THING_TYPE, PANEL_THING_TYPE, PARTITION_THING_TYPE, ZONE_THING_TYPE, KEYPAD_THING_TYPE)
.collect(Collectors.toSet()));
// Set of all supported Bridge Type UIDs
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(CADDXBRIDGE_THING_TYPE).collect(Collectors.toSet()));
}

View File

@@ -0,0 +1,477 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import java.util.TooManyListenersException;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortEvent;
import org.openhab.core.io.transport.serial.SerialPortEventListener;
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
import org.openhab.core.util.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link CaddxCommunicator} is responsible for the asynchronous serial communication
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxCommunicator implements SerialPortEventListener {
private final Logger logger = LoggerFactory.getLogger(CaddxCommunicator.class);
private final SerialPortManager portManager;
private final Set<CaddxPanelListener> listenerQueue = new HashSet<>();
private final Deque<CaddxMessage> messages = new LinkedBlockingDeque<>();
private final SynchronousQueue<CaddxMessage> exchanger = new SynchronousQueue<>();
private final Thread communicator;
private final CaddxProtocol protocol;
private final String serialPortName;
private final int baudRate;
private final SerialPort serialPort;
private final InputStream in;
private final OutputStream out;
// Receiver state variables
private boolean inMessage = false;
private boolean haveFirstByte = false;
private int messageBufferLength = 0;
private byte[] message;
private int messageBufferIndex = 0;
private boolean unStuff = false;
private int tempAsciiByte = 0;
public CaddxCommunicator(SerialPortManager portManager, CaddxProtocol protocol, String serialPortName, int baudRate)
throws UnsupportedCommOperationException, PortInUseException, IOException, TooManyListenersException {
this.portManager = portManager;
this.protocol = protocol;
this.serialPortName = serialPortName;
this.baudRate = baudRate;
SerialPortIdentifier portIdentifier = this.portManager.getIdentifier(serialPortName);
if (portIdentifier == null) {
throw new IOException("Cannot get the port identifier.");
}
serialPort = portIdentifier.open(this.getClass().getName(), 2000);
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialPort.enableReceiveThreshold(1);
serialPort.disableReceiveTimeout();
InputStream localIn = serialPort.getInputStream();
if (localIn == null) {
logger.warn("Cannot get the input stream of the serial port");
throw new IOException("Input stream is null");
}
in = localIn;
OutputStream localOut = serialPort.getOutputStream();
if (localOut == null) {
logger.warn("Cannot get the output stream of the serial port");
throw new IOException("Output stream is null");
}
out = localOut;
serialPort.notifyOnDataAvailable(true);
serialPort.addEventListener(this);
communicator = new Thread(this::messageDispatchLoop, "Caddx Communicator");
communicator.setDaemon(true);
communicator.start();
message = new byte[0];
logger.trace("CaddxCommunicator communication thread started successfully for {}", serialPortName);
}
public CaddxProtocol getProtocol() {
return protocol;
}
public String getSerialPortName() {
return serialPortName;
}
public int getBaudRate() {
return baudRate;
}
public void addListener(CaddxPanelListener listener) {
listenerQueue.add(listener);
}
/**
* Send message to panel. Asynchronous, i.e. returns immediately.
* Messages are sent only when panel is ready (i.e. sent an
* acknowledgment to last message), but no checks are implemented that
* the message was correctly received and executed.
*
* @param msg Data to be sent to panel. First byte is message type.
* Fletcher sum is computed and appended by transmit.
*/
public void transmit(CaddxMessage msg) {
messages.add(msg);
}
/**
* Adds this message before any others in the queue.
* Used by receiver to send ACKs.
*
* @param msg The message
*/
public void transmitFirst(CaddxMessage msg) {
messages.addFirst(msg);
}
public void stop() {
logger.trace("CaddxCommunicator stopping");
// kick thread out of waiting for FIFO
communicator.interrupt();
// Close the streams first to unblock blocked reads and writes
try {
in.close();
} catch (IOException e) {
}
try {
out.close();
} catch (IOException e) {
}
// Wait until communication thread exits
try {
communicator.join(3000);
} catch (InterruptedException e) {
}
// Also close the serial port
serialPort.removeEventListener();
serialPort.close();
}
@SuppressWarnings("null")
private void messageDispatchLoop() {
int @Nullable [] expectedMessageNumbers = null;
@Nullable
CaddxMessage outgoingMessage = null;
boolean skipTransmit = true;
try {
// loop until the thread is interrupted, sending out messages
while (!Thread.currentThread().isInterrupted()) {
// Initialize the state
outgoingMessage = null;
expectedMessageNumbers = null;
if (!skipTransmit) {
// send next outgoing message if we have one
outgoingMessage = messages.poll();
if (outgoingMessage != null) {
logger.trace("CaddxCommunicator.run() Outgoing message: {}", outgoingMessage.getMessageType());
byte[] msg = outgoingMessage.getMessageFrameBytes(protocol);
out.write(msg);
out.flush();
expectedMessageNumbers = outgoingMessage.getReplyMessageNumbers();
// Log message
if (logger.isDebugEnabled()) {
logger.debug("->: {}", outgoingMessage.getName());
logger.debug("->: {}", HexUtils
.bytesToHex(outgoingMessage.getMessageFrameBytes(CaddxProtocol.Binary), " "));
}
}
} else {
logger.trace("CaddxCommunicator.run() skipTransmit: true");
skipTransmit = false;
}
// Check for an incoming message
CaddxMessage incomingMessage = null;
try {
incomingMessage = exchanger.poll(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.debug("CaddxCommunicator.run() InterruptedException caught.");
Thread.currentThread().interrupt();
}
// Log
if (incomingMessage == null) {
if (expectedMessageNumbers == null) { // Nothing expected, Nothing received we continue
logger.trace("CaddxCommunicator.run(): Nothing expected, Nothing received we continue");
continue;
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("<-: {}", incomingMessage.getName());
logger.debug("<-: {}",
HexUtils.bytesToHex(incomingMessage.getMessageFrameBytes(CaddxProtocol.Binary), " "));
}
}
// Check if we wait for a reply
if (expectedMessageNumbers == null) {
if (incomingMessage != null) { // Nothing expected. Message received.
logger.trace("CaddxCommunicator.run() Nothing expected, Message received");
// Check if Acknowledgement handling is required.
if (incomingMessage.hasAcknowledgementFlag()) {
if (incomingMessage.isChecksumCorrect()) {
// send ACK
transmitFirst(new CaddxMessage(CaddxMessageType.POSITIVE_ACKNOWLEDGE, ""));
} else {
// Send NAK
transmitFirst(new CaddxMessage(CaddxMessageType.NEGATIVE_ACKNOWLEDGE, ""));
}
}
}
} else {
if (incomingMessage == null) {
logger.trace("CaddxCommunicator.run() Message expected. Nothing received");
// Message expected. Nothing received
if (outgoingMessage != null) {
transmitFirst(outgoingMessage); // put message in queue again
continue;
}
} else {
logger.trace("CaddxCommunicator.run() Message expected. Message received");
// Message expected. Message received.
int receivedMessageType = incomingMessage.getMessageType();
boolean isMessageExpected = IntStream.of(expectedMessageNumbers)
.anyMatch(x -> x == receivedMessageType);
if (!isMessageExpected) {
logger.trace("Non expected message received exp:{}, recv: {}", expectedMessageNumbers,
receivedMessageType);
// Non expected reply received
if (outgoingMessage != null) {
transmitFirst(outgoingMessage); // put message in queue again
skipTransmit = true; // Skip the transmit on the next cycle to receive the panel message
}
}
}
}
// Inform the listeners
if (incomingMessage != null) {
if (incomingMessage.isChecksumCorrect()) {
for (CaddxPanelListener listener : listenerQueue) {
listener.caddxMessage(this, incomingMessage);
}
} else {
logger.warn(
"CaddxCommunicator.run() Received packet checksum does not match. in: {} {}, calc {} {}",
incomingMessage.getChecksum1In(), incomingMessage.getChecksum2In(),
incomingMessage.getChecksum1Calc(), incomingMessage.getChecksum2Calc());
}
}
}
} catch (IOException e) {
logger.debug("CaddxCommunicator.run() IOException. Stopping sender thread. {}", getSerialPortName());
Thread.currentThread().interrupt();
}
logger.warn("CaddxCommunicator.run() Sender thread stopped. {}", getSerialPortName());
}
/**
* Event handler to receive the data from the serial port
*
* @param SerialPortEvent serialPortEvent The event that occurred on the serial port
*/
@Override
public void serialEvent(@Nullable SerialPortEvent serialPortEvent) {
if (serialPortEvent == null) {
return;
}
if (serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
logger.trace("Data receiving from the serial port");
if (protocol == CaddxProtocol.Binary) {
receiveInBinaryProtocol(serialPortEvent);
} else {
receiveInAsciiProtocol(serialPortEvent);
}
}
}
private int readByte(InputStream stream) throws IOException {
int b = -1;
if (stream.available() > 0) {
b = stream.read();
}
if (b == -1) {
throw new EOFException();
}
return b;
}
private int readAsciiByte(InputStream stream) throws IOException {
if (!haveFirstByte) { // this is the 1st digit
int b = readByte(in);
tempAsciiByte = (b >= 0x30 && b <= 0x39) ? (b - 0x30) * 0x10 : (b - 0x37) * 0x10;
haveFirstByte = true;
}
if (haveFirstByte) { // this is the 2nd digit
int b = readByte(in);
tempAsciiByte += (b >= 0x30 && b <= 0x39) ? (b - 0x30) : (b - 0x37);
haveFirstByte = false;
}
return tempAsciiByte;
}
private void loopUntilByteIsRead(InputStream stream, int byteToRead) throws IOException {
int b = 0;
do {
b = readByte(in);
} while (b != byteToRead);
}
private void offerCaddxMessage() throws InterruptedException {
logger.trace("Offering received message");
// Full message received in data byte array
CaddxMessage caddxMessage = new CaddxMessage(message, true);
if (!exchanger.offer(caddxMessage, 3, TimeUnit.SECONDS)) {
logger.debug("Offered message was not received");
}
}
private void receiveInBinaryProtocol(SerialPortEvent serialPortEvent) {
try {
// Read the start byte
if (!inMessage) // skip until 0x7E
{
loopUntilByteIsRead(in, 0x7e);
inMessage = true;
messageBufferLength = 0;
}
logger.trace("CaddxCommunicator.handleBinaryProtocol() Got start byte");
// Read the message length
if (messageBufferLength == 0) {
int b = readByte(in);
messageBufferLength = b + 2; // add two bytes for the checksum
message = new byte[messageBufferLength];
messageBufferIndex = 0;
}
logger.trace("CaddxCommunicator.handleBinaryProtocol() Got message length {}", messageBufferLength);
// Read the message
do {
int b = readByte(in);
message[messageBufferIndex] = (byte) b;
if (message[messageBufferIndex] == 0x7D) {
unStuff = true;
continue;
}
if (unStuff) {
message[messageBufferIndex] |= 0x20;
unStuff = false;
}
messageBufferIndex++;
} while (messageBufferIndex < messageBufferLength);
// Offer the message
offerCaddxMessage();
logger.trace("CaddxCommunicator.handleBinaryProtocol() Got message {}", message[0]);
} catch (EOFException e) {
return;
} catch (IOException e) {
} catch (InterruptedException e) {
logger.trace("InterruptedException caught.");
Thread.currentThread().interrupt();
}
// Initialize state for a new reception
inMessage = false;
messageBufferLength = 0;
messageBufferIndex = 0;
unStuff = false;
}
private void receiveInAsciiProtocol(SerialPortEvent serialPortEvent) {
try {
// Read the start byte
if (!inMessage) {
loopUntilByteIsRead(in, 0x0a);
inMessage = true;
haveFirstByte = false;
messageBufferLength = 0;
}
logger.trace("CaddxCommunicator.handleAsciiProtocol() Got start byte");
// Read the message length
if (messageBufferLength == 0) {
int b = readAsciiByte(in);
messageBufferLength = b + 2; // add 2 bytes for the checksum
message = new byte[messageBufferLength];
}
logger.trace("CaddxCommunicator.handleAsciiProtocol() Got message length {}", messageBufferLength);
// Read the message
do {
int b = readAsciiByte(in);
message[messageBufferIndex] = (byte) b;
messageBufferIndex++;
} while (messageBufferIndex < messageBufferLength);
// Offer the message
offerCaddxMessage();
logger.trace("CaddxCommunicator.handleAsciiProtocol() Got message {}", message[0]);
} catch (EOFException e) {
return;
} catch (IOException e) {
} catch (InterruptedException e) {
logger.trace("InterruptedException caught.");
Thread.currentThread().interrupt();
}
// Initialize state for a new reception
inMessage = false;
messageBufferLength = 0;
messageBufferIndex = 0;
}
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Message Direction enumeration.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public enum CaddxDirection {
IN,
OUT
};

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import java.util.EventObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Event for Receiving API Messages.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxEvent extends EventObject {
private static final long serialVersionUID = 1L;
private final CaddxMessage caddxMessage;
private final @Nullable Integer partition;
private final @Nullable Integer zone;
private final @Nullable Integer keypad;
/**
* Constructor.
*
* @param source
*/
public CaddxEvent(CaddxMessage caddxMessage, @Nullable Integer partition, @Nullable Integer zone,
@Nullable Integer keypad) {
super(caddxMessage);
this.caddxMessage = caddxMessage;
this.partition = partition;
this.zone = zone;
this.keypad = keypad;
}
/**
* Returns the Message event from the Caddx Alarm System.
*
* @return message
*/
public CaddxMessage getCaddxMessage() {
return caddxMessage;
}
public @Nullable Integer getPartition() {
return partition;
}
public @Nullable Integer getZone() {
return zone;
}
public @Nullable Integer getKeypad() {
return keypad;
}
}

View File

@@ -0,0 +1,411 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.util.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A class that represents the Caddx Alarm Messages.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxMessage {
private final Logger logger = LoggerFactory.getLogger(CaddxMessage.class);
private final CaddxMessageType caddxMessageType;
private final Map<String, String> propertyMap = new HashMap<>();
private final Map<String, String> idMap = new HashMap<>();
private final byte[] message;
private final boolean hasAcknowledgementFlag;
private final byte checksum1In;
private final byte checksum2In;
private final byte checksum1Calc;
private final byte checksum2Calc;
/**
* Constructor.
*
* @param message
* - the message received
*/
public CaddxMessage(byte[] message, boolean withChecksum) {
if (withChecksum && message.length < 3) {
logger.debug("CaddxMessage: The message should be at least 3 bytes long.");
throw new IllegalArgumentException("The message should be at least 3 bytes long");
}
if (!withChecksum && message.length < 1) {
logger.debug("CaddxMessage: The message should be at least 1 byte long.");
throw new IllegalArgumentException("The message should be at least 1 byte long");
}
// Received data
byte[] msg = message;
// Fill in the checksum
if (withChecksum) {
checksum1In = message[message.length - 2];
checksum2In = message[message.length - 1];
msg = Arrays.copyOf(message, message.length - 2);
byte[] fletcherSum = fletcher(msg);
checksum1Calc = fletcherSum[0];
checksum2Calc = fletcherSum[1];
} else {
byte[] fletcherSum = fletcher(msg);
checksum1Calc = fletcherSum[0];
checksum2Calc = fletcherSum[1];
checksum1In = checksum1Calc;
checksum2In = checksum2Calc;
}
// Fill in the message
this.message = msg;
// Fill-in the acknowledgement flag
if ((message[0] & 0x80) != 0) {
hasAcknowledgementFlag = true;
message[0] = (byte) (message[0] & 0x7f);
} else {
hasAcknowledgementFlag = false;
}
// Fill-in the message type
CaddxMessageType mt = CaddxMessageType.valueOfMessageType(message[0]);
if (mt == null) {
throw new IllegalArgumentException("Unknown message");
}
caddxMessageType = mt;
// Fill-in the properties
processCaddxMessage();
}
public CaddxMessage(CaddxMessageType type, String data) {
int length = type.length;
String[] tokens = data.split("\\,");
if (length != 1 && tokens.length != length - 1) {
logger.debug("token.length should be length-1. token.length={}, length={}", tokens.length, length);
throw new IllegalArgumentException("CaddxMessage: data has not the correct format.");
}
byte[] msg = new byte[length];
msg[0] = (byte) type.number;
for (int i = 0; i < length - 1; i++) {
msg[i + 1] = (byte) Integer.decode(tokens[i]).intValue();
}
// Fill-in the checksum
byte[] fletcherSum = fletcher(msg);
checksum1Calc = fletcherSum[0];
checksum2Calc = fletcherSum[1];
checksum1In = checksum1Calc;
checksum2In = checksum2Calc;
// Fill-in the message
this.message = msg;
// Fill-in the acknowledgement flag
if ((message[0] & 0x80) != 0) {
hasAcknowledgementFlag = true;
message[0] = (byte) (message[0] & 0x7f);
} else {
hasAcknowledgementFlag = false;
}
// Fill-in the message type
this.caddxMessageType = type;
// Fill-in the properties
processCaddxMessage();
}
public byte getChecksum1In() {
return checksum1In;
}
public byte getChecksum2In() {
return checksum2In;
}
public byte getChecksum1Calc() {
return checksum1Calc;
}
public byte getChecksum2Calc() {
return checksum2Calc;
}
public CaddxMessageType getCaddxMessageType() {
return caddxMessageType;
}
public byte getMessageType() {
return message[0];
}
public String getName() {
StringBuilder sb = new StringBuilder();
sb.append(caddxMessageType.name);
switch (caddxMessageType) {
case ZONE_STATUS_REQUEST:
case ZONE_STATUS_MESSAGE:
sb.append(" [Zone: ");
sb.append(getPropertyById("zone_number"));
sb.append("]");
break;
case LOG_EVENT_REQUEST:
case LOG_EVENT_MESSAGE:
sb.append(" [Event: ");
sb.append(getPropertyById("panel_log_event_number"));
sb.append("]");
break;
case PARTITION_STATUS_REQUEST:
case PARTITION_STATUS_MESSAGE:
sb.append(" [Partition: ");
sb.append(getPropertyById("partition_number"));
sb.append("]");
break;
default:
break;
}
return sb.toString();
}
public String getPropertyValue(String property) {
if (!propertyMap.containsKey(property)) {
logger.debug("Message does not contain property [{}]", property);
return "";
}
return propertyMap.get(property);
}
public String getPropertyById(String id) {
if (!idMap.containsKey(id)) {
logger.debug("Message does not contain id [{}]", id);
return "";
}
return idMap.get(id);
}
public int @Nullable [] getReplyMessageNumbers() {
return caddxMessageType.replyMessageNumbers;
}
public CaddxSource getSource() {
return getCaddxMessageType().source;
}
public boolean isChecksumCorrect() {
return checksum1In == checksum1Calc && checksum2In == checksum2Calc;
}
public boolean isLengthCorrect() {
return message.length == caddxMessageType.length;
}
public boolean hasAcknowledgementFlag() {
return hasAcknowledgementFlag;
}
public byte[] getMessageFrameBytes(CaddxProtocol protocol) {
if (protocol == CaddxProtocol.Binary) {
return getMessageFrameBytesInBinary();
} else {
return getMessageFrameBytesInAscii();
}
}
public byte[] getMessageBytes() {
return message;
}
/**
* Returns a string representation of a CaddxMessage.
*
* @return CaddxMessage string
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
CaddxMessageType mt = CaddxMessageType.valueOfMessageType(message[0]);
if (mt == null) {
return "Unknown message type";
}
sb.append("Message: ");
sb.append(String.format("%2s", Integer.toHexString(message[0])));
sb.append(" ");
sb.append(mt.name);
sb.append(System.lineSeparator());
for (CaddxProperty p : mt.properties) {
sb.append("\t").append(p.toString(message));
sb.append(System.lineSeparator());
}
return sb.toString();
}
private void putByteInBuffer(ByteBuffer frame, byte b) {
if (b == 0x7e) {
frame.put((byte) 0x7d);
frame.put((byte) 0x5e);
} else if (b == 0x7d) {
frame.put((byte) 0x7d);
frame.put((byte) 0x5d);
} else {
frame.put(b);
}
}
private byte[] getByteBufferArray(ByteBuffer frame) {
if (frame.hasArray()) {
return frame.array();
} else {
byte[] byteArray = new byte[frame.capacity()];
frame.position(0);
frame.get(byteArray);
return byteArray;
}
}
private byte[] getMessageFrameBytesInBinary() {
// Calculate bytes
// 1 for the startbyte
// 1 for the length
// 2 for the checksum
// n for the count of 0x7d and 0x7e occurrences in the message and checksum
int additional = 4;
for (int i = 0; i < message.length; i++) {
if (message[i] == 0x7d || message[i] == 0x7e) {
additional++;
}
}
if (checksum1Calc == 0x7d || checksum1Calc == 0x7e) {
additional++;
}
if (checksum2Calc == 0x7d || checksum2Calc == 0x7e) {
additional++;
}
ByteBuffer frame = ByteBuffer.allocate(message.length + additional);
// start character
frame.put((byte) 0x7e);
// message length
frame.put((byte) message.length);
// message
for (int i = 0; i < message.length; i++) {
putByteInBuffer(frame, message[i]);
}
// 1st checksum byte
putByteInBuffer(frame, checksum1Calc);
// 2nd checksum byte
putByteInBuffer(frame, checksum2Calc);
return getByteBufferArray(frame);
}
private byte[] getMessageFrameBytesInAscii() {
// Calculate additional bytes
// 1 for the start byte
// 2 for the length
// 4 for the checksum
// 1 for the stop byte
int additional = 8;
ByteBuffer frame = ByteBuffer.allocate(2 * message.length + additional);
// start character
frame.put((byte) 0x0a);
// message length
frame.put(HexUtils.byteToHex((byte) message.length));
// message
for (int i = 0; i < message.length; i++) {
frame.put(HexUtils.byteToHex(message[i]));
}
// Checksum 1st byte
frame.put(HexUtils.byteToHex(checksum1Calc));
// Checksum 2nd byte
frame.put(HexUtils.byteToHex(checksum2Calc));
// Stop character
frame.put((byte) 0x0d);
return getByteBufferArray(frame);
}
/**
* Processes the incoming Caddx message and extracts the information.
*/
private void processCaddxMessage() {
// fill the property lookup hashmaps
for (CaddxProperty p : caddxMessageType.properties) {
propertyMap.put(p.getName(), p.getValue(message));
}
for (CaddxProperty p : caddxMessageType.properties) {
if (!"".equals(p.getId())) {
idMap.put(p.getId(), p.getValue(message));
}
}
}
/**
* Calculates the Fletcher checksum of the byte array.
*
* @param data The input byte array
* @return Byte array with two elements. Checksum1 and Checksum2
*/
private byte[] fletcher(byte data[]) {
int len = data.length;
int sum1 = len, sum2 = len;
for (int i = 0; i < len; i++) {
int d = data[i] & 0xff;
if (0xff - sum1 < d) {
sum1 = (sum1 + 1) & 0xff;
}
sum1 = (sum1 + d) & 0xff;
if (sum1 == 0xff) {
sum1 = 0;
}
if (0xff - sum2 < sum1) {
sum2 = (sum2 + 1) & 0xff;
}
sum2 = (sum2 + sum1) & 0xff;
if (sum2 == 0xff) {
sum2 = 0;
}
}
return new byte[] { (byte) sum1, (byte) sum2 };
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Panel listener interface
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public interface CaddxPanelListener {
public void caddxMessage(CaddxCommunicator communicator, CaddxMessage message);
}

View File

@@ -0,0 +1,190 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Panel message property class
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxProperty {
// private
private final String name;
private final CaddxPropertyType type; // 'Int', 'String', 'Bit'
private final int byteFrom;
private final int byteLength;
private final int bitFrom;
private final int bitLength;
private final boolean external;
private final String id;
// Constructor
public CaddxProperty(String id, int byteFrom, int byteLength, int bitFrom, int bitLength, CaddxPropertyType type,
String name, boolean external) {
this.id = id;
this.name = name;
this.type = type;
this.byteFrom = byteFrom;
this.byteLength = byteLength;
this.bitFrom = bitFrom;
this.bitLength = bitLength;
this.external = external;
}
public String getName() {
return name;
}
public CaddxPropertyType getType() {
return type;
}
public boolean getExternal() {
return external;
}
public String getId() {
return id;
}
public String getValue(byte[] message) {
int mask;
int val;
switch (type) {
case INT:
if (bitFrom == 0 && bitLength == 0) {
mask = 255;
val = message[byteFrom - 1] & mask;
} else {
mask = ((1 << ((bitLength - bitFrom))) - 1) << bitFrom;
val = (message[byteFrom - 1] & mask) >> bitFrom;
}
return Integer.toString(val);
case STRING:
byte[] str = Arrays.copyOfRange(message, byteFrom - 1, byteFrom + byteLength);
return mapCaddxString(new String(str, StandardCharsets.US_ASCII));
case BIT:
return (((message[byteFrom - 1] & (1 << bitFrom)) > 0) ? "true" : "false");
default:
throw new IllegalArgumentException("type is unknown.");
}
}
public String toString(byte[] message) {
int mask;
int val;
StringWriter sWriter = new StringWriter();
PrintWriter pWriter = new PrintWriter(sWriter);
switch (type) {
case INT:
if (bitFrom == 0 && bitLength == 0) {
mask = 255;
val = message[byteFrom - 1];
} else {
mask = ((1 << ((bitLength - bitFrom) + 1)) - 1) << bitFrom;
val = (message[byteFrom - 1] & mask) >> bitFrom;
}
pWriter.printf("%s: %02x - %d - %c", name, val, val, Character.isValidCodePoint(val) ? val : 32);
pWriter.flush();
return sWriter.toString();
case STRING:
pWriter.print(name);
pWriter.print(": ");
byte[] a = Arrays.copyOfRange(message, byteFrom - 1, byteFrom + byteLength);
pWriter.println(mapCaddxString(new String(a, StandardCharsets.US_ASCII)));
pWriter.println();
for (int i = 0; i < byteLength; i++) {
pWriter.printf("%02x", message[byteFrom - 1 + i]);
pWriter.print(" - ");
pWriter.println((char) message[byteFrom - 1 + i]);
}
pWriter.flush();
return sWriter.toString();
case BIT:
pWriter.print(name);
pWriter.print(": ");
pWriter.print(((message[byteFrom - 1] & (1 << bitFrom)) > 0));
pWriter.flush();
return sWriter.toString();
default:
pWriter.print("Unknown type: ");
pWriter.print(type.toString());
pWriter.flush();
return sWriter.toString();
}
}
private String mapCaddxString(String str) {
StringBuilder s = new StringBuilder(str.length());
CharacterIterator it = new StringCharacterIterator(str);
for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
switch (ch) {
case 0xb7:
s.append('Γ');
break;
case 0x10:
s.append('Δ');
break;
case 0x13:
s.append('Θ');
break;
case 0x14:
s.append('Λ');
break;
case 0x12:
s.append('Ξ');
break;
case 0xc8:
s.append('Π');
break;
case 0x16:
s.append('Σ');
break;
case 0xcc:
s.append('Φ');
break;
case 0x17:
s.append('Ψ');
break;
case 0x15:
s.append('Ω');
break;
default:
s.append(ch);
break;
}
}
return s.toString();
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Message property types enumeration.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public enum CaddxPropertyType {
INT,
STRING,
BIT
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Panel Protocol enumeration.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public enum CaddxProtocol {
Binary,
Ascii
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Alarm component Source enumeration.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public enum CaddxSource {
NONE,
PANEL,
KEYPAD,
PARTITION,
ZONE
};

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.caddx.internal.CaddxProtocol;
/**
* Configuration class for the Caddx RS232 Serial interface bridge.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxBridgeConfiguration {
// Caddx Bridge Thing constants
public static final String PROTOCOL = "protocol";
public static final String SERIAL_PORT = "serialPort";
public static final String BAUD = "baud";
private CaddxProtocol protocol = CaddxProtocol.Binary;
private @Nullable String serialPort;
private int baudrate = 9600;
public CaddxProtocol getProtocol() {
return protocol;
}
public @Nullable String getSerialPort() {
return serialPort;
}
public int getBaudrate() {
return baudrate;
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Configuration class for the Caddx Keypad Thing.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxKeypadConfiguration {
// Keypad Thing constants
public static final String KEYPAD_ADDRESS = "keypadAddress";
private int keypadAddress;
public int getKeypadAddress() {
return keypadAddress;
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Configuration class for the Caddx Partition Thing.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxPartitionConfiguration {
// Partition Thing constants
public static final String PARTITION_NUMBER = "partitionNumber";
/**
* The Partition Number. Can be in the range of 1-8. This is a required parameter for a partition.
*/
private int partitionNumber;
/**
* The User Number of the user that will execute commands against the partition.
*/
private int userNumber;
public int getPartitionNumber() {
return partitionNumber;
}
public int getUserNumber() {
return userNumber;
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Configuration class for the Caddx Zone Thing.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxZoneConfiguration {
// Zone Thing constants
public static final String ZONE_NUMBER = "zoneNumber";
/**
* The Zone Number. Can be in the range of 1-192. Depends on the Panel model. This is a required parameter for a
* zone.
*/
private int zoneNumber;
public int getZoneNumber() {
return zoneNumber;
}
}

View File

@@ -0,0 +1,166 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.discovery;
import java.util.Collections;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.caddx.internal.CaddxBindingConstants;
import org.openhab.binding.caddx.internal.CaddxEvent;
import org.openhab.binding.caddx.internal.config.CaddxKeypadConfiguration;
import org.openhab.binding.caddx.internal.config.CaddxPartitionConfiguration;
import org.openhab.binding.caddx.internal.config.CaddxZoneConfiguration;
import org.openhab.binding.caddx.internal.handler.CaddxBridgeHandler;
import org.openhab.binding.caddx.internal.handler.CaddxThingType;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is responsible for discovering the supported Things.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService, DiscoveryService {
private final Logger logger = LoggerFactory.getLogger(CaddxDiscoveryService.class);
private @Nullable CaddxBridgeHandler caddxBridgeHandler = null;
public CaddxDiscoveryService() {
super(CaddxBindingConstants.SUPPORTED_THING_TYPES_UIDS, 15, false);
}
@Override
protected void startScan() {
// Discovery is performed implicitly via the CadxBridgeHandler
}
/**
* Method to add a Thing to the Smarthome Inbox.
*
* @param bridge
* @param caddxThingType
* @param event
*/
public void addThing(Bridge bridge, CaddxThingType caddxThingType, CaddxEvent event) {
ThingUID thingUID = null;
String thingID = "";
String thingLabel = "";
Map<String, Object> properties = null;
Integer partition = event.getPartition();
Integer zone = event.getZone();
Integer keypad = event.getKeypad();
String representationProperty = null;
switch (caddxThingType) {
case PANEL:
thingID = "panel";
thingLabel = "Panel";
thingUID = new ThingUID(CaddxBindingConstants.PANEL_THING_TYPE, bridge.getUID(), thingID);
break;
case PARTITION:
thingID = "partition" + partition;
thingLabel = "Partition " + partition;
thingUID = new ThingUID(CaddxBindingConstants.PARTITION_THING_TYPE, bridge.getUID(), thingID);
if (partition != null) {
properties = Collections.singletonMap(CaddxPartitionConfiguration.PARTITION_NUMBER, partition);
representationProperty = CaddxPartitionConfiguration.PARTITION_NUMBER;
}
break;
case ZONE:
thingID = "zone" + zone;
thingLabel = "Zone " + zone;
thingUID = new ThingUID(CaddxBindingConstants.ZONE_THING_TYPE, bridge.getUID(), thingID);
if (zone != null) {
properties = Collections.singletonMap(CaddxZoneConfiguration.ZONE_NUMBER, zone);
representationProperty = CaddxZoneConfiguration.ZONE_NUMBER;
}
break;
case KEYPAD:
thingID = "keypad";
thingLabel = "Keypad";
thingUID = new ThingUID(CaddxBindingConstants.KEYPAD_THING_TYPE, bridge.getUID(), thingID);
if (keypad != null) {
properties = Collections.singletonMap(CaddxKeypadConfiguration.KEYPAD_ADDRESS, keypad);
representationProperty = CaddxKeypadConfiguration.KEYPAD_ADDRESS;
}
break;
}
if (thingUID != null) {
DiscoveryResult discoveryResult;
if (properties != null && representationProperty != null) {
discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withRepresentationProperty(representationProperty).withBridge(bridge.getUID())
.withLabel(thingLabel).build();
} else {
discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridge.getUID())
.withLabel(thingLabel).build();
}
thingDiscovered(discoveryResult);
} else {
logger.warn("addThing(): Unable to Add Caddx Alarm Thing to Inbox!");
}
}
/**
* Activates the Discovery Service.
*/
@Override
public void activate() {
CaddxBridgeHandler handler = caddxBridgeHandler;
if (handler != null) {
handler.registerDiscoveryService(this);
}
}
/**
* Deactivates the Discovery Service.
*/
@Override
public void deactivate() {
CaddxBridgeHandler handler = caddxBridgeHandler;
if (handler != null) {
handler.unregisterDiscoveryService();
}
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof CaddxBridgeHandler) {
caddxBridgeHandler = (CaddxBridgeHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return caddxBridgeHandler;
}
}

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.factory;
import static org.openhab.binding.caddx.internal.CaddxBindingConstants.SUPPORTED_THING_TYPES_UIDS;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.caddx.internal.CaddxBindingConstants;
import org.openhab.binding.caddx.internal.handler.CaddxBridgeHandler;
import org.openhab.binding.caddx.internal.handler.ThingHandlerKeypad;
import org.openhab.binding.caddx.internal.handler.ThingHandlerPanel;
import org.openhab.binding.caddx.internal.handler.ThingHandlerPartition;
import org.openhab.binding.caddx.internal.handler.ThingHandlerZone;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link CaddxHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Georgios Moutsos - Initial contribution
*/
@Component(configurationPid = "binding.caddx", service = ThingHandlerFactory.class)
@NonNullByDefault
public class CaddxHandlerFactory extends BaseThingHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(CaddxHandlerFactory.class);
private final SerialPortManager portManager;
@Activate
public CaddxHandlerFactory(@Reference SerialPortManager portManager) {
this.portManager = portManager;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(CaddxBindingConstants.CADDXBRIDGE_THING_TYPE)) {
return new CaddxBridgeHandler(portManager, (Bridge) thing);
} else if (thingTypeUID.equals(CaddxBindingConstants.PANEL_THING_TYPE)) {
return new ThingHandlerPanel(thing);
} else if (thingTypeUID.equals(CaddxBindingConstants.PARTITION_THING_TYPE)) {
return new ThingHandlerPartition(thing);
} else if (thingTypeUID.equals(CaddxBindingConstants.ZONE_THING_TYPE)) {
return new ThingHandlerZone(thing);
} else if (thingTypeUID.equals(CaddxBindingConstants.KEYPAD_THING_TYPE)) {
return new ThingHandlerKeypad(thing);
} else {
logger.debug("createHandler(): ThingHandler not found for {}", thingTypeUID);
return null;
}
}
}

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.caddx.internal.CaddxEvent;
import org.openhab.binding.caddx.internal.config.CaddxKeypadConfiguration;
import org.openhab.binding.caddx.internal.config.CaddxPartitionConfiguration;
import org.openhab.binding.caddx.internal.config.CaddxZoneConfiguration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract class for a Caddx Thing Handler.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public abstract class CaddxBaseThingHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(CaddxBaseThingHandler.class);
/** Bridge Handler for the Thing. */
private @Nullable CaddxBridgeHandler caddxBridgeHandler = null;
/** Caddx Alarm Thing type. */
private CaddxThingType caddxThingType;
/** Partition Number. */
private int partitionNumber;
/** User Number. */
private int userNumber;
/** Zone Number. */
private int zoneNumber;
/** Keypad Address. */
private int keypadAddress;
public CaddxBaseThingHandler(Thing thing, CaddxThingType caddxThingType) {
super(thing);
this.caddxThingType = caddxThingType;
}
@Override
public void initialize() {
getConfiguration(caddxThingType);
// set the Thing offline for now
updateStatus(ThingStatus.OFFLINE);
}
/**
* Get the Bridge Handler for the Caddx system.
*
* @return CaddxBridgeHandler
*/
public @Nullable CaddxBridgeHandler getCaddxBridgeHandler() {
if (this.caddxBridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("getCaddxBridgeHandler(): Unable to get bridge!");
return null;
}
logger.trace("getCaddxBridgeHandler(): Bridge for '{}' - '{}'", getThing().getUID(), bridge.getUID());
ThingHandler handler = bridge.getHandler();
if (handler instanceof CaddxBridgeHandler) {
this.caddxBridgeHandler = (CaddxBridgeHandler) handler;
} else {
logger.debug("getCaddxBridgeHandler(): Unable to get bridge handler!");
}
}
return this.caddxBridgeHandler;
}
/**
* Method to Update a Channel
*
* @param channel
* @param state
* @param description
*/
public abstract void updateChannel(ChannelUID channel, String data);
/**
* Receives Events from the bridge.
*
* @param event.
* @param thing
*/
public abstract void caddxEventReceived(CaddxEvent event, Thing thing);
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
/**
* Get the thing configuration.
*
* @param caddxThingType The Thing type
*/
private void getConfiguration(CaddxThingType caddxThingType) {
switch (caddxThingType) {
case PARTITION:
CaddxPartitionConfiguration partitionConfiguration = getConfigAs(CaddxPartitionConfiguration.class);
setPartitionNumber(partitionConfiguration.getPartitionNumber());
setUserNumber(partitionConfiguration.getUserNumber());
break;
case ZONE:
CaddxZoneConfiguration zoneConfiguration = getConfigAs(CaddxZoneConfiguration.class);
setZoneNumber(zoneConfiguration.getZoneNumber());
break;
case KEYPAD:
CaddxKeypadConfiguration keypadConfiguration = getConfigAs(CaddxKeypadConfiguration.class);
setKeypadAddress(keypadConfiguration.getKeypadAddress());
default:
break;
}
}
/**
* Get the Thing type.
*
* @return caddxThingType
*/
public CaddxThingType getCaddxThingType() {
return caddxThingType;
}
/**
* Get Partition Number.
*
* @return partitionNumber
*/
public int getPartitionNumber() {
return partitionNumber;
}
/**
* Set Partition Number.
*
* @param partitionNumber
*/
public void setPartitionNumber(int partitionNumber) {
this.partitionNumber = partitionNumber;
}
/**
* Get User Number.
*
* @return userNumber
*/
public int getUserNumber() {
return userNumber;
}
/**
* Set User Number.
*
* @param userNumber
*/
public void setUserNumber(int userNumber) {
this.userNumber = userNumber;
}
/**
* Get Zone Number.
*
* @return zoneNumber
*/
public int getZoneNumber() {
return zoneNumber;
}
/**
* Set Zone Number.
*
* @param zoneNumber
*/
public void setZoneNumber(int zoneNumber) {
this.zoneNumber = zoneNumber;
}
/**
* Get Keypad Address.
*
* @return keypadAddress
*/
public int getKeypadAddress() {
return keypadAddress;
}
/**
* Set Keypad Address.
*
* @param keypadAddress
*/
public void setKeypadAddress(int keypadAddress) {
this.keypadAddress = keypadAddress;
}
/**
* Get Channel by ChannelUID.
*
* @param channelUID
*/
public @Nullable Channel getChannel(ChannelUID channelUID) {
Channel channel = null;
List<Channel> channels = getThing().getChannels();
for (Channel ch : channels) {
if (channelUID == ch.getUID()) {
channel = ch;
break;
}
}
return channel;
}
}

View File

@@ -0,0 +1,417 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import static org.openhab.binding.caddx.internal.CaddxBindingConstants.SEND_COMMAND;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.caddx.internal.CaddxBindingConstants;
import org.openhab.binding.caddx.internal.CaddxCommunicator;
import org.openhab.binding.caddx.internal.CaddxEvent;
import org.openhab.binding.caddx.internal.CaddxMessage;
import org.openhab.binding.caddx.internal.CaddxMessageType;
import org.openhab.binding.caddx.internal.CaddxPanelListener;
import org.openhab.binding.caddx.internal.CaddxProtocol;
import org.openhab.binding.caddx.internal.CaddxSource;
import org.openhab.binding.caddx.internal.config.CaddxBridgeConfiguration;
import org.openhab.binding.caddx.internal.config.CaddxKeypadConfiguration;
import org.openhab.binding.caddx.internal.config.CaddxPartitionConfiguration;
import org.openhab.binding.caddx.internal.config.CaddxZoneConfiguration;
import org.openhab.binding.caddx.internal.discovery.CaddxDiscoveryService;
import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The bridge handler for the Caddx RS232 Serial interface.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class CaddxBridgeHandler extends BaseBridgeHandler implements CaddxPanelListener {
private final Logger logger = LoggerFactory.getLogger(CaddxBridgeHandler.class);
static final byte[] DISCOVERY_PARTITION_STATUS_REQUEST_0 = { 0x26, 0x00 };
static final byte[] DISCOVERY_ZONES_SNAPSHOT_REQUEST_00 = { 0x25, 0x00 };
static final byte[] DISCOVERY_ZONES_SNAPSHOT_REQUEST_10 = { 0x25, 0x10 };
static final byte[] DISCOVERY_ZONES_SNAPSHOT_REQUEST_20 = { 0x25, 0x20 };
static final byte[] DISCOVERY_PARTITIONS_SNAPSHOT_REQUEST = { 0x27 };
private final SerialPortManager portManager;
private @Nullable CaddxDiscoveryService discoveryService = null;
private CaddxProtocol protocol = CaddxProtocol.Binary;
private String serialPortName = "";
private int baudRate;
private @Nullable CaddxCommunicator communicator = null;
// Things served by the bridge
private Map<BigDecimal, Thing> thingZonesMap = new ConcurrentHashMap<>();
private Map<BigDecimal, Thing> thingPartitionsMap = new ConcurrentHashMap<>();
private Map<BigDecimal, Thing> thingKeypadsMap = new ConcurrentHashMap<>();
private @Nullable Thing thingPanel = null;
public @Nullable CaddxDiscoveryService getDiscoveryService() {
return discoveryService;
}
public void setDiscoveryService(CaddxDiscoveryService discoveryService) {
this.discoveryService = discoveryService;
}
/**
* Constructor.
*
* @param bridge
*/
public CaddxBridgeHandler(SerialPortManager portManager, Bridge bridge) {
super(bridge);
this.portManager = portManager;
}
@Override
public void initialize() {
CaddxBridgeConfiguration configuration = getConfigAs(CaddxBridgeConfiguration.class);
String portName = configuration.getSerialPort();
if (portName == null) {
logger.debug("Serial port is not defined in the configuration");
return;
}
serialPortName = portName;
protocol = configuration.getProtocol();
baudRate = configuration.getBaudrate();
updateStatus(ThingStatus.OFFLINE);
// create & start panel interface
logger.debug("Starting interface at port {} with baudrate {} and protocol {}", serialPortName, baudRate,
protocol);
try {
communicator = new CaddxCommunicator(portManager, protocol, serialPortName, baudRate);
} catch (IOException | TooManyListenersException | UnsupportedCommOperationException | PortInUseException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Communication cannot be initialized. " + e.toString());
return;
}
CaddxCommunicator comm = communicator;
if (comm != null) {
comm.addListener(this);
// Send discovery commands for the things
comm.transmit(new CaddxMessage(DISCOVERY_ZONES_SNAPSHOT_REQUEST_00, false));
comm.transmit(new CaddxMessage(DISCOVERY_ZONES_SNAPSHOT_REQUEST_10, false));
comm.transmit(new CaddxMessage(DISCOVERY_ZONES_SNAPSHOT_REQUEST_20, false));
comm.transmit(new CaddxMessage(DISCOVERY_PARTITION_STATUS_REQUEST_0, false));
comm.transmit(new CaddxMessage(DISCOVERY_PARTITIONS_SNAPSHOT_REQUEST, false));
}
// list all channels
if (logger.isTraceEnabled()) {
logger.trace("list all {} channels:", getThing().getChannels().size());
for (Channel c : getThing().getChannels()) {
logger.trace("Channel Type {} UID {}", c.getChannelTypeUID(), c.getUID());
}
}
}
@Override
public void dispose() {
CaddxCommunicator comm = communicator;
if (comm != null) {
comm.stop();
comm = null;
}
if (discoveryService != null) {
unregisterDiscoveryService();
}
super.dispose();
}
public @Nullable Thing findThing(CaddxThingType caddxThingType, @Nullable Integer partition, @Nullable Integer zone,
@Nullable Integer keypad) {
switch (caddxThingType) {
case PARTITION:
if (partition != null) {
return thingPartitionsMap.get(BigDecimal.valueOf(partition));
}
case ZONE:
if (zone != null) {
return thingZonesMap.get(BigDecimal.valueOf(zone));
}
case KEYPAD:
if (keypad != null) {
return thingKeypadsMap.get(BigDecimal.valueOf(keypad));
}
case PANEL:
return thingPanel;
}
return null;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.trace("handleCommand(), channelUID: {}, command: {}", channelUID, command);
switch (channelUID.getId()) {
case SEND_COMMAND:
if (!command.toString().isEmpty()) {
String[] tokens = command.toString().split("\\|");
String cmd = tokens[0];
String data = "";
if (tokens.length > 1) {
data = tokens[1];
}
sendCommand(cmd, data);
updateState(channelUID, new StringType(""));
}
break;
default:
logger.debug("Unknown command {}", command);
break;
}
}
/**
* Sends a command to the panel
*
* @param command The command to be send
* @param data The associated command data
*/
public boolean sendCommand(String command, String data) {
logger.trace("sendCommand(): Attempting to send Command: command - {} - data: {}", command, data);
CaddxMessage msg = null;
switch (command) {
case CaddxBindingConstants.ZONE_BYPASSED:
msg = new CaddxMessage(CaddxMessageType.ZONE_BYPASS_TOGGLE, data);
break;
case CaddxBindingConstants.ZONE_STATUS_REQUEST:
msg = new CaddxMessage(CaddxMessageType.ZONE_STATUS_REQUEST, data);
break;
case CaddxBindingConstants.ZONE_NAME_REQUEST:
msg = new CaddxMessage(CaddxMessageType.ZONE_NAME_REQUEST, data);
break;
case CaddxBindingConstants.PARTITION_STATUS_REQUEST:
msg = new CaddxMessage(CaddxMessageType.PARTITION_STATUS_REQUEST, data);
break;
case CaddxBindingConstants.PARTITION_PRIMARY_COMMAND_WITH_PIN:
msg = new CaddxMessage(CaddxMessageType.PRIMARY_KEYPAD_FUNCTION_WITH_PIN, data);
break;
case CaddxBindingConstants.PARTITION_SECONDARY_COMMAND:
msg = new CaddxMessage(CaddxMessageType.SECONDARY_KEYPAD_FUNCTION, data);
break;
case CaddxBindingConstants.PANEL_SYSTEM_STATUS_REQUEST:
msg = new CaddxMessage(CaddxMessageType.SYSTEM_STATUS_REQUEST, data);
break;
case CaddxBindingConstants.PANEL_INTERFACE_CONFIGURATION_REQUEST:
msg = new CaddxMessage(CaddxMessageType.INTERFACE_CONFIGURATION_REQUEST, data);
break;
case CaddxBindingConstants.PANEL_LOG_EVENT_REQUEST:
msg = new CaddxMessage(CaddxMessageType.LOG_EVENT_REQUEST, data);
break;
default:
logger.debug("Unknown command {}", command);
return false;
}
CaddxCommunicator comm = communicator;
if (comm != null) {
comm.transmit(msg);
}
return true;
}
/**
* Register the Discovery Service.
*
* @param discoveryService
*/
public void registerDiscoveryService(CaddxDiscoveryService discoveryService) {
this.discoveryService = discoveryService;
logger.trace("registerDiscoveryService(): Discovery Service Registered!");
}
/**
* Unregister the Discovery Service.
*/
public void unregisterDiscoveryService() {
logger.trace("unregisterDiscoveryService(): Discovery Service Unregistered!");
discoveryService = null;
}
@Override
public void caddxMessage(CaddxCommunicator communicator, CaddxMessage caddxMessage) {
CaddxSource source = caddxMessage.getSource();
if (source != CaddxSource.NONE) {
CaddxThingType caddxThingType = null;
@Nullable
Integer partition = null;
@Nullable
Integer zone = null;
@Nullable
Integer keypad = null;
switch (source) {
case PANEL:
caddxThingType = CaddxThingType.PANEL;
break;
case PARTITION:
caddxThingType = CaddxThingType.PARTITION;
partition = Integer.parseInt(caddxMessage.getPropertyById("partition_number")) + 1;
break;
case ZONE:
caddxThingType = CaddxThingType.ZONE;
zone = Integer.parseInt(caddxMessage.getPropertyById("zone_number")) + 1;
break;
case KEYPAD:
caddxThingType = CaddxThingType.KEYPAD;
keypad = Integer.parseInt(caddxMessage.getPropertyById("keypad_address"));
break;
default:
logger.debug("Source has illegal value");
return;
}
CaddxEvent event = new CaddxEvent(caddxMessage, partition, zone, keypad);
// Find the thing
Thing thing = findThing(caddxThingType, partition, zone, keypad);
CaddxDiscoveryService discoveryService = getDiscoveryService();
if (thing != null) {
CaddxBaseThingHandler thingHandler = (CaddxBaseThingHandler) thing.getHandler();
if (thingHandler != null) {
thingHandler.caddxEventReceived(event, thing);
}
} else {
if (discoveryService != null) {
discoveryService.addThing(getThing(), caddxThingType, event);
}
}
// Handle specific messages that add multiple discovered things
if (discoveryService != null) {
switch (caddxMessage.getCaddxMessageType()) {
case PARTITIONS_SNAPSHOT_MESSAGE:
for (int i = 1; i <= 8; i++) {
if (caddxMessage.getPropertyById("partition_" + i + "_valid").equals("true")) {
thing = findThing(CaddxThingType.PARTITION, i, null, null);
if (thing != null) {
continue;
}
event = new CaddxEvent(caddxMessage, i, null, null);
discoveryService.addThing(getThing(), CaddxThingType.PARTITION, event);
}
}
break;
case ZONES_SNAPSHOT_MESSAGE:
int zoneOffset = Integer.parseInt(caddxMessage.getPropertyById("zone_offset"));
for (int i = 1; i <= 16; i++) {
if (caddxMessage.getPropertyById("zone_" + i + "_trouble").equals("false")) {
thing = findThing(CaddxThingType.ZONE, null, zoneOffset + i, null);
if (thing != null) {
continue;
}
event = new CaddxEvent(caddxMessage, null, zoneOffset + i, null);
discoveryService.addThing(getThing(), CaddxThingType.ZONE, event);
} else {
logger.debug("troubled zone: {}", zoneOffset + i);
}
}
break;
default:
break;
}
}
}
updateStatus(ThingStatus.ONLINE);
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(CaddxDiscoveryService.class);
}
@Override
public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
if (childHandler instanceof ThingHandlerPartition) {
BigDecimal id = (BigDecimal) childThing.getConfiguration()
.get(CaddxPartitionConfiguration.PARTITION_NUMBER);
thingPartitionsMap.put(id, childThing);
} else if (childHandler instanceof ThingHandlerZone) {
BigDecimal id = (BigDecimal) childThing.getConfiguration().get(CaddxZoneConfiguration.ZONE_NUMBER);
thingZonesMap.put(id, childThing);
} else if (childHandler instanceof ThingHandlerKeypad) {
BigDecimal id = (BigDecimal) childThing.getConfiguration().get(CaddxKeypadConfiguration.KEYPAD_ADDRESS);
thingKeypadsMap.put(id, childThing);
} else if (childHandler instanceof ThingHandlerPanel) {
thingPanel = childThing;
}
super.childHandlerInitialized(childHandler, childThing);
}
@Override
public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
if (childHandler instanceof ThingHandlerPartition) {
BigDecimal id = (BigDecimal) childThing.getConfiguration()
.get(CaddxPartitionConfiguration.PARTITION_NUMBER);
thingPartitionsMap.remove(id);
} else if (childHandler instanceof ThingHandlerZone) {
BigDecimal id = (BigDecimal) childThing.getConfiguration().get(CaddxZoneConfiguration.ZONE_NUMBER);
thingZonesMap.remove(id);
} else if (childHandler instanceof ThingHandlerKeypad) {
BigDecimal id = (BigDecimal) childThing.getConfiguration().get(CaddxKeypadConfiguration.KEYPAD_ADDRESS);
thingKeypadsMap.remove(id);
} else if (childHandler instanceof ThingHandlerPanel) {
thingPanel = null;
}
super.childHandlerDisposed(childHandler, childThing);
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Used to map thing types from the binding string to a ENUM value.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public enum CaddxThingType {
PANEL,
PARTITION,
ZONE,
KEYPAD;
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.caddx.internal.CaddxMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Used to parse panel log event messages.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class LogEventMessage {
private final Logger logger = LoggerFactory.getLogger(LogEventMessage.class);
public final String number;
public final String size;
public final String type;
public final String zud;
public final String partition;
public final String month;
public final String day;
public final String hour;
public final String minute;
LogEventMessage(CaddxMessage message) {
this.number = message.getPropertyById("panel_log_event_number");
this.size = message.getPropertyById("panel_log_event_size");
this.type = message.getPropertyById("panel_log_event_type");
this.zud = message.getPropertyById("panel_log_event_zud");
this.partition = message.getPropertyById("panel_log_event_partition");
this.month = message.getPropertyById("panel_log_event_month");
this.day = message.getPropertyById("panel_log_event_day");
this.hour = message.getPropertyById("panel_log_event_hour");
this.minute = message.getPropertyById("panel_log_event_minute");
}
@Override
public String toString() {
try {
StringBuilder sb = new StringBuilder();
int eventType = Integer.parseInt(type);
logger.trace("eventType received: {}", eventType);
LogEventType logEventType = LogEventType.valueOfLogEventType(eventType);
if (logEventType == null) {
return "Unknown log event type received";
}
// Date
sb.append(String.format("%02d", Integer.parseInt(day))).append('-')
.append(String.format("%02d", Integer.parseInt(month))).append(' ')
.append(String.format("%02d", Integer.parseInt(hour))).append(':')
.append(String.format("%02d", Integer.parseInt(minute))).append(' ');
sb.append(logEventType.description);
if (logEventType.isPartitionValid) {
sb.append(" Partition ").append(Integer.parseInt(partition) + 1);
}
switch (logEventType.zud) {
case None:
break;
case Zone:
sb.append(" Zone ").append(Integer.parseInt(zud) + 1);
break;
case User:
sb.append(" User ").append(Integer.parseInt(zud) + 1);
break;
case Device:
sb.append(" Device ").append(zud);
break;
}
return sb.toString();
} catch (NumberFormatException e) {
logger.debug("LogEventMessage error. {}", e.getMessage(), e);
return "logmessage cannot be constructed";
}
}
}

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* All the log event types
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public enum LogEventType {
ALARM(0, ZoneUserDevice.Zone, true, "Alarm"),
ALARM_RESTORE(1, ZoneUserDevice.Zone, true, "Alarm restore"),
BYPASS(2, ZoneUserDevice.Zone, true, "Bypass"),
BYPASS_RESTORE(3, ZoneUserDevice.Zone, true, "Bypass restore"),
TAMPER(4, ZoneUserDevice.Zone, true, "Tamper"),
TAMPER_RESTORE(5, ZoneUserDevice.Zone, true, "Tamper restore"),
TROUBLE(6, ZoneUserDevice.Zone, true, "Trouble"),
TROUBLE_RESTORE(7, ZoneUserDevice.Zone, true, "Trouble restore"),
TX_LOW_BATTERY(8, ZoneUserDevice.Zone, true, "TX low battery"),
TX_LOW_BATTERY_RESTORE(9, ZoneUserDevice.Zone, true, "TX low battery restore"),
ZONE_LOST(10, ZoneUserDevice.Zone, true, "Zone lost"),
ZONE_LOST_RESTORE(11, ZoneUserDevice.Zone, true, "Zone lost restore"),
START_OF_CROSS_TIME(12, ZoneUserDevice.Zone, true, "Start of cross time"),
SPECIAL_EXPANSION_EVENT(17, ZoneUserDevice.None, false, "Special expansion event"),
DURESS(18, ZoneUserDevice.None, true, "Duress"),
MANUAL_FIRE(19, ZoneUserDevice.None, true, "Manual fire"),
AUXILIARY2_PANIC(20, ZoneUserDevice.None, true, "Auxiliary 2 panic"),
PANIC(22, ZoneUserDevice.None, true, "Panic"),
KEYPAD_TAMPER(23, ZoneUserDevice.None, true, "Keypad tamper"),
CONTROL_BOX_TAMPER(24, ZoneUserDevice.Device, false, "Control box tamper"),
CONTROL_BOX_TAMPER_RESTORE(25, ZoneUserDevice.Device, false, "Control box tamper restore"),
AC_FAIL(26, ZoneUserDevice.Device, false, "AC fail"),
AC_FAIL_RESTORE(27, ZoneUserDevice.Device, false, "AC fail restore"),
LOW_BATTERY(28, ZoneUserDevice.Device, false, "Low battery"),
LOW_BATTERY_RESTORE(29, ZoneUserDevice.Device, false, "Low battery restore"),
OVER_CURRENT(30, ZoneUserDevice.Device, false, "Over-current"),
OVER_CURRENT_RESTORE(31, ZoneUserDevice.Device, false, "Over-current restore"),
SIREN_TAMPER(32, ZoneUserDevice.Device, false, "Siren tamper"),
SIREN_TAMPER_RESTORE(33, ZoneUserDevice.Device, false, "Siren tamper restore"),
TELEPHONE_FAULT(34, ZoneUserDevice.None, false, "Telephone fault"),
TELEPHONE_FAULT_RESTORE(35, ZoneUserDevice.None, false, "Telephone fault restore"),
EXPANDER_TROUBLE(36, ZoneUserDevice.Device, false, "Expander trouble"),
EXPANDER_TROUBLE_RESTORE(37, ZoneUserDevice.Device, false, "Expander trouble restore"),
FAIL_TO_COMMUNICATE(38, ZoneUserDevice.None, false, "Fail to communicate"),
LOG_FULL(39, ZoneUserDevice.None, false, "Log full"),
OPENING(40, ZoneUserDevice.User, true, "Opening"),
CLOSING(41, ZoneUserDevice.User, true, "Closing"),
EXIT_ERROR(42, ZoneUserDevice.User, true, "Exit error"),
RECENT_CLOSING(43, ZoneUserDevice.User, true, "Recent closing"),
AUTO_TEST(44, ZoneUserDevice.None, false, "Auto-test"),
START_PROGRAM(45, ZoneUserDevice.None, false, "Start program"),
END_PROGRAM(46, ZoneUserDevice.None, false, "End program"),
START_DOWNLOAD(47, ZoneUserDevice.None, false, "Start download"),
END_DOWNLOAD(48, ZoneUserDevice.None, false, "End download"),
CANCEL(49, ZoneUserDevice.User, true, "Cancel"),
GROUND_FAULT(50, ZoneUserDevice.None, false, "Ground fault"),
GROUND_FAULT_RESTORE(51, ZoneUserDevice.None, false, "Ground fault restore"),
MANUAL_TEST(52, ZoneUserDevice.None, false, "Manual test"),
CLOSED_WITH_ZONES_BYPASSED(53, ZoneUserDevice.User, true, "Closed with zones bypassed"),
START_OF_LISTEN_IN(54, ZoneUserDevice.None, false, "Start of listen in"),
TECHNICIAN_ON_SITE(55, ZoneUserDevice.None, false, "Technician on site"),
TECHNICIAN_LEFT(56, ZoneUserDevice.None, false, "Technician left"),
CONTROL_POWER_UP(57, ZoneUserDevice.None, false, "Control power up"),
FIRST_TO_OPEN(120, ZoneUserDevice.User, true, "First to open"),
LAST_TO_CLOSE(121, ZoneUserDevice.User, true, "Last toC close"),
PIN_ENTERED_WITH_BIT7_SET(122, ZoneUserDevice.User, true, "PIN entered with bit 7 set"),
BEGIN_WALK_TEST(123, ZoneUserDevice.None, false, "Begin walk-test"),
END_WALK_TEST(124, ZoneUserDevice.None, false, "End walk-test"),
RE_EXIT(125, ZoneUserDevice.None, true, "Re-exit"),
OUTPUT_TRIP(126, ZoneUserDevice.User, false, "Output trip"),
DATA_LOST(127, ZoneUserDevice.None, false, "Data Lost");
private static final Map<Integer, LogEventType> BY_LOG_EVENT_TYPE = new HashMap<>();
public final int eventType;
public final ZoneUserDevice zud;
public final boolean isPartitionValid;
public final String description;
LogEventType(int eventType, ZoneUserDevice zud, boolean isPartitionValid, String description) {
this.eventType = eventType;
this.zud = zud;
this.isPartitionValid = isPartitionValid;
this.description = description;
}
static {
for (LogEventType logEventType : values()) {
BY_LOG_EVENT_TYPE.put(logEventType.eventType, logEventType);
}
}
public static @Nullable LogEventType valueOfLogEventType(int eventType) {
return BY_LOG_EVENT_TYPE.get(eventType);
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.caddx.internal.CaddxEvent;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.Command;
/**
* This is a class for handling a Keypad type Thing.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class ThingHandlerKeypad extends CaddxBaseThingHandler {
/**
* Constructor.
*
* @param thing
*/
public ThingHandlerKeypad(Thing thing) {
super(thing, CaddxThingType.KEYPAD);
}
@Override
public void updateChannel(ChannelUID channelUID, String data) {
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void caddxEventReceived(CaddxEvent event, Thing thing) {
updateStatus(ThingStatus.ONLINE);
}
}

View File

@@ -0,0 +1,196 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import java.util.HashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.caddx.internal.CaddxBindingConstants;
import org.openhab.binding.caddx.internal.CaddxEvent;
import org.openhab.binding.caddx.internal.CaddxMessage;
import org.openhab.binding.caddx.internal.CaddxMessageType;
import org.openhab.binding.caddx.internal.CaddxProperty;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a class for handling a Panel type Thing.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class ThingHandlerPanel extends CaddxBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(ThingHandlerPanel.class);
private @Nullable HashMap<String, String> panelLogMessagesMap = null;
private @Nullable String communicatorStackPointer = null;
/**
* Constructor.
*
* @param thing
*/
public ThingHandlerPanel(Thing thing) {
super(thing, CaddxThingType.PANEL);
}
@Override
public void updateChannel(ChannelUID channelUID, String data) {
if (channelUID.getId().equals(CaddxBindingConstants.PANEL_FIRMWARE_VERSION)
|| channelUID.getId().startsWith("panel_log_message_")) {
updateState(channelUID, new StringType(data));
} else {
// All Panel channels are OnOffType
OnOffType onOffType;
onOffType = ("true".equals(data)) ? OnOffType.ON : OnOffType.OFF;
updateState(channelUID, onOffType);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.trace("handleCommand(): Command Received - {} {}.", channelUID, command);
String cmd = null;
String data = null;
CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
if (bridgeHandler == null) {
return;
}
if (command instanceof RefreshType) {
if (CaddxBindingConstants.PANEL_FIRMWARE_VERSION.equals(channelUID.getId())) {
cmd = CaddxBindingConstants.PANEL_INTERFACE_CONFIGURATION_REQUEST;
data = "";
} else if (CaddxBindingConstants.PANEL_LOG_MESSAGE_N_0.equals(channelUID.getId())) {
cmd = CaddxBindingConstants.PANEL_SYSTEM_STATUS_REQUEST;
data = "";
} else {
return;
}
bridgeHandler.sendCommand(cmd, data);
} else {
logger.debug("Unknown command {}", command);
}
}
@Override
public void caddxEventReceived(CaddxEvent event, Thing thing) {
logger.trace("caddxEventReceived(): Event Received - {}.", event);
if (getThing().equals(thing)) {
CaddxMessage message = event.getCaddxMessage();
CaddxMessageType mt = message.getCaddxMessageType();
ChannelUID channelUID = null;
// Log event messages have special handling
if (CaddxMessageType.SYSTEM_STATUS_MESSAGE.equals(mt)) {
handleSystemStatusMessage(message);
} else if (CaddxMessageType.LOG_EVENT_MESSAGE.equals(mt)) {
handleLogEventMessage(message);
} else {
for (CaddxProperty p : mt.properties) {
if (!p.getId().isEmpty()) {
String value = message.getPropertyById(p.getId());
channelUID = new ChannelUID(getThing().getUID(), p.getId());
updateChannel(channelUID, value);
}
}
}
updateStatus(ThingStatus.ONLINE);
}
}
/*
* Gets the pointer into the panel's log messages ring buffer
* and sends the command for the retrieval of the last event_message
*/
private void handleSystemStatusMessage(CaddxMessage message) {
// Get the bridge handler
CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
if (bridgeHandler == null) {
return;
}
String pointer = message.getPropertyById("panel_communicator_stack_pointer");
communicatorStackPointer = pointer;
// build map of log message channels to event numbers
HashMap<String, String> map = new HashMap<String, String>();
map.put(pointer, CaddxBindingConstants.PANEL_LOG_MESSAGE_N_0);
bridgeHandler.sendCommand(CaddxBindingConstants.PANEL_LOG_EVENT_REQUEST, pointer);
panelLogMessagesMap = map;
}
/*
* This function handles the panel log messages.
* If the received event_number matches our communication stack pointer then this is the last panel message. The
* channel gets updated and the required log message requests are generated for the update of the other log message
* channels
*/
private void handleLogEventMessage(CaddxMessage message) {
// Get the bridge handler
CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
if (bridgeHandler == null) {
return;
}
String eventNumberString = message.getPropertyById("panel_log_event_number");
String eventSizeString = message.getPropertyById("panel_log_event_size");
// build the message
LogEventMessage logEventMessage = new LogEventMessage(message);
logger.trace("Log_event: {}", logEventMessage);
// get the channel id from the map
HashMap<String, String> logMap = panelLogMessagesMap;
if (logMap != null && logMap.containsKey(eventNumberString)) {
String id = logMap.get(eventNumberString);
ChannelUID channelUID = new ChannelUID(getThing().getUID(), id);
updateChannel(channelUID, logEventMessage.toString());
}
if (communicatorStackPointer != null && eventNumberString.equals(communicatorStackPointer)) {
HashMap<String, String> map = new HashMap<String, String>();
int eventNumber = Integer.parseInt(eventNumberString);
int eventSize = Integer.parseInt(eventSizeString);
// Retrieve at maximum the 10 last log messages from the panel
int messagesToRetrieve = Math.min(eventSize, 10);
for (int i = 1; i < messagesToRetrieve; i++) {
eventNumber--;
if (eventNumber < 0) {
eventNumber = eventSize;
}
map.put(Integer.toString(eventNumber), "panel_log_message_n_" + i);
bridgeHandler.sendCommand(CaddxBindingConstants.PANEL_LOG_EVENT_REQUEST, Integer.toString(eventNumber));
}
communicatorStackPointer = null;
panelLogMessagesMap = map;
}
}
}

View File

@@ -0,0 +1,116 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.caddx.internal.CaddxBindingConstants;
import org.openhab.binding.caddx.internal.CaddxEvent;
import org.openhab.binding.caddx.internal.CaddxMessage;
import org.openhab.binding.caddx.internal.CaddxMessageType;
import org.openhab.binding.caddx.internal.CaddxProperty;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a class for handling a Partition type Thing.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class ThingHandlerPartition extends CaddxBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(ThingHandlerPartition.class);
/**
* Constructor.
*
* @param thing
*/
public ThingHandlerPartition(Thing thing) {
super(thing, CaddxThingType.PARTITION);
}
@Override
public void updateChannel(ChannelUID channelUID, String data) {
if (CaddxBindingConstants.PARTITION_SECONDARY_COMMAND.equals(channelUID.getId())) {
updateState(channelUID, new DecimalType(data));
} else {
OnOffType onOffType = ("true".equals(data)) ? OnOffType.ON : OnOffType.OFF;
updateState(channelUID, onOffType);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand(): Command Received - {} {}.", channelUID, command);
String cmd = null;
String data = null;
CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
if (bridgeHandler == null) {
return;
}
if (command instanceof RefreshType) {
if (channelUID.getId().equals(CaddxBindingConstants.PARTITION_ARMED)) {
cmd = CaddxBindingConstants.PARTITION_STATUS_REQUEST;
data = String.format("%d", getPartitionNumber() - 1);
} else {
return;
}
} else if (channelUID.getId().equals(CaddxBindingConstants.PARTITION_SECONDARY_COMMAND)) {
cmd = channelUID.getId();
data = String.format("%s,%d", command.toString(), (1 << getPartitionNumber() - 1));
} else {
logger.debug("Unknown command {}", command);
return;
}
if (!data.startsWith("-")) {
bridgeHandler.sendCommand(cmd, data);
}
}
@Override
public void caddxEventReceived(CaddxEvent event, Thing thing) {
logger.trace("caddxEventReceived(): Event Received - {}", event);
if (getThing().equals(thing)) {
CaddxMessage message = event.getCaddxMessage();
CaddxMessageType mt = message.getCaddxMessageType();
ChannelUID channelUID = null;
for (CaddxProperty p : mt.properties) {
if (!p.getId().isEmpty()) {
String value = message.getPropertyById(p.getId());
channelUID = new ChannelUID(getThing().getUID(), p.getId());
updateChannel(channelUID, value);
}
}
// Reset the command
String value = "-1";
channelUID = new ChannelUID(getThing().getUID(), CaddxBindingConstants.PARTITION_SECONDARY_COMMAND);
updateChannel(channelUID, value);
updateStatus(ThingStatus.ONLINE);
}
}
}

View File

@@ -0,0 +1,128 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.caddx.internal.CaddxBindingConstants;
import org.openhab.binding.caddx.internal.CaddxEvent;
import org.openhab.binding.caddx.internal.CaddxMessage;
import org.openhab.binding.caddx.internal.CaddxMessageType;
import org.openhab.binding.caddx.internal.CaddxProperty;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a class for handling a Zone type Thing.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
public class ThingHandlerZone extends CaddxBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(ThingHandlerZone.class);
/**
* Constructor.
*
* @param thing
*/
public ThingHandlerZone(Thing thing) {
super(thing, CaddxThingType.ZONE);
}
@Override
public void updateChannel(ChannelUID channelUID, String data) {
if (channelUID.getId().equals(CaddxBindingConstants.ZONE_NAME)) {
getThing().setLabel(data);
updateState(channelUID, new StringType(data));
logger.trace(" updateChannel: {} = {}", channelUID, data);
} else if (channelUID.getId().equals(CaddxBindingConstants.ZONE_FAULTED)) {
OpenClosedType openClosedType = ("true".equals(data)) ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
updateState(channelUID, openClosedType);
logger.trace(" updateChannel: {} = {}", channelUID, data);
} else {
OnOffType onOffType = ("true".equals(data)) ? OnOffType.ON : OnOffType.OFF;
updateState(channelUID, onOffType);
logger.trace(" updateChannel: {} = {}", channelUID, onOffType);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.trace("handleCommand(): Command Received - {} {}.", channelUID, command);
String cmd1 = null;
String cmd2 = null;
String data = null;
if (command instanceof RefreshType) {
if (channelUID.getId().equals(CaddxBindingConstants.ZONE_FAULTED)) {
cmd1 = CaddxBindingConstants.ZONE_STATUS_REQUEST;
cmd2 = CaddxBindingConstants.ZONE_NAME_REQUEST;
data = String.format("%d", getZoneNumber() - 1);
} else {
return;
}
} else if (channelUID.getId().equals(CaddxBindingConstants.ZONE_BYPASSED)) {
cmd1 = channelUID.getId();
cmd2 = CaddxBindingConstants.ZONE_STATUS_REQUEST;
data = String.format("%d", getZoneNumber() - 1);
} else {
logger.debug("Unknown command {}", command);
return;
}
CaddxBridgeHandler bridgeHandler = getCaddxBridgeHandler();
if (bridgeHandler == null) {
return;
}
bridgeHandler.sendCommand(cmd1, data);
bridgeHandler.sendCommand(cmd2, data);
}
@Override
public void caddxEventReceived(CaddxEvent event, Thing thing) {
logger.trace("caddxEventReceived(): Event Received - {}", event);
if (getThing().equals(thing)) {
CaddxMessage message = event.getCaddxMessage();
CaddxMessageType mt = message.getCaddxMessageType();
ChannelUID channelUID = null;
for (CaddxProperty p : mt.properties) {
logger.trace(" Checking property: {}", p.getName());
if (!p.getId().isEmpty()) {
String value = message.getPropertyById(p.getId());
channelUID = new ChannelUID(getThing().getUID(), p.getId());
updateChannel(channelUID, value);
logger.trace(" updateChannel: {} = {}", channelUID, value);
}
}
updateStatus(ThingStatus.ONLINE);
}
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.caddx.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Zone, User, Device enumeration.
*
* @author Georgios Moutsos - Initial contribution
*/
@NonNullByDefault
enum ZoneUserDevice {
None,
Zone,
User,
Device
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="caddx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Caddx Security Binding</name>
<description>Binding for Caddx security system with RS232 serial interface.</description>
<author>Georgios Moutsos</author>
</binding:binding>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="caddx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="bridge">
<label>Caddx</label>
<description>This bridge represents the Caddx Serial interface.</description>
<channels>
<channel id="send_command" typeId="command">
<label>Send Command</label>
<description>Sends an Alarm Panel Command</description>
</channel>
</channels>
<config-description>
<parameter name="serialPort" type="text" required="true">
<context>serial-port</context>
<label>Caddx Bridge Serial Port</label>
<description>The serial port name for the communication. Valid values
are e.g. COM1 for Windows and /dev/ttyS0 or
/dev/ttyUSB0 for Linux.</description>
</parameter>
<parameter name="baudrate" type="integer" required="true">
<label>Baud Rate</label>
<description>The baud rate of the serial port. Valid values for the NX-584E are 600, 1200, 2400, 4800, 9600
(default), 19200, 38400, and 76800. Valid values for the NX-8E are 2400, 4800, 9600
(default), 19200 and 38400.</description>
<default>9600</default>
<options>
<option value="600">600</option>
<option value="1200">1200</option>
<option value="2400">2400</option>
<option value="4800">4800</option>
<option value="9600">9600</option>
<option value="19200">19200</option>
<option value="38400">38400</option>
<option value="76800">76800</option>
</options>
</parameter>
<parameter name="protocol" type="text" required="true">
<context>protocol</context>
<label>Caddx Bridge Protocol</label>
<description>The configured panel protocol. Valid values
are Binary and Ascii.</description>
<default>Binary</default>
<options>
<option value="Ascii">Ascii</option>
<option value="Binary">Binary</option>
</options>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="caddx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- bridge -->
<channel-type id="reset">
<item-type>Switch</item-type>
<label>Reset</label>
<description>Reset Switch</description>
</channel-type>
<channel-type id="command">
<item-type>String</item-type>
<label>Send Command</label>
<description>Sends a Command</description>
</channel-type>
<!-- panel -->
<channel-type id="panel_text">
<item-type>String</item-type>
<label>Panel Text</label>
<description>Panel text</description>
<state pattern="%s" readOnly="true"></state>
</channel-type>
<channel-type id="panel_flag">
<item-type>Switch</item-type>
<label>Panel Flag</label>
<description>Panel flag</description>
<state readOnly="true"></state>
</channel-type>
<!-- partition -->
<channel-type id="partition_condition">
<item-type>Switch</item-type>
<label>Partition Condition</label>
<description>Partition Condition</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="partition_secondary">
<item-type>Number</item-type>
<label>Partition Secondary Command</label>
<description>Partition secondary command</description>
<state>
<options>
<option value="-1">None</option>
<option value="0">Stay (1 button arm / toggle interiors)</option>
<option value="1">Chime (toggle chime mode)</option>
<option value="2">Exit (1 button arm / toggle instant)</option>
<option value="3">Bypass interiors</option>
<option value="4">Fire Panic</option>
<option value="5">Medical Panic</option>
<option value="6">Police Panic</option>
<option value="7">Smoke detector reset</option>
<option value="8">Auto callback download</option>
<option value="9">Manual pickup download</option>
<option value="10">Enable silent exit (for this arm cycle)</option>
<option value="11">Perform test</option>
<option value="12">Group Bypass</option>
<option value="13">Auxiliary function 1</option>
<option value="14">Auxiliary function 2</option>
<option value="15">Start keypad sounder</option>
</options>
</state>
</channel-type>
<!-- zone -->
<channel-type id="zone_text">
<item-type>String</item-type>
<label>Zone Text</label>
<description>Zone text</description>
<state pattern="%s" readOnly="true"></state>
</channel-type>
<channel-type id="zone_partition">
<item-type>Switch</item-type>
<label>Zone Partition</label>
<description>Zone Partition</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="zone_type">
<item-type>Switch</item-type>
<label>Zone Type</label>
<description>Zone Type</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="zone_condition">
<item-type>Switch</item-type>
<label>Zone Condition</label>
<description>Zone Condition</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="zone_status">
<item-type>Contact</item-type>
<label>Zone Status</label>
<description>Zone Status (Open/Closed)</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="zone_bypass">
<item-type>Switch</item-type>
<label>Bypass Mode</label>
<description>Bypass Mode (OFF=Armed, ON=Bypassed)</description>
</channel-type>
<!-- keypad -->
<channel-type id="led">
<item-type>Number</item-type>
<label>Keypad Led</label>
<description>Keypad Led (0=Off, 1=On, 2=Flashing)</description>
<state pattern="%d" readOnly="true">
<options>
<option value="0">Off</option>
<option value="1">On</option>
<option value="2">Flashing</option>
</options>
</state>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="caddx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="keypad">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Caddx Alarm Keypad</label>
<description>Represents any of the keypads of the Caddx Alarm System.</description>
<representation-property>keypadAddress</representation-property>
<config-description>
<parameter name="keypadAddress" type="text" required="true">
<label>Keypad Address</label>
<description>The Keypad Address.</description>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="caddx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="panel">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Caddx Alarm Panel</label>
<description>The basic representation of the Caddx Alarm System.</description>
<channels>
<channel id="panel_firmware_version" typeId="panel_text">
<label>Firmware Version</label>
<description>Firmware version</description>
</channel>
<channel id="panel_log_message_n_0" typeId="panel_text">
<label>Log Message 10</label>
<description>Log message 10</description>
</channel>
<channel id="panel_log_message_n_1" typeId="panel_text">
<label>Log Message 9</label>
<description>Log message 9</description>
</channel>
<channel id="panel_log_message_n_2" typeId="panel_text">
<label>Log Message 8</label>
<description>Log message 8</description>
</channel>
<channel id="panel_log_message_n_3" typeId="panel_text">
<label>Log Message 7</label>
<description>Log message 7</description>
</channel>
<channel id="panel_log_message_n_4" typeId="panel_text">
<label>Log Message 6</label>
<description>Log message 6</description>
</channel>
<channel id="panel_log_message_n_5" typeId="panel_text">
<label>Log Message 5</label>
<description>Log message 5</description>
</channel>
<channel id="panel_log_message_n_6" typeId="panel_text">
<label>Log Message 4</label>
<description>Log message 4</description>
</channel>
<channel id="panel_log_message_n_7" typeId="panel_text">
<label>Log Message 3</label>
<description>Log message 3</description>
</channel>
<channel id="panel_log_message_n_8" typeId="panel_text">
<label>Log Message 2</label>
<description>Log message 2</description>
</channel>
<channel id="panel_log_message_n_9" typeId="panel_text">
<label>Log Message 1</label>
<description>Log message 1</description>
</channel>
<channel id="panel_interface_configuration_message" typeId="panel_flag">
<label>Interface Configuration Message</label>
<description>Interface Configuration Message</description>
</channel>
<channel id="panel_zone_status_message" typeId="panel_flag">
<label>Zone Status Message</label>
<description>Zone Status Message</description>
</channel>
<channel id="panel_zones_snapshot_message" typeId="panel_flag">
<label>Zones Snapshot Message</label>
<description>Zones Snapshot Message</description>
</channel>
<channel id="panel_partition_status_message" typeId="panel_flag">
<label>Partition Status Message</label>
<description>Partition Status Message</description>
</channel>
<channel id="panel_partitions_snapshot_message" typeId="panel_flag">
<label>Partitions Snapshot Message</label>
<description>Partitions Snapshot Message</description>
</channel>
<channel id="panel_system_status_message" typeId="panel_flag">
<label>System Status Message</label>
<description>System Status Message</description>
</channel>
<channel id="panel_x10_message_received" typeId="panel_flag">
<label>X-10 Message Received</label>
<description>X-10 Message Received</description>
</channel>
<channel id="panel_log_event_message" typeId="panel_flag">
<label>Log Event Message</label>
<description>Log Event Message</description>
</channel>
<channel id="panel_keypad_message_received" typeId="panel_flag">
<label>Keypad Message Received</label>
<description>Keypad Message Received</description>
</channel>
<channel id="panel_interface_configuration_request" typeId="panel_flag">
<label>Interface Configuration Request</label>
<description>Interface Configuration Request</description>
</channel>
<channel id="panel_zone_name_request" typeId="panel_flag">
<label>Zone Name Request</label>
<description>Zone Name Request</description>
</channel>
<channel id="panel_zone_status_request" typeId="panel_flag">
<label>Zone Status Request</label>
<description>Zone Status Request</description>
</channel>
<channel id="panel_zones_snapshot_request" typeId="panel_flag">
<label>Zones Snapshot Request</label>
<description>Zones Snapshot Request</description>
</channel>
<channel id="panel_partition_status_request" typeId="panel_flag">
<label>Partition Status Request</label>
<description>Partition Status Request</description>
</channel>
<channel id="panel_partitions_snapshot_request" typeId="panel_flag">
<label>Partitions Snapshot Request</label>
<description>Partitions Snapshot Request</description>
</channel>
<channel id="panel_system_status_request" typeId="panel_flag">
<label>System Status Request</label>
<description>System Status Request</description>
</channel>
<channel id="panel_send_x10_message" typeId="panel_flag">
<label>Send X-10 Message</label>
<description>Send X-10 Message</description>
</channel>
<channel id="panel_log_event_request" typeId="panel_flag">
<label>Log Event Request</label>
<description>Log Event Request</description>
</channel>
<channel id="panel_send_keypad_text_message" typeId="panel_flag">
<label>Send Keypad Text Message</label>
<description>Send Keypad Text Message</description>
</channel>
<channel id="panel_keypad_terminal_mode_request" typeId="panel_flag">
<label>Keypad Terminal Mode Request</label>
<description>Keypad Terminal Mode Request</description>
</channel>
<channel id="panel_program_data_request" typeId="panel_flag">
<label>Program Data Request</label>
<description>Program Data Request</description>
</channel>
<channel id="panel_program_data_command" typeId="panel_flag">
<label>Program Data Command</label>
<description>Program Data Command</description>
</channel>
<channel id="panel_user_information_request_with_pin" typeId="panel_flag">
<label>User Information Request with PIN</label>
<description>User Information Request with PIN</description>
</channel>
<channel id="panel_user_information_request_without_pin" typeId="panel_flag">
<label>User Information Request without PIN</label>
<description>User Information Request without PIN</description>
</channel>
<channel id="panel_set_user_code_command_with_pin" typeId="panel_flag">
<label>Set User Code Command with PIN</label>
<description>Set User Code Command with PIN</description>
</channel>
<channel id="panel_set_user_code_command_without_pin" typeId="panel_flag">
<label>Set User Code Command without PIN</label>
<description>Set User Code Command without PIN</description>
</channel>
<channel id="panel_set_user_authorization_command_with_pin" typeId="panel_flag">
<label>Set User Authorization Command with PIN</label>
<description>Set User Authorization Command with PIN</description>
</channel>
<channel id="panel_set_user_authorization_command_without_pin" typeId="panel_flag">
<label>Set User Authorization Command without PIN</label>
<description>Set User Authorization Command without PIN</description>
</channel>
<channel id="panel_store_communication_event_command" typeId="panel_flag">
<label>Store Communication Event Command</label>
<description>Store Communication Event Command</description>
</channel>
<channel id="panel_set_clock_calendar_command" typeId="panel_flag">
<label>Set Clock / Calendar Command</label>
<description>Set Clock / Calendar Command</description>
</channel>
<channel id="panel_primary_keypad_function_with_pin" typeId="panel_flag">
<label>Primary Keypad Function with PIN</label>
<description>Primary Keypad Function with PIN</description>
</channel>
<channel id="panel_primary_keypad_function_without_pin" typeId="panel_flag">
<label>Primary Keypad Function without PIN</label>
<description>Primary Keypad Function without PIN</description>
</channel>
<channel id="panel_secondary_keypad_function" typeId="panel_flag">
<label>Secondary Keypad Function</label>
<description>Secondary Keypad Function</description>
</channel>
<channel id="panel_zone_bypass_toggle" typeId="panel_flag">
<label>Zone Bypass Toggle</label>
<description>Zone Bypass Toggle</description>
</channel>
</channels>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="caddx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="partition">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Caddx Alarm Partition</label>
<description>Represents a controllable area within a Caddx Alarm System.</description>
<channels>
<channel id="partition_bypass_code_required" typeId="partition_condition">
<label>Bypass Code Required</label>
<description>Bypass code required</description>
</channel>
<channel id="partition_fire_trouble" typeId="partition_condition">
<label>Fire Trouble</label>
<description>Fire trouble</description>
</channel>
<channel id="partition_fire" typeId="partition_condition">
<label>Fire</label>
<description>Fire</description>
</channel>
<channel id="partition_pulsing_buzzer" typeId="partition_condition">
<label>Pulsing Buzzer</label>
<description>Pulsing Buzzer</description>
</channel>
<channel id="partition_tlm_fault_memory" typeId="partition_condition">
<label>TLM Fault Memory</label>
<description>TLM fault memory</description>
</channel>
<channel id="partition_armed" typeId="partition_condition">
<label>Armed</label>
<description>Armed</description>
</channel>
<channel id="partition_instant" typeId="partition_condition">
<label>Instant</label>
<description>Instant</description>
</channel>
<channel id="partition_previous_alarm" typeId="partition_condition">
<label>Previous Alarm</label>
<description>Previous Alarm</description>
</channel>
<channel id="partition_siren_on" typeId="partition_condition">
<label>Siren On</label>
<description>Siren on</description>
</channel>
<channel id="partition_steady_siren_on" typeId="partition_condition">
<label>Steady Siren On</label>
<description>Steady siren on</description>
</channel>
<channel id="partition_alarm_memory" typeId="partition_condition">
<label>Alarm Memory</label>
<description>Alarm memory</description>
</channel>
<channel id="partition_tamper" typeId="partition_condition">
<label>Tamper</label>
<description>Tamper</description>
</channel>
<channel id="partition_cancel_command_entered" typeId="partition_condition">
<label>Cancel Command Entered</label>
<description>Cancel command entered</description>
</channel>
<channel id="partition_code_entered" typeId="partition_condition">
<label>Code Entered</label>
<description>Code entered</description>
</channel>
<channel id="partition_cancel_pending" typeId="partition_condition">
<label>Cancel Pending</label>
<description>Cancel pending</description>
</channel>
<channel id="partition_silent_exit_enabled" typeId="partition_condition">
<label>Silent Exit Enabled</label>
<description>Silent exit enabled</description>
</channel>
<channel id="partition_entryguard" typeId="partition_condition">
<label>Entryguard (Stay Mode)</label>
<description>Entryguard (stay mode)</description>
</channel>
<channel id="partition_chime_mode_on" typeId="partition_condition">
<label>Chime Mode On</label>
<description>Chime mode on</description>
</channel>
<channel id="partition_entry" typeId="partition_condition">
<label>Entry</label>
<description>Entry</description>
</channel>
<channel id="partition_delay_expiration_warning" typeId="partition_condition">
<label>Delay Expiration Warning</label>
<description>Delay expiration warning</description>
</channel>
<channel id="partition_exit1" typeId="partition_condition">
<label>Exit1</label>
<description>Exit1</description>
</channel>
<channel id="partition_exit2" typeId="partition_condition">
<label>Exit2</label>
<description>Exit2</description>
</channel>
<channel id="partition_led_extinguish" typeId="partition_condition">
<label>Led Extinguish</label>
<description>Led extinguish</description>
</channel>
<channel id="partition_cross_timing" typeId="partition_condition">
<label>Cross Timing</label>
<description>Cross timing</description>
</channel>
<channel id="partition_recent_closing_being_timed" typeId="partition_condition">
<label>Recent Closing Being Timed</label>
<description>Recent closing being timed</description>
</channel>
<channel id="partition_exit_error_triggered" typeId="partition_condition">
<label>Exit Error Triggered</label>
<description>Exit error triggered</description>
</channel>
<channel id="partition_auto_home_inhibited" typeId="partition_condition">
<label>Auto Home Inhibited</label>
<description>Auto home inhibited</description>
</channel>
<channel id="partition_sensor_low_battery" typeId="partition_condition">
<label>Sensor Low Battery</label>
<description>Sensor low battery</description>
</channel>
<channel id="partition_sensor_lost_supervision" typeId="partition_condition">
<label>Sensor Lost Supervision</label>
<description>Sensor lost supervision</description>
</channel>
<channel id="partition_zone_bypassed" typeId="partition_condition">
<label>Zone Bypassed</label>
<description>Zone bypassed</description>
</channel>
<channel id="partition_force_arm_triggered_by_auto_arm" typeId="partition_condition">
<label>Force Arm triggered by Auto Arm</label>
<description>Force arm triggered by auto arm</description>
</channel>
<channel id="partition_ready_to_arm" typeId="partition_condition">
<label>Ready to Arm</label>
<description>Ready to arm</description>
</channel>
<channel id="partition_ready_to_force_arm" typeId="partition_condition">
<label>Ready to Force Arm</label>
<description>Ready to force arm</description>
</channel>
<channel id="partition_valid_pin_accepted" typeId="partition_condition">
<label>Valid PIN Accepted</label>
<description>Valid PIN accepted</description>
</channel>
<channel id="partition_chime_on" typeId="partition_condition">
<label>Chime On (Sounding)</label>
<description>Chime on (sounding)</description>
</channel>
<channel id="partition_error_beep" typeId="partition_condition">
<label>Error Beep (Triple Beep)</label>
<description>Error beep (triple beep)</description>
</channel>
<channel id="partition_tone_on" typeId="partition_condition">
<label>Tone On (Activation Tone)</label>
<description>Tone on (activation tone)</description>
</channel>
<channel id="partition_entry1" typeId="partition_condition">
<label>Entry 1</label>
<description>Entry 1</description>
</channel>
<channel id="partition_open_period" typeId="partition_condition">
<label>Open Period</label>
<description>Open period</description>
</channel>
<channel id="partition_alarm_sent_using_phone_number_1" typeId="partition_condition">
<label>Alarm Sent Using Phone 1</label>
<description>Alarm sent using phone number 1</description>
</channel>
<channel id="partition_alarm_sent_using_phone_number_2" typeId="partition_condition">
<label>Alarm Sent Using Phone 2</label>
<description>Alarm sent using phone number 2</description>
</channel>
<channel id="partition_alarm_sent_using_phone_number_3" typeId="partition_condition">
<label>Alarm Sent Using Phone 3</label>
<description>Alarm sent using phone number 3</description>
</channel>
<channel id="partition_cancel_report_is_in_the_stack" typeId="partition_condition">
<label>Cancel Report is in the Stack</label>
<description>Cancel report is in the stack</description>
</channel>
<channel id="partition_keyswitch_armed" typeId="partition_condition">
<label>Keyswitch Armed</label>
<description>Keyswitch armed</description>
</channel>
<channel id="partition_delay_trip_in_progress" typeId="partition_condition">
<label>Delay Trip in Progress</label>
<description>Delay Trip in progress (common zone)</description>
</channel>
<channel id="partition_secondary_command" typeId="partition_secondary">
<label>Partition Secondary Command</label>
<description>Partition Secondary Command</description>
</channel>
</channels>
<representation-property>partitionNumber</representation-property>
<config-description>
<parameter name="partitionNumber" type="integer" required="true" min="1" max="8">
<label>Partition Number</label>
<description>The Partition Number.</description>
<default>1</default>
</parameter>
<parameter name="userNumber" type="integer" required="true" min="1" max="192">
<label>User Number</label>
<description>The User Number.</description>
<default>1</default>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="caddx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="zone">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Caddx Alarm Zone</label>
<description>Represents a physical device such as a door, window, or motion sensor.</description>
<channels>
<!-- Zone partitions -->
<channel id="zone_partition1" typeId="zone_partition">
<label>Partition 1</label>
<description>Partition 1</description>
</channel>
<channel id="zone_partition2" typeId="zone_partition">
<label>Partition 2</label>
<description>Partition 2</description>
</channel>
<channel id="zone_partition3" typeId="zone_partition">
<label>Partition 3</label>
<description>Partition 3</description>
</channel>
<channel id="zone_partition4" typeId="zone_partition">
<label>Partition 4</label>
<description>Partition 4</description>
</channel>
<channel id="zone_partition5" typeId="zone_partition">
<label>Partition 5</label>
<description>Partition 5</description>
</channel>
<channel id="zone_partition6" typeId="zone_partition">
<label>Partition 6</label>
<description>Partition 6</description>
</channel>
<channel id="zone_partition7" typeId="zone_partition">
<label>Partition 7</label>
<description>Partition 7</description>
</channel>
<channel id="zone_partition8" typeId="zone_partition">
<label>Partition 8</label>
<description>Partition 8</description>
</channel>
<!-- Name -->
<channel id="zone_name" typeId="zone_text">
<label>Name</label>
<description>Name</description>
</channel>
<!-- Zone types -->
<channel id="zone_fire" typeId="zone_type">
<label>Fire</label>
<description>Fire</description>
</channel>
<channel id="zone_24hour" typeId="zone_type">
<label>24 Hour</label>
<description>24 Hour</description>
</channel>
<channel id="zone_key_switch" typeId="zone_type">
<label>Key-switch</label>
<description>Key-switch</description>
</channel>
<channel id="zone_follower" typeId="zone_type">
<label>Follower</label>
<description>Follower</description>
</channel>
<channel id="zone_entry_exit_delay_1" typeId="zone_type">
<label>Entry / Exit Delay 1</label>
<description>Entry / exit delay 1</description>
</channel>
<channel id="zone_entry_exit_delay_2" typeId="zone_type">
<label>Entry / Exit Delay 2</label>
<description>Entry / exit delay 2</description>
</channel>
<channel id="zone_interior" typeId="zone_type">
<label>Interior</label>
<description>Interior</description>
</channel>
<channel id="zone_local_only" typeId="zone_type">
<label>Local Only</label>
<description>Local only</description>
</channel>
<channel id="zone_keypad_sounder" typeId="zone_type">
<label>Keypad Sounder</label>
<description>Keypad Sounder</description>
</channel>
<channel id="zone_yelping_siren" typeId="zone_type">
<label>Yelping Siren</label>
<description>Yelping siren</description>
</channel>
<channel id="zone_steady_siren" typeId="zone_type">
<label>Steady Siren</label>
<description>Steady siren</description>
</channel>
<channel id="zone_chime" typeId="zone_type">
<label>Chime</label>
<description>Chime</description>
</channel>
<channel id="zone_bypassable" typeId="zone_type">
<label>Bypassable</label>
<description>Bypassable</description>
</channel>
<channel id="zone_group_bypassable" typeId="zone_type">
<label>Group Bypassable</label>
<description>Group bypassable</description>
</channel>
<channel id="zone_force_armable" typeId="zone_type">
<label>Force Armable</label>
<description>Force armable</description>
</channel>
<channel id="zone_entry_guard" typeId="zone_type">
<label>Entry Guard</label>
<description>Entry guard</description>
</channel>
<channel id="zone_fast_loop_response" typeId="zone_type">
<label>Fast Loop Response</label>
<description>Fast loop response</description>
</channel>
<channel id="zone_double_eol_tamper" typeId="zone_type">
<label>Double EOL Tamper</label>
<description>Double EOL tamper</description>
</channel>
<channel id="zone_type_trouble" typeId="zone_type">
<label>Trouble</label>
<description>Trouble</description>
</channel>
<channel id="zone_cross_zone" typeId="zone_type">
<label>Cross Zone</label>
<description>Cross zone</description>
</channel>
<channel id="zone_dialer_delay" typeId="zone_type">
<label>Dialer Delay</label>
<description>Dialer delay</description>
</channel>
<channel id="zone_swinger_shutdown" typeId="zone_type">
<label>Swinger Shutdown</label>
<description>Swinger shutdown</description>
</channel>
<channel id="zone_restorable" typeId="zone_type">
<label>Restorable</label>
<description>Restorable</description>
</channel>
<channel id="zone_listen_in" typeId="zone_type">
<label>Listen in</label>
<description>Listen in</description>
</channel>
<!-- Zone conditions -->
<channel id="zone_faulted" typeId="zone_status">
<label>Faulted</label>
<description>Faulted (or delayed trip)</description>
</channel>
<channel id="zone_tampered" typeId="zone_condition">
<label>Tampered</label>
<description>Tampered</description>
</channel>
<channel id="zone_trouble" typeId="zone_condition">
<label>Trouble</label>
<description>Trouble</description>
</channel>
<channel id="zone_bypassed" typeId="zone_bypass">
<label>Bypassed</label>
<description>Bypassed</description>
</channel>
<channel id="zone_inhibited" typeId="zone_condition">
<label>Inhibited (Force Armed)</label>
<description>Inhibited (force armed)</description>
</channel>
<channel id="zone_low_battery" typeId="zone_condition">
<label>Low Battery</label>
<description>Low Battery</description>
</channel>
<channel id="zone_loss_of_supervision" typeId="zone_condition">
<label>Loss of Supervision</label>
<description>Loss of supervision</description>
</channel>
<channel id="zone_alarm_memory" typeId="zone_condition">
<label>Alarm Memory</label>
<description>Alarm memory</description>
</channel>
<channel id="zone_bypass_memory" typeId="zone_condition">
<label>Bypass Memory</label>
<description>Bypass memory</description>
</channel>
</channels>
<representation-property>zoneNumber</representation-property>
<config-description>
<parameter name="zoneNumber" type="integer" required="true" min="1" max="192">
<label>Zone Number</label>
<description>The Zone Number.</description>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>