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,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.caddx</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab2-addons

View File

@@ -0,0 +1,273 @@
# Caddx Binding
The Caddx binding is used for communicating with the Caddx alarm panels. Also known as Interlogix.
It provides connectivity to the NetworX alarm panels via a RS-232 serial connection to the NX-584E interface or directly to the NX-8E.
## Supported Things
This binding supports the following Thing types
| Thing | Thing Type | Description |
|------------|------------|------------------------------------------------------------------------|
| bridge | Bridge | The RS-232 interface. |
| panel | Thing | The basic representation of the alarm System. |
| partition | Thing | Represents a controllable area within the alarm system. |
| zone | Thing | Represents a physical device such as a door, window, or motion sensor. |
| keypad | Thing | Represents a keypad. (Not yet functional) |
## Discovery
First the bridge must be **manually** defined. The serial port, baud rate and protocol have to be set correctly to match the respective configuration of the panel (see below Prerequisites section).
After the bridge is manually added and available to openHAB, the binding will automatically start to discover partitions and zones and add them to the discovery inbox.
Note:
There is currently no support to discover the available keypads.
## Prerequisites
For the binding to work the panel has also to be programmed appropriately.
### Programming locations for the NX-8E control panel
| Location | Segment | Value | Description |
|----------|---------|-------------|--------------------------------------------------------------------------------------------------------|
| 207 | 1 | 1 | Serial Port selector |
| 208 | 1 | 0 - 4 | Baud rate table<br>0 = 2400 Baud<br>1 = 4800 Baud<br>2 = 9600 Baud<br>3 = 19200 Baud<br>4 = 38400 Baud |
| 209 | 1 | Off/On | Home automation protocol<br>1 = Off: Binary. On: ASCII. |
| 210 | 1 | 2,5,6,7,8 | Enabling the Transitions |
| 210 | 2 | 1,2,3,4 | |
| 211 | 1 | 2,4,5,6,7,8 | Programming the Command/Request enables |
| 211 | 2 | 1,2,3,4,5 | (Flags 4 and 5 are not yet functional. Can be ignored.) |
| 211 | 3 | | |
| 211 | 4 | 5,7,8 | |
| 212 | 1 | 192 | Programming the LCD keypad address. (Not yet functional. Can be ignored.) |
### Programming locations for the NX-584E home automation module
| Location | Segment | Value | Description |
|----------|---------|-------------|--------------------------------------------------------------------------------------------------------|
| 0 | 1 | Off/On | Home automation protocol<br>1 = Off: Binary. On: ASCII. |
| 1 | 1 | 0 - 4 | Baud rate table<br>0 = 600 Baud<br>1 = 1200 Baud<br>2 = 2400 Baud<br>3 = 4800 Baud<br>4 = 9600 Baud<br>5 = 19200 Baud<br>6 = 38400 Baud<br>7 = 76800 Baud |
| 2 | 1 | 2,5,6,7,8 | Enabling the Transitions |
| 2 | 2 | 1,2,3,4 | |
| 3 | 1 | 2,4,5,6,7,8 | Programming the Command/Request enables |
| 3 | 2 | 1,2,3,4,5 | (Flags 4 and 5 are not yet functional. Can be ignored.) |
| 3 | 3 | | |
| 3 | 4 | 5,7,8 | |
| 4 | 1 | 192 | Programming the LCD keypad address. (Not yet functional. Can be ignored.) |
## Thing Configuration
The things can be configured either through the online configuration utility via discovery, or manually through the configuration file.
The following table shows the available configuration parameters for each thing.
| Thing | Configuration Parameters |
|-----------|------------------------------------------------------------------------------------------------|
| bridge | `serialPort` - Serial port for the bridge - Required |
| | `protocol` - Protocol used for the communication (Binary, Ascii) - Required - Default = Binary |
| | `baud` - Baud rate of the bridge - Required - Default = 9600 |
| partition | `partitionNumber` - Partition number (1-8) - Required |
| zone | `zoneNumber` - Zone number (1-192) - Required |
| keypad | `keypadAddress` - Keypad address (192-255) - Required |
A full example is further below.
## Channels
Caddx Alarm things support a variety of channels as seen below in the following table:
| Channel | Item Type | Type | Description |
|--------------------------------------------------|-----------|---------------------|--------------------------------------------|
| send_Command | String | Command | Send a command to the panel |
| panel_firmware_version | String | Configuration | Firmware version |
| panel_log_message_n_0 | String | Runtime | Log message 10 |
| panel_log_message_n_1 | String | Runtime | Log message 9 |
| panel_log_message_n_2 | String | Runtime | Log message 8 |
| panel_log_message_n_3 | String | Runtime | Log message 7 |
| panel_log_message_n_4 | String | Runtime | Log message 6 |
| panel_log_message_n_5 | String | Runtime | Log message 5 |
| panel_log_message_n_6 | String | Runtime | Log message 4 |
| panel_log_message_n_7 | String | Runtime | Log message 3 |
| panel_log_message_n_8 | String | Runtime | Log message 2 |
| panel_log_message_n_9 | String | Runtime | Log message 1 |
| panel_interface_configuration_message | Switch | Configuration | Interface Configuration Message |
| panel_zone_status_message | Switch | Configuration | Zone Status Message |
| panel_zones_snapshot_message | Switch | Configuration | Zones Snapshot Message |
| panel_partition_status_message | Switch | Configuration | Partition Status Message |
| panel_partitions_snapshot_message | Switch | Configuration | Partitions Snapshot Message |
| panel_system_status_message | Switch | Configuration | System Status Message |
| panel_x10_message_received | Switch | Configuration | X-10 Message Received |
| panel_log_event_message | Switch | Configuration | Log Event Message |
| panel_keypad_message_received | Switch | Configuration | Keypad Message Received |
| panel_interface_configuration_request | Switch | Configuration | Interface Configuration Request |
| panel_zone_name_request | Switch | Configuration | Zone Name Request |
| panel_zone_status_request | Switch | Configuration | Zone Status Request |
| panel_zones_snapshot_request | Switch | Configuration | Zones Snapshot Request |
| panel_partition_status_request | Switch | Configuration | Partition Status Request |
| panel_partitions_snapshot_request | Switch | Configuration | Partitions Snapshot Request |
| panel_system_status_request | Switch | Configuration | System Status Request |
| panel_send_x10_message | Switch | Configuration | Send X-10 Message |
| panel_log_event_request | Switch | Configuration | Log Event Request |
| panel_send_keypad_text_message | Switch | Configuration | Send Keypad Text Message |
| panel_keypad_terminal_mode_request | Switch | Configuration | Keypad Terminal Mode Request |
| panel_program_data_request | Switch | Configuration | Program Data Request |
| panel_program_data_command | Switch | Configuration | Program Data Command |
| panel_user_information_request_with_pin | Switch | Configuration | User Information Request with PIN |
| panel_user_information_request_without_pin | Switch | Configuration | User Information Request without PIN |
| panel_set_user_code_command_with_pin | Switch | Configuration | Set User Code Command with PIN |
| panel_set_user_code_command_without_pin | Switch | Configuration | Set User Code Command without PIN |
| panel_set_user_authorization_command_with_pin | Switch | Configuration | Set User Authorization Command with PIN |
| panel_set_user_authorization_command_without_pin | Switch | Configuration | Set User Authorization Command without PIN |
| panel_store_communication_event_command | Switch | Configuration | Store Communication Event Command |
| panel_set_clock_calendar_command | Switch | Configuration | Set Clock / Calendar Command |
| panel_primary_keypad_function_with_pin | Switch | Configuration | Primary Keypad Function with PIN |
| panel_primary_keypad_function_without_pin | Switch | Configuration | Primary Keypad Function without PIN |
| panel_secondary_keypad_function | Switch | Configuration | Secondary Keypad Function |
| panel_zone_bypass_toggle | Switch | Configuration | Zone Bypass Toggle |
| partition_bypass_code_required | Switch | Partition Condition | Bypass code required |
| partition_fire_trouble | Switch | Partition Condition | Fire trouble |
| partition_fire | Switch | Partition Condition | Fire |
| partition_pulsing_buzzer | Switch | Partition Condition | Pulsing Buzzer |
| partition_tlm_fault_memory | Switch | Partition Condition | TLM fault memory |
| partition_armed | Switch | Partition Condition | Armed |
| partition_instant | Switch | Partition Condition | Instant |
| partition_previous_alarm | Switch | Partition Condition | Previous Alarm |
| partition_siren_on | Switch | Partition Condition | Siren on |
| partition_steady_siren_on | Switch | Partition Condition | Steady siren on |
| partition_alarm_memory | Switch | Partition Condition | Alarm memory |
| partition_tamper | Switch | Partition Condition | Tamper |
| partition_cancel_command_entered | Switch | Partition Condition | Cancel command entered |
| partition_code_entered | Switch | Partition Condition | Code entered |
| partition_cancel_pending | Switch | Partition Condition | Cancel pending |
| partition_silent_exit_enabled | Switch | Partition Condition | Silent exit enabled |
| partition_entryguard | Switch | Partition Condition | Entryguard (stay mode) |
| partition_chime_mode_on | Switch | Partition Condition | Chime mode on |
| partition_entry | Switch | Partition Condition | Entry |
| partition_delay_expiration_warning | Switch | Partition Condition | Delay expiration warning |
| partition_exit1 | Switch | Partition Condition | Exit1 |
| partition_exit2 | Switch | Partition Condition | Exit2 |
| partition_led_extinguish | Switch | Partition Condition | LED extinguish |
| partition_cross_timing | Switch | Partition Condition | Cross timing |
| partition_recent_closing_being_timed | Switch | Partition Condition | Recent closing being timed |
| partition_exit_error_triggered | Switch | Partition Condition | Exit error triggered |
| partition_auto_home_inhibited | Switch | Partition Condition | Auto home inhibited |
| partition_sensor_low_battery | Switch | Partition Condition | Sensor low battery |
| partition_sensor_lost_supervision | Switch | Partition Condition | Sensor lost supervision |
| partition_zone_bypassed | Switch | Partition Condition | Zone bypassed |
| partition_force_arm_triggered_by_auto_arm | Switch | Partition Condition | Force arm triggered by auto arm |
| partition_ready_to_arm | Switch | Partition Condition | Ready to arm |
| partition_ready_to_force_arm | Switch | Partition Condition | Ready to force arm |
| partition_valid_pin_accepted | Switch | Partition Condition | Valid PIN accepted |
| partition_chime_on | Switch | Partition Condition | Chime on (sounding) |
| partition_error_beep | Switch | Partition Condition | Error beep (triple beep) |
| partition_tone_on | Switch | Partition Condition | Tone on (activation tone) |
| partition_entry1 | Switch | Partition Condition | Entry 1 |
| partition_open_period | Switch | Partition Condition | Open period |
| partition_alarm_sent_using_phone_number_1 | Switch | Partition Condition | Alarm sent using phone number 1 |
| partition_alarm_sent_using_phone_number_2 | Switch | Partition Condition | Alarm sent using phone number 2 |
| partition_alarm_sent_using_phone_number_3 | Switch | Partition Condition | Alarm sent using phone number 3 |
| partition_cancel_report_is_in_the_stack | Switch | Partition Condition | Cancel report is in the stack |
| partition_keyswitch_armed | Switch | Partition Condition | Keyswitch armed |
| partition_delay_trip_in_progress | Switch | Partition Condition | Delay Trip in progress (common zone) |
| partition_secondary_command | Number | Command | Partition Secondary Command |
| zone_partition2 | Switch | Configuration | Partition 2 |
| zone_partition3 | Switch | Configuration | Partition 3 |
| zone_partition1 | Switch | Configuration | Partition 1 |
| zone_partition4 | Switch | Configuration | Partition 4 |
| zone_partition5 | Switch | Configuration | Partition 5 |
| zone_partition6 | Switch | Configuration | Partition 6 |
| zone_partition7 | Switch | Configuration | Partition 7 |
| zone_partition8 | Switch | Configuration | Partition 8 |
| zone_name | String | Configuration | Name |
| zone_fire | Switch | Configuration | Fire |
| zone_24hour | Switch | Configuration | 24 Hour |
| zone_key_switch | Switch | Configuration | Key-switch |
| zone_follower | Switch | Configuration | Follower |
| zone_entry_exit_delay_1 | Switch | Configuration | Entry / exit delay 1 |
| zone_entry_exit_delay_2 | Switch | Configuration | Entry / exit delay 2 |
| zone_interior | Switch | Configuration | Interior |
| zone_local_only | Switch | Configuration | Local only |
| zone_keypad_sounder | Switch | Configuration | Keypad Sounder |
| zone_yelping_siren | Switch | Configuration | Yelping siren |
| zone_steady_siren | Switch | Configuration | Steady siren |
| zone_chime | Switch | Configuration | Chime |
| zone_bypassable | Switch | Configuration | Bypassable |
| zone_group_bypassable | Switch | Configuration | Group bypassable |
| zone_force_armable | Switch | Configuration | Force armable |
| zone_entry_guard | Switch | Configuration | Entry guard |
| zone_fast_loop_response | Switch | Configuration | Fast loop response |
| zone_double_eol_tamper | Switch | Configuration | Double EOL tamper |
| zone_type_trouble | Switch | Configuration | Trouble |
| zone_cross_zone | Switch | Configuration | Cross zone |
| zone_dialer_delay | Switch | Configuration | Dialer delay |
| zone_swinger_shutdown | Switch | Configuration | Swinger shutdown |
| zone_restorable | Switch | Configuration | Restorable |
| zone_listen_in | Switch | Configuration | Listen in |
| zone_faulted | Contact | Zone Condition | Faulted (or delayed trip) |
| zone_tampered | Switch | Zone Condition | Tampered |
| zone_trouble | Switch | Zone Condition | Trouble |
| zone_bypassed | Switch | Zone Condition | Bypassed |
| zone_inhibited | Switch | Zone Condition | Inhibited (force armed) |
| zone_low_battery | Switch | Zone Condition | Low battery |
| zone_loss_of_supervision | Switch | Zone Condition | Loss of supervision |
| zone_alarm_memory | Switch | Zone Condition | Alarm memory |
| zone_bypass_memory | Switch | Zone Condition | Bypass memory |
## Full Example
The following is an example of a things file (caddx.things):
```
Bridge caddx:bridge:thebridge "Bridge" [ protocol="Binary", serialPort="/dev/ttyUSB0", baudrate=38400 ] {
Thing partition partition1 "Groundfloor alarm" [ partitionNumber=1 ]
Thing zone zone1 "Livingroom motion sensor" [ zoneNumber=1 ]
Thing zone zone2 "Bedroom motion sensor" [ zoneNumber=2 ]
Thing zone zone3 "Guestroom motion sensor" [ zoneNumber=3 ]
Thing zone zone4 "Livingroom window" [ zoneNumber=4 ]
Thing zone zone5 "Bedroom window" [ zoneNumber=5 ]
Thing zone zone6 "Guestroom window" [ zoneNumber=6 ]
}
```
The following is an example of an items file (caddx.items):
```
Group:Contact:OR(OPEN,CLOSED) MotionSensors "Motion Sensors [%s]" <motion>
Group:Contact:OR(OPEN,CLOSED) Windows "Windows open [%s]" <window>
Contact Bedroom_Motion "Bedroom [%s]" <motion> (MotionSensors) { channel="caddx:zone:thebridge:zone1:zone_faulted" }
Contact Livingroom_Motion "Livingroom [%s]" <motion> (MotionSensors) { channel="caddx:zone:thebridge:zone2:zone_faulted" }
Contact Guestroom_Motion "Guestroom [%s]" <motion> (MotionSensors) { channel="caddx:zone:thebridge:zone3:zone_faulted" }
Contact Bedroom_Window "Bedroom Window [%s]" <window> (Windows) { channel="caddx:zone:thebridge:zone4:zone_faulted" }
Contact Livingroom_Window "Livinroom Window [%s]" <window> (Windows) { channel="caddx:zone:thebridge:zone5:zone_faulted" }
Contact Guestroom_Window "Guestroom Window [%s]" <window> (Windows) { channel="caddx:zone:thebridge:zone6:zone_faulted" }
Switch Partition1_Armed "Armed [%s]" <groundfloor> { channel="caddx:partition:thebridge:partition1:partition_armed" }
Switch Partition1_EntryGuard "Entry Guard [%s]" <groundfloor> { channel="caddx:partition:thebridge:partition1:partition_entryguard" }
```
The following is an example of a sitemap file (home.sitemap):
```
sitemap home label="Home" {
Frame label="Ground floor" {
Text item=Partition1_Armed
Text item=Partition1_EntryGuard
Text item=MotionSensors
Text label="Motion Sensors (detailed)" {
Text item=Bedroom_Motion
Text item=Livingroom_Motion
Text item=Guestroom_Motion
}
Text item=Windows
Text label="Windows (detailed)" {
Text item=Bedroom_Window
Text item=Livingroom_Window
Text item=Guestroom_Window
}
}
}
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.caddx</artifactId>
<name>openHAB Add-ons :: Bundles :: Caddx Binding</name>
</project>

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>