diff --git a/CODEOWNERS b/CODEOWNERS index 9e60ad6b66254..e4811da7232ee 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -31,6 +31,7 @@ /bundles/org.openhab.binding.bsblan/ @hypetsch /bundles/org.openhab.binding.bticinosmarther/ @MrRonfo /bundles/org.openhab.binding.buienradar/ @gedejong +/bundles/org.openhab.binding.caddx/ @jossuar /bundles/org.openhab.binding.chromecast/ @kaikreuzer /bundles/org.openhab.binding.cm11a/ @BobRak /bundles/org.openhab.binding.coolmasternet/ @projectgus diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 37c5b8dbaea60..5384fe28b7988 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -144,6 +144,11 @@ org.openhab.binding.buienradar ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.caddx + ${project.version} + org.openhab.addons.bundles org.openhab.binding.chromecast diff --git a/bundles/org.openhab.binding.caddx/.classpath b/bundles/org.openhab.binding.caddx/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.caddx/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.caddx/.project b/bundles/org.openhab.binding.caddx/.project new file mode 100644 index 0000000000000..368a86586e338 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.caddx + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.caddx/NOTICE b/bundles/org.openhab.binding.caddx/NOTICE new file mode 100644 index 0000000000000..4c20ef446c1e4 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/NOTICE @@ -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 diff --git a/bundles/org.openhab.binding.caddx/README.md b/bundles/org.openhab.binding.caddx/README.md new file mode 100644 index 0000000000000..bef6eb45a9bdb --- /dev/null +++ b/bundles/org.openhab.binding.caddx/README.md @@ -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
0 = 2400 Baud
1 = 4800 Baud
2 = 9600 Baud
3 = 19200 Baud
4 = 38400 Baud | +| 209 | 1 | Off/On | Home automation protocol
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
1 = Off: Binary. On: ASCII. | +| 1 | 1 | 0 - 4 | Baud rate table
0 = 600 Baud
1 = 1200 Baud
2 = 2400 Baud
3 = 4800 Baud
4 = 9600 Baud
5 = 19200 Baud
6 = 38400 Baud
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]" +Group:Contact:OR(OPEN,CLOSED) Windows "Windows open [%s]" + +Contact Bedroom_Motion "Bedroom [%s]" (MotionSensors) { channel="caddx:zone:thebridge:zone1:zone_faulted" } +Contact Livingroom_Motion "Livingroom [%s]" (MotionSensors) { channel="caddx:zone:thebridge:zone2:zone_faulted" } +Contact Guestroom_Motion "Guestroom [%s]" (MotionSensors) { channel="caddx:zone:thebridge:zone3:zone_faulted" } +Contact Bedroom_Window "Bedroom Window [%s]" (Windows) { channel="caddx:zone:thebridge:zone4:zone_faulted" } +Contact Livingroom_Window "Livinroom Window [%s]" (Windows) { channel="caddx:zone:thebridge:zone5:zone_faulted" } +Contact Guestroom_Window "Guestroom Window [%s]" (Windows) { channel="caddx:zone:thebridge:zone6:zone_faulted" } + +Switch Partition1_Armed "Armed [%s]" { channel="caddx:partition:thebridge:partition1:partition_armed" } +Switch Partition1_EntryGuard "Entry Guard [%s]" { 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 + } + } +} +``` diff --git a/bundles/org.openhab.binding.caddx/pom.xml b/bundles/org.openhab.binding.caddx/pom.xml new file mode 100644 index 0000000000000..7248b132fd4ec --- /dev/null +++ b/bundles/org.openhab.binding.caddx/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.7-SNAPSHOT + + + org.openhab.binding.caddx + + openHAB Add-ons :: Bundles :: Caddx Binding + + diff --git a/bundles/org.openhab.binding.caddx/src/main/feature/feature.xml b/bundles/org.openhab.binding.caddx/src/main/feature/feature.xml new file mode 100644 index 0000000000000..cacd59d6466de --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/feature/feature.xml @@ -0,0 +1,10 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + openhab-transport-serial + mvn:org.openhab.addons.bundles/org.openhab.binding.caddx/${project.version} + + diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxBindingConstants.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxBindingConstants.java new file mode 100644 index 0000000000000..e75e2145a2dab --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxBindingConstants.java @@ -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.eclipse.smarthome.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 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 SUPPORTED_BRIDGE_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(CADDXBRIDGE_THING_TYPE).collect(Collectors.toSet())); +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxCommunicator.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxCommunicator.java new file mode 100644 index 0000000000000..1bec266567a61 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxCommunicator.java @@ -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.eclipse.smarthome.core.util.HexUtils; +import org.eclipse.smarthome.io.transport.serial.PortInUseException; +import org.eclipse.smarthome.io.transport.serial.SerialPort; +import org.eclipse.smarthome.io.transport.serial.SerialPortEvent; +import org.eclipse.smarthome.io.transport.serial.SerialPortEventListener; +import org.eclipse.smarthome.io.transport.serial.SerialPortIdentifier; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; +import org.eclipse.smarthome.io.transport.serial.UnsupportedCommOperationException; +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 listenerQueue = new HashSet<>(); + private final Deque messages = new LinkedBlockingDeque<>(); + private final SynchronousQueue 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; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxDirection.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxDirection.java new file mode 100644 index 0000000000000..d25e77ca91c45 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxDirection.java @@ -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 +}; diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxEvent.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxEvent.java new file mode 100644 index 0000000000000..ca5edefa0caed --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxEvent.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxMessage.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxMessage.java new file mode 100644 index 0000000000000..698f0d8ace8c2 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxMessage.java @@ -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.eclipse.smarthome.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 propertyMap = new HashMap<>(); + private final Map 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 }; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxMessageType.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxMessageType.java new file mode 100644 index 0000000000000..c45072a95a16a --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxMessageType.java @@ -0,0 +1,1349 @@ +/** + * 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.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * All the panel message types + * + * @author Georgios Moutsos - Initial contribution + */ +@NonNullByDefault +public enum CaddxMessageType { + + INTERFACE_CONFIGURATION_MESSAGE(0x01, null, 12, "Interface Configuration Message", + "This message will contain the firmware version number and other information about features currently enabled. It will be sent each time the unit is reset or programmed.", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + new CaddxProperty("panel_firmware_version", 2, 4, 0, 0, CaddxPropertyType.STRING, "Firmware version", + false), + + // Byte 6 Supported transition message flags (1) + new CaddxProperty("panel_interface_configuration_message", 6, 1, 1, 1, CaddxPropertyType.BIT, + "Interface Configuration Message", false), + new CaddxProperty("panel_zone_status_message", 6, 1, 4, 1, CaddxPropertyType.BIT, "Zone Status Message", + false), + new CaddxProperty("panel_zones_snapshot_message", 6, 1, 5, 1, CaddxPropertyType.BIT, + "Zones Snapshot Message", false), + new CaddxProperty("panel_partition_status_message", 6, 1, 6, 1, CaddxPropertyType.BIT, + "Partition Status Message", false), + new CaddxProperty("panel_partitions_snapshot_message", 6, 1, 7, 1, CaddxPropertyType.BIT, + "Partitions Snapshot Message", false), + + // Byte 7 Supported transition message flags (2) + new CaddxProperty("panel_system_status_message", 7, 1, 0, 1, CaddxPropertyType.BIT, "System Status Message", + false), + new CaddxProperty("panel_x10_message_received", 7, 1, 1, 1, CaddxPropertyType.BIT, "X-10 Message Received", + false), + new CaddxProperty("panel_log_event_message", 7, 1, 2, 1, CaddxPropertyType.BIT, "Log Event Message", false), + new CaddxProperty("panel_keypad_message_received", 7, 1, 3, 1, CaddxPropertyType.BIT, + "Keypad Message Received", false), + + // Byte 8 Supported request / command flags (1) + new CaddxProperty("panel_interface_configuration_request", 8, 1, 1, 1, CaddxPropertyType.BIT, + "Interface Configuration Request", false), + new CaddxProperty("panel_zone_name_request", 8, 1, 3, 1, CaddxPropertyType.BIT, "Zone Name Request", false), + new CaddxProperty("panel_zone_status_request", 8, 1, 4, 1, CaddxPropertyType.BIT, "Zone Status Request", + false), + new CaddxProperty("panel_zones_snapshot_request", 8, 1, 5, 1, CaddxPropertyType.BIT, + "Zones Snapshot Request", false), + new CaddxProperty("panel_partition_status_request", 8, 1, 6, 1, CaddxPropertyType.BIT, + "Partition Status Request", false), + new CaddxProperty("panel_partitions_snapshot_request", 8, 1, 7, 1, CaddxPropertyType.BIT, + "Partitions Snapshot Request", false), + + // Byte 9 Supported request / command flags (2) + new CaddxProperty("panel_system_status_request", 9, 1, 0, 1, CaddxPropertyType.BIT, "System Status Request", + false), + new CaddxProperty("panel_send_x10_message", 9, 1, 1, 1, CaddxPropertyType.BIT, "Send X-10 Message", false), + new CaddxProperty("panel_log_event_request", 9, 1, 2, 1, CaddxPropertyType.BIT, "Log Event Request", false), + new CaddxProperty("panel_send_keypad_text_message", 9, 1, 3, 1, CaddxPropertyType.BIT, + "Send Keypad Text Message", false), + new CaddxProperty("panel_keypad_terminal_mode_request", 9, 1, 4, 1, CaddxPropertyType.BIT, + "Keypad Terminal Mode Request", false), + + // Byte 10 Supported request / command flags (3) + new CaddxProperty("panel_program_data_request", 10, 1, 0, 1, CaddxPropertyType.BIT, "Program Data Request", + false), + new CaddxProperty("panel_program_data_command", 10, 1, 1, 1, CaddxPropertyType.BIT, "Program Data Command", + false), + new CaddxProperty("panel_user_information_request_with_pin", 10, 1, 2, 1, CaddxPropertyType.BIT, + "User Information Request with PIN", false), + new CaddxProperty("panel_user_information_request_without_pin", 10, 1, 3, 1, CaddxPropertyType.BIT, + "User Information Request without PIN", false), + new CaddxProperty("panel_set_user_code_command_with_pin", 10, 1, 4, 1, CaddxPropertyType.BIT, + "Set User Code Command with PIN", false), + new CaddxProperty("panel_set_user_code_command_without_pin", 10, 1, 5, 1, CaddxPropertyType.BIT, + "Set User Code Command without PIN", false), + new CaddxProperty("panel_set_user_authorization_command_with_pin", 10, 1, 6, 1, CaddxPropertyType.BIT, + "Set User Authorization Command with PIN", false), + new CaddxProperty("panel_set_user_authorization_command_without_pin", 10, 1, 7, 1, CaddxPropertyType.BIT, + "Set User Authorization Command without PIN", false), + + // Byte 11 Supported request / command flags (4) + new CaddxProperty("panel_store_communication_event_command", 11, 1, 2, 1, CaddxPropertyType.BIT, + "Store Communication Event Command", false), + new CaddxProperty("panel_set_clock_calendar_command", 11, 1, 3, 1, CaddxPropertyType.BIT, + "Set Clock / Calendar Command", false), + new CaddxProperty("panel_primary_keypad_function_with_pin", 11, 1, 4, 1, CaddxPropertyType.BIT, + "Primary Keypad Function with PIN", false), + new CaddxProperty("panel_primary_keypad_function_without_pin", 11, 1, 5, 1, CaddxPropertyType.BIT, + "Primary Keypad Function without PIN", false), + new CaddxProperty("panel_secondary_keypad_function", 11, 1, 6, 1, CaddxPropertyType.BIT, + "Secondary Keypad Function", false), + new CaddxProperty("panel_zone_bypass_toggle", 11, 1, 7, 1, CaddxPropertyType.BIT, "Zone Bypass Toggle", + false)), + + ZONE_NAME_MESSAGE(0x03, null, 18, "Zone Name Message", + "This message will contain the 16-character name for the zone number that was requested (via Zone Name Request (23h)).", + CaddxDirection.IN, CaddxSource.ZONE, + + // Properties + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + new CaddxProperty("zone_number", 2, 1, 0, 0, CaddxPropertyType.INT, "Zone number", false), + new CaddxProperty("zone_name", 3, 16, 0, 0, CaddxPropertyType.STRING, "Zone name", false)), + + ZONE_STATUS_MESSAGE(0x04, null, 8, "Zone Status Message", + "This message will contain all information relevant to a zone in the system.", CaddxDirection.IN, + CaddxSource.ZONE, + + // Properties + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + new CaddxProperty("zone_number", 2, 1, 0, 0, CaddxPropertyType.INT, "Zone number", false), + + // Byte 3 Partition mask + new CaddxProperty("zone_partition1", 3, 1, 0, 1, CaddxPropertyType.BIT, "Partition 1 enable", false), + new CaddxProperty("zone_partition2", 3, 1, 1, 1, CaddxPropertyType.BIT, "Partition 2 enable", false), + new CaddxProperty("zone_partition3", 3, 1, 2, 1, CaddxPropertyType.BIT, "Partition 3 enable", false), + new CaddxProperty("zone_partition4", 3, 1, 3, 1, CaddxPropertyType.BIT, "Partition 4 enable", false), + new CaddxProperty("zone_partition5", 3, 1, 4, 1, CaddxPropertyType.BIT, "Partition 5 enable", false), + new CaddxProperty("zone_partition6", 3, 1, 5, 1, CaddxPropertyType.BIT, "Partition 6 enable", false), + new CaddxProperty("zone_partition7", 3, 1, 6, 1, CaddxPropertyType.BIT, "Partition 7 enable", false), + new CaddxProperty("zone_partition8", 3, 1, 7, 1, CaddxPropertyType.BIT, "Partition 8 enable", false), + + // Byte 4 Zone type flags (1) + new CaddxProperty("zone_fire", 4, 1, 0, 1, CaddxPropertyType.BIT, "Fire", false), + new CaddxProperty("zone_24hour", 4, 1, 1, 1, CaddxPropertyType.BIT, "24 Hour", false), + new CaddxProperty("zone_key_switch", 4, 1, 2, 1, CaddxPropertyType.BIT, "Key-switch", false), + new CaddxProperty("zone_follower", 4, 1, 3, 1, CaddxPropertyType.BIT, "Follower", false), + new CaddxProperty("zone_entry_exit_delay_1", 4, 1, 4, 1, CaddxPropertyType.BIT, "Entry / exit delay 1", + false), + new CaddxProperty("zone_entry_exit_delay_2", 4, 1, 5, 1, CaddxPropertyType.BIT, "Entry / exit delay 2", + false), + new CaddxProperty("zone_interior", 4, 1, 6, 1, CaddxPropertyType.BIT, "Interior", false), + new CaddxProperty("zone_local_only", 4, 1, 7, 1, CaddxPropertyType.BIT, "Local only", false), + + // Byte 5 Zone type flags (2) + new CaddxProperty("zone_keypad_sounder", 5, 1, 0, 1, CaddxPropertyType.BIT, "Keypad sounder", false), + new CaddxProperty("zone_yelping_siren", 5, 1, 1, 1, CaddxPropertyType.BIT, "Yelping siren", false), + new CaddxProperty("zone_steady_siren", 5, 1, 2, 1, CaddxPropertyType.BIT, "Steady siren", false), + new CaddxProperty("zone_chime", 5, 1, 3, 1, CaddxPropertyType.BIT, "Chime", false), + new CaddxProperty("zone_bypassable", 5, 1, 4, 1, CaddxPropertyType.BIT, "Bypassable", false), + new CaddxProperty("zone_group_bypassable", 5, 1, 5, 1, CaddxPropertyType.BIT, "Group bypassable", false), + new CaddxProperty("zone_force_armable", 5, 1, 6, 1, CaddxPropertyType.BIT, "Force armable", false), + new CaddxProperty("zone_entry_guard", 5, 1, 7, 1, CaddxPropertyType.BIT, "Entry guard", false), + + // Byte 6 Zone type flags (3) + new CaddxProperty("zone_fast_loop_response", 6, 1, 0, 1, CaddxPropertyType.BIT, "Fast loop response", + false), + new CaddxProperty("zone_double_eol_tamper", 6, 1, 1, 1, CaddxPropertyType.BIT, "Double EOL tamper", false), + new CaddxProperty("zone_type_trouble", 6, 1, 2, 1, CaddxPropertyType.BIT, "Trouble", false), + new CaddxProperty("zone_cross_zone", 6, 1, 3, 1, CaddxPropertyType.BIT, "Cross zone", false), + new CaddxProperty("zone_dialer_delay", 6, 1, 4, 1, CaddxPropertyType.BIT, "Dialer delay", false), + new CaddxProperty("zone_swinger_shutdown", 6, 1, 5, 1, CaddxPropertyType.BIT, "Swinger shutdown", false), + new CaddxProperty("zone_restorable", 6, 1, 6, 1, CaddxPropertyType.BIT, "Restorable", false), + new CaddxProperty("zone_listen_in", 6, 1, 7, 1, CaddxPropertyType.BIT, "Listen in", false), + + // Byte 7 Zone condition flags (1) + new CaddxProperty("zone_faulted", 7, 1, 0, 1, CaddxPropertyType.BIT, "Faulted (or delayed trip)", false), + new CaddxProperty("zone_tampered", 7, 1, 1, 1, CaddxPropertyType.BIT, "Tampered", false), + new CaddxProperty("zone_trouble", 7, 1, 2, 1, CaddxPropertyType.BIT, "Trouble", false), + new CaddxProperty("zone_bypassed", 7, 1, 3, 1, CaddxPropertyType.BIT, "Bypassed", false), + new CaddxProperty("zone_inhibited", 7, 1, 4, 1, CaddxPropertyType.BIT, "Inhibited (force armed)", false), + new CaddxProperty("zone_low_battery", 7, 1, 5, 1, CaddxPropertyType.BIT, "Low battery", false), + new CaddxProperty("zone_loss_of_supervision", 7, 1, 6, 1, CaddxPropertyType.BIT, "Loss of supervision", + false), + + // Byte 8 Zone condition flags (2) + new CaddxProperty("zone_alarm_memory", 8, 1, 0, 1, CaddxPropertyType.BIT, "Alarm memory", false), + new CaddxProperty("zone_bypass_memory", 8, 1, 1, 1, CaddxPropertyType.BIT, "Bypass memory", false)), + + ZONES_SNAPSHOT_MESSAGE(0x05, null, 10, "Zones Snapshot Message", + "This message will contain an abbreviated set of information for any group of 16 zones possible on the system. (A zone offset number will set the range of zones)", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + new CaddxProperty("zone_offset", 2, 1, 0, 0, CaddxPropertyType.INT, "Zone offset (0= start at zone 1)", + false), + + // Byte 3 Zone 1 & 2 (+offset) status flags + new CaddxProperty("", 3, 1, 0, 1, CaddxPropertyType.BIT, "Zone 1 faulted (or delayed trip)", false), + new CaddxProperty("", 3, 1, 1, 1, CaddxPropertyType.BIT, "Zone 1 bypass (or inhibited)", false), + new CaddxProperty("zone_1_trouble", 3, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 1 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 3, 1, 3, 1, CaddxPropertyType.BIT, "Zone 1 alarm memory", false), + new CaddxProperty("", 3, 1, 4, 1, CaddxPropertyType.BIT, "Zone 2 faulted (or delayed trip)", false), + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, "Zone 2 bypass (or inhibited)", false), + new CaddxProperty("zone_2_trouble", 3, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 2 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Zone 2 alarm memory", false), + + // Byte 4 Zone 3 & 4 status flags (see byte 3) + new CaddxProperty("", 4, 1, 0, 1, CaddxPropertyType.BIT, "Zone 3 faulted (or delayed trip)", false), + new CaddxProperty("", 4, 1, 1, 1, CaddxPropertyType.BIT, "Zone 3 bypass (or inhibited)", false), + new CaddxProperty("zone_3_trouble", 4, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 3 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 4, 1, 3, 1, CaddxPropertyType.BIT, "Zone 3 alarm memory", false), + new CaddxProperty("", 4, 1, 4, 1, CaddxPropertyType.BIT, "Zone 4 faulted (or delayed trip)", false), + new CaddxProperty("", 4, 1, 5, 1, CaddxPropertyType.BIT, "Zone 4 bypass (or inhibited)", false), + new CaddxProperty("zone_4_trouble", 4, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 4 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 4, 1, 7, 1, CaddxPropertyType.BIT, "Zone 4 alarm memory", false), + + // Byte 5 Zone 5 & 6 status flags (see byte 3) + new CaddxProperty("", 5, 1, 0, 1, CaddxPropertyType.BIT, "Zone 5 faulted (or delayed trip)", false), + new CaddxProperty("", 5, 1, 1, 1, CaddxPropertyType.BIT, "Zone 5 bypass (or inhibited)", false), + new CaddxProperty("zone_5_trouble", 5, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 5 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 5, 1, 3, 1, CaddxPropertyType.BIT, "Zone 5 alarm memory", false), + new CaddxProperty("", 5, 1, 4, 1, CaddxPropertyType.BIT, "Zone 6 faulted (or delayed trip)", false), + new CaddxProperty("", 5, 1, 5, 1, CaddxPropertyType.BIT, "Zone 6 bypass (or inhibited)", false), + new CaddxProperty("zone_6_trouble", 5, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 6 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 5, 1, 7, 1, CaddxPropertyType.BIT, "Zone 6 alarm memory", false), + + // Byte 6 Zone 7 & 8 status flags (see byte 3) + new CaddxProperty("", 6, 1, 0, 1, CaddxPropertyType.BIT, "Zone 7 faulted (or delayed trip)", false), + new CaddxProperty("", 6, 1, 1, 1, CaddxPropertyType.BIT, "Zone 7 bypass (or inhibited)", false), + new CaddxProperty("zone_7_trouble", 6, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 7 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 6, 1, 3, 1, CaddxPropertyType.BIT, "Zone 7 alarm memory", false), + new CaddxProperty("", 6, 1, 4, 1, CaddxPropertyType.BIT, "Zone 8 faulted (or delayed trip)", false), + new CaddxProperty("", 6, 1, 5, 1, CaddxPropertyType.BIT, "Zone 8 bypass (or inhibited)", false), + new CaddxProperty("zone_8_trouble", 6, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 8 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 6, 1, 7, 1, CaddxPropertyType.BIT, "Zone 8 alarm memory", false), + + // Byte 7 Zone 9 & 10 status flags (see byte 3) + new CaddxProperty("", 7, 1, 0, 1, CaddxPropertyType.BIT, "Zone 9 faulted (or delayed trip)", false), + new CaddxProperty("", 7, 1, 1, 1, CaddxPropertyType.BIT, "Zone 9 bypass (or inhibited)", false), + new CaddxProperty("zone_9_trouble", 7, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 9 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 7, 1, 3, 1, CaddxPropertyType.BIT, "Zone 9 alarm memory", false), + new CaddxProperty("", 7, 1, 4, 1, CaddxPropertyType.BIT, "Zone 10 faulted (or delayed trip)", false), + new CaddxProperty("", 7, 1, 5, 1, CaddxPropertyType.BIT, "Zone 10 bypass (or inhibited)", false), + new CaddxProperty("zone_10_trouble", 7, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 10 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 7, 1, 7, 1, CaddxPropertyType.BIT, "Zone 10 alarm memory", false), + + // Byte 8 Zone 11 & 12 status flags (see byte 3) + new CaddxProperty("", 8, 1, 0, 1, CaddxPropertyType.BIT, "Zone 11 faulted (or delayed trip)", false), + new CaddxProperty("", 8, 1, 1, 1, CaddxPropertyType.BIT, "Zone 11 bypass (or inhibited)", false), + new CaddxProperty("zone_11_trouble", 8, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 11 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 8, 1, 3, 1, CaddxPropertyType.BIT, "Zone 11 alarm memory", false), + new CaddxProperty("", 8, 1, 4, 1, CaddxPropertyType.BIT, "Zone 12 faulted (or delayed trip)", false), + new CaddxProperty("", 8, 1, 5, 1, CaddxPropertyType.BIT, "Zone 12 bypass (or inhibited)", false), + new CaddxProperty("zone_12_trouble", 8, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 12 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 8, 1, 7, 1, CaddxPropertyType.BIT, "Zone 12 alarm memory", false), + + // Byte 9 Zone 13 & 14 status flags (see byte 3) + new CaddxProperty("", 9, 1, 0, 1, CaddxPropertyType.BIT, "Zone 13 faulted (or delayed trip)", false), + new CaddxProperty("", 9, 1, 1, 1, CaddxPropertyType.BIT, "Zone 13 bypass (or inhibited)", false), + new CaddxProperty("zone_13_trouble", 9, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 13 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 9, 1, 3, 1, CaddxPropertyType.BIT, "Zone 13 alarm memory", false), + new CaddxProperty("", 9, 1, 4, 1, CaddxPropertyType.BIT, "Zone 14 faulted (or delayed trip)", false), + new CaddxProperty("", 9, 1, 5, 1, CaddxPropertyType.BIT, "Zone 14 bypass (or inhibited)", false), + new CaddxProperty("zone_14_trouble", 9, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 14 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 9, 1, 7, 1, CaddxPropertyType.BIT, "Zone 14 alarm memory", false), + + // Byte 10 Zone 15 & 16 status flags (see byte 3) + new CaddxProperty("", 10, 1, 0, 1, CaddxPropertyType.BIT, "Zone 15 faulted (or delayed trip)", false), + new CaddxProperty("", 10, 1, 1, 1, CaddxPropertyType.BIT, "Zone 15 bypass (or inhibited)", false), + new CaddxProperty("zone_15_trouble", 10, 1, 2, 1, CaddxPropertyType.BIT, + "Zone 15 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 10, 1, 3, 1, CaddxPropertyType.BIT, "Zone 15 alarm memory", false), + new CaddxProperty("", 10, 1, 4, 1, CaddxPropertyType.BIT, "Zone 16 faulted (or delayed trip)", false), + new CaddxProperty("", 10, 1, 5, 1, CaddxPropertyType.BIT, "Zone 16 bypass (or inhibited)", false), + new CaddxProperty("zone_16_trouble", 10, 1, 6, 1, CaddxPropertyType.BIT, + "Zone 16 trouble (tamper, low battery, or lost)", false), + new CaddxProperty("", 10, 1, 7, 1, CaddxPropertyType.BIT, "Zone 16 alarm memory", false)), + + PARTITION_STATUS_MESSAGE(0x06, null, 9, "Partition Status Message", + "This message will contain all information relevant to a single partition in the system.", + CaddxDirection.IN, CaddxSource.PARTITION, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + new CaddxProperty("partition_number", 2, 1, 0, 0, CaddxPropertyType.INT, + "Partition number (0= partition 1)", false), + + // Byte 3 Partition condition flags (1) + new CaddxProperty("partition_bypass_code_required", 3, 1, 0, 1, CaddxPropertyType.BIT, + "Bypass code required", false), + new CaddxProperty("partition_fire_trouble", 3, 1, 1, 1, CaddxPropertyType.BIT, "Fire trouble", false), + new CaddxProperty("partition_fire", 3, 1, 2, 1, CaddxPropertyType.BIT, "Fire", false), + new CaddxProperty("partition_pulsing_buzzer", 3, 1, 3, 1, CaddxPropertyType.BIT, "Pulsing Buzzer", false), + new CaddxProperty("partition_tlm_fault_memory", 3, 1, 4, 1, CaddxPropertyType.BIT, "TLM fault memory", + false), + new CaddxProperty("partition_armed", 3, 1, 6, 1, CaddxPropertyType.BIT, "Armed", false), + new CaddxProperty("partition_instant", 3, 1, 7, 1, CaddxPropertyType.BIT, "Instant", false), + + // Byte 4 Partition condition flags (2) + new CaddxProperty("partition_previous_alarm", 4, 1, 0, 1, CaddxPropertyType.BIT, "Previous Alarm", false), + new CaddxProperty("partition_siren_on", 4, 1, 1, 1, CaddxPropertyType.BIT, "Siren on", false), + new CaddxProperty("partition_steady_siren_on", 4, 1, 2, 1, CaddxPropertyType.BIT, "Steady siren on", false), + new CaddxProperty("partition_alarm_memory", 4, 1, 3, 1, CaddxPropertyType.BIT, "Alarm memory", false), + new CaddxProperty("partition_tamper", 4, 1, 4, 1, CaddxPropertyType.BIT, "Tamper", false), + new CaddxProperty("partition_cancel_command_entered", 4, 1, 5, 1, CaddxPropertyType.BIT, + "Cancel command entered", false), + new CaddxProperty("partition_code_entered", 4, 1, 6, 1, CaddxPropertyType.BIT, "Code entered", false), + new CaddxProperty("partition_cancel_pending", 4, 1, 7, 1, CaddxPropertyType.BIT, "Cancel pending", false), + + // Byte 5 Partition condition flags (3) + new CaddxProperty("partition_silent_exit_enabled", 5, 1, 1, 1, CaddxPropertyType.BIT, "Silent exit enabled", + false), + new CaddxProperty("partition_entryguard", 5, 1, 2, 1, CaddxPropertyType.BIT, "Entryguard (stay mode)", + false), + new CaddxProperty("partition_chime_mode_on", 5, 1, 3, 1, CaddxPropertyType.BIT, "Chime mode on", false), + new CaddxProperty("partition_entry", 5, 1, 4, 1, CaddxPropertyType.BIT, "Entry", false), + new CaddxProperty("partition_delay_expiration_warning", 5, 1, 5, 1, CaddxPropertyType.BIT, + "Delay expiration warning", false), + new CaddxProperty("partition_exit1", 5, 1, 6, 1, CaddxPropertyType.BIT, "Exit1", false), + new CaddxProperty("partition_exit2", 5, 1, 7, 1, CaddxPropertyType.BIT, "Exit2", false), + + // Byte 6 Partition condition flags (4) + new CaddxProperty("partition_led_extinguish", 6, 1, 0, 1, CaddxPropertyType.BIT, "LED extinguish", false), + new CaddxProperty("partition_cross_timing", 6, 1, 1, 1, CaddxPropertyType.BIT, "Cross timing", false), + new CaddxProperty("partition_recent_closing_being_timed", 6, 1, 2, 1, CaddxPropertyType.BIT, + "Recent closing being timed", false), + new CaddxProperty("partition_exit_error_triggered", 6, 1, 4, 1, CaddxPropertyType.BIT, + "Exit error triggered", false), + new CaddxProperty("partition_auto_home_inhibited", 6, 1, 5, 1, CaddxPropertyType.BIT, "Auto home inhibited", + false), + new CaddxProperty("partition_sensor_low_battery", 6, 1, 6, 1, CaddxPropertyType.BIT, "Sensor low battery", + false), + new CaddxProperty("partition_sensor_lost_supervision", 6, 1, 7, 1, CaddxPropertyType.BIT, + "Sensor lost supervision", false), + + new CaddxProperty("", 7, 1, 0, 0, CaddxPropertyType.INT, "Last user number", false), + + // Byte 8 Partition condition flags (5) + new CaddxProperty("partition_zone_bypassed", 8, 1, 0, 1, CaddxPropertyType.BIT, "Zone bypassed", false), + new CaddxProperty("partition_force_arm_triggered_by_auto_arm", 8, 1, 1, 1, CaddxPropertyType.BIT, + "Force arm triggered by auto arm", false), + new CaddxProperty("partition_ready_to_arm", 8, 1, 2, 1, CaddxPropertyType.BIT, "Ready to arm", false), + new CaddxProperty("partition_ready_to_force_arm", 8, 1, 3, 1, CaddxPropertyType.BIT, "Ready to force arm", + false), + new CaddxProperty("partition_valid_pin_accepted", 8, 1, 4, 1, CaddxPropertyType.BIT, "Valid PIN accepted", + false), + new CaddxProperty("partition_chime_on", 8, 1, 5, 1, CaddxPropertyType.BIT, "Chime on (sounding)", false), + new CaddxProperty("partition_error_beep", 8, 1, 6, 1, CaddxPropertyType.BIT, "Error beep (triple beep)", + false), + new CaddxProperty("partition_tone_on", 8, 1, 7, 1, CaddxPropertyType.BIT, "Tone on (activation tone)", + false), + + // Byte 9 Partition condition flags (6) + new CaddxProperty("partition_entry1", 9, 1, 0, 1, CaddxPropertyType.BIT, "Entry 1", false), + new CaddxProperty("partition_open_period", 9, 1, 1, 1, CaddxPropertyType.BIT, "Open period", false), + new CaddxProperty("partition_alarm_sent_using_phone_number_1", 9, 1, 2, 1, CaddxPropertyType.BIT, + "Alarm sent using phone number 1", false), + new CaddxProperty("partition_alarm_sent_using_phone_number_2", 9, 1, 3, 1, CaddxPropertyType.BIT, + "Alarm sent using phone number 2", false), + new CaddxProperty("partition_alarm_sent_using_phone_number_3", 9, 1, 4, 1, CaddxPropertyType.BIT, + "Alarm sent using phone number 3", false), + new CaddxProperty("partition_cancel_report_is_in_the_stack", 9, 1, 5, 1, CaddxPropertyType.BIT, + "Cancel report is in the stack", false), + new CaddxProperty("partition_keyswitch_armed", 9, 1, 6, 1, CaddxPropertyType.BIT, "Keyswitch armed", false), + new CaddxProperty("partition_delay_trip_in_progress", 9, 1, 7, 1, CaddxPropertyType.BIT, + "Delay Trip in progress (common zone)", false)), + + PARTITIONS_SNAPSHOT_MESSAGE(0x07, null, 9, "Partitions Snapshot Message", + "This message will contain an abbreviated set of information for all 8 partitions on the system.", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 Partition 1 condition flags + new CaddxProperty("partition_1_valid", 2, 1, 0, 1, CaddxPropertyType.BIT, "Partition 1 valid partition", + false), + new CaddxProperty("", 2, 1, 1, 1, CaddxPropertyType.BIT, "Partition 1 ready", false), + new CaddxProperty("", 2, 1, 2, 1, CaddxPropertyType.BIT, "Partition 1 armed", false), + new CaddxProperty("", 2, 1, 3, 1, CaddxPropertyType.BIT, "Partition 1 stay mode", false), + new CaddxProperty("", 2, 1, 4, 1, CaddxPropertyType.BIT, "Partition 1 chime mode", false), + new CaddxProperty("", 2, 1, 5, 1, CaddxPropertyType.BIT, "Partition 1 any entry delay", false), + new CaddxProperty("", 2, 1, 6, 1, CaddxPropertyType.BIT, "Partition 1 any exit delay", false), + new CaddxProperty("", 2, 1, 7, 1, CaddxPropertyType.BIT, "Partition 1 previous alarm", false), + + // Byte 3 Partition 2 condition flags + new CaddxProperty("partition_2_valid", 3, 1, 0, 1, CaddxPropertyType.BIT, "Partition 2 valid partition", + false), + new CaddxProperty("", 3, 1, 1, 1, CaddxPropertyType.BIT, "Partition 2 ready", false), + new CaddxProperty("", 3, 1, 2, 1, CaddxPropertyType.BIT, "Partition 2 armed", false), + new CaddxProperty("", 3, 1, 3, 1, CaddxPropertyType.BIT, "Partition 2 stay mode", false), + new CaddxProperty("", 3, 1, 4, 1, CaddxPropertyType.BIT, "Partition 2 chime mode", false), + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, "Partition 2 any entry delay", false), + new CaddxProperty("", 3, 1, 6, 1, CaddxPropertyType.BIT, "Partition 2 any exit delay", false), + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Partition 2 previous alarm", false), + + // Byte 4 Partition 3 condition flags + new CaddxProperty("partition_3_valid", 4, 1, 0, 1, CaddxPropertyType.BIT, "Partition 3 valid partition", + false), + new CaddxProperty("", 4, 1, 1, 1, CaddxPropertyType.BIT, "Partition 3 ready", false), + new CaddxProperty("", 4, 1, 2, 1, CaddxPropertyType.BIT, "Partition 3 armed", false), + new CaddxProperty("", 4, 1, 3, 1, CaddxPropertyType.BIT, "Partition 3 stay mode", false), + new CaddxProperty("", 4, 1, 4, 1, CaddxPropertyType.BIT, "Partition 3 chime mode", false), + new CaddxProperty("", 4, 1, 5, 1, CaddxPropertyType.BIT, "Partition 3 any entry delay", false), + new CaddxProperty("", 4, 1, 6, 1, CaddxPropertyType.BIT, "Partition 3 any exit delay", false), + new CaddxProperty("", 4, 1, 7, 1, CaddxPropertyType.BIT, "Partition 3 previous alarm", false), + + // Byte 5 Partition 4 condition flags + new CaddxProperty("partition_4_valid", 5, 1, 0, 1, CaddxPropertyType.BIT, "Partition 4 valid partition", + false), + new CaddxProperty("", 5, 1, 1, 1, CaddxPropertyType.BIT, "Partition 4 ready", false), + new CaddxProperty("", 5, 1, 2, 1, CaddxPropertyType.BIT, "Partition 4 armed", false), + new CaddxProperty("", 5, 1, 3, 1, CaddxPropertyType.BIT, "Partition 4 stay mode", false), + new CaddxProperty("", 5, 1, 4, 1, CaddxPropertyType.BIT, "Partition 4 chime mode", false), + new CaddxProperty("", 5, 1, 5, 1, CaddxPropertyType.BIT, "Partition 4 any entry delay", false), + new CaddxProperty("", 5, 1, 6, 1, CaddxPropertyType.BIT, "Partition 4 any exit delay", false), + new CaddxProperty("", 5, 1, 7, 1, CaddxPropertyType.BIT, "Partition 4 previous alarm", false), + + // Byte 6 Partition 5 condition flags + new CaddxProperty("partition_5_valid", 6, 1, 0, 1, CaddxPropertyType.BIT, "Partition 5 valid partition", + false), + new CaddxProperty("", 6, 1, 1, 1, CaddxPropertyType.BIT, "Partition 5 ready", false), + new CaddxProperty("", 6, 1, 2, 1, CaddxPropertyType.BIT, "Partition 5 armed", false), + new CaddxProperty("", 6, 1, 3, 1, CaddxPropertyType.BIT, "Partition 5 stay mode", false), + new CaddxProperty("", 6, 1, 4, 1, CaddxPropertyType.BIT, "Partition 5 chime mode", false), + new CaddxProperty("", 6, 1, 5, 1, CaddxPropertyType.BIT, "Partition 5 any entry delay", false), + new CaddxProperty("", 6, 1, 6, 1, CaddxPropertyType.BIT, "Partition 5 any exit delay", false), + new CaddxProperty("", 6, 1, 7, 1, CaddxPropertyType.BIT, "Partition 5 previous alarm", false), + + // Byte 7 Partition 6 condition flags + new CaddxProperty("partition_6_valid", 7, 1, 0, 1, CaddxPropertyType.BIT, "Partition 6 valid partition", + false), + new CaddxProperty("", 7, 1, 1, 1, CaddxPropertyType.BIT, "Partition 6 ready", false), + new CaddxProperty("", 7, 1, 2, 1, CaddxPropertyType.BIT, "Partition 6 armed", false), + new CaddxProperty("", 7, 1, 3, 1, CaddxPropertyType.BIT, "Partition 6 stay mode", false), + new CaddxProperty("", 7, 1, 4, 1, CaddxPropertyType.BIT, "Partition 6 chime mode", false), + new CaddxProperty("", 7, 1, 5, 1, CaddxPropertyType.BIT, "Partition 6 any entry delay", false), + new CaddxProperty("", 7, 1, 6, 1, CaddxPropertyType.BIT, "Partition 6 any exit delay", false), + new CaddxProperty("", 7, 1, 7, 1, CaddxPropertyType.BIT, "Partition 6 previous alarm", false), + + // Byte 8 Partition 7 condition flags + new CaddxProperty("partition_7_valid", 8, 1, 0, 1, CaddxPropertyType.BIT, "Partition 7 valid partition", + false), + new CaddxProperty("", 8, 1, 1, 1, CaddxPropertyType.BIT, "Partition 7 ready", false), + new CaddxProperty("", 8, 1, 2, 1, CaddxPropertyType.BIT, "Partition 7 armed", false), + new CaddxProperty("", 8, 1, 3, 1, CaddxPropertyType.BIT, "Partition 7 stay mode", false), + new CaddxProperty("", 8, 1, 4, 1, CaddxPropertyType.BIT, "Partition 7 chime mode", false), + new CaddxProperty("", 8, 1, 5, 1, CaddxPropertyType.BIT, "Partition 7 any entry delay", false), + new CaddxProperty("", 8, 1, 6, 1, CaddxPropertyType.BIT, "Partition 7 any exit delay", false), + new CaddxProperty("", 8, 1, 7, 1, CaddxPropertyType.BIT, "Partition 7 previous alarm", false), + + // Byte 9 Partition 8 condition flags + new CaddxProperty("partition_8_valid", 9, 1, 0, 1, CaddxPropertyType.BIT, "Partition 8 valid partition", + false), + new CaddxProperty("", 9, 1, 1, 1, CaddxPropertyType.BIT, "Partition 8 ready", false), + new CaddxProperty("", 9, 1, 2, 1, CaddxPropertyType.BIT, "Partition 8 armed", false), + new CaddxProperty("", 9, 1, 3, 1, CaddxPropertyType.BIT, "Partition 8 stay mode", false), + new CaddxProperty("", 9, 1, 4, 1, CaddxPropertyType.BIT, "Partition 8 chime mode", false), + new CaddxProperty("", 9, 1, 5, 1, CaddxPropertyType.BIT, "Partition 8 any entry delay", false), + new CaddxProperty("", 9, 1, 6, 1, CaddxPropertyType.BIT, "Partition 8 any exit delay", false), + new CaddxProperty("", 9, 1, 8, 1, CaddxPropertyType.BIT, "Partition 8 previous alarm", false)), + + SYSTEM_STATUS_MESSAGE(0x08, null, 12, "System Status Message", + "This message will contain all information relevant to the entire system.", CaddxDirection.IN, + CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Panel ID number", false), + + // Byte 3 + new CaddxProperty("", 3, 1, 0, 1, CaddxPropertyType.BIT, "Line seizure", false), + new CaddxProperty("", 3, 1, 1, 1, CaddxPropertyType.BIT, "Off hook", false), + new CaddxProperty("", 3, 1, 2, 1, CaddxPropertyType.BIT, "Initial handshake received", false), + new CaddxProperty("", 3, 1, 3, 1, CaddxPropertyType.BIT, "Download in progress", false), + new CaddxProperty("", 3, 1, 4, 1, CaddxPropertyType.BIT, "Dialer delay in progress", false), + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, "Using backup phone", false), + new CaddxProperty("", 3, 1, 6, 1, CaddxPropertyType.BIT, "Listen in active", false), + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Two way lockout", false), + + // Byte 4 + new CaddxProperty("", 4, 1, 0, 1, CaddxPropertyType.BIT, "Ground fault", false), + new CaddxProperty("", 4, 1, 1, 1, CaddxPropertyType.BIT, "Phone fault", false), + new CaddxProperty("", 4, 1, 2, 1, CaddxPropertyType.BIT, "Fail to communicate", false), + new CaddxProperty("", 4, 1, 3, 1, CaddxPropertyType.BIT, "Fuse fault", false), + new CaddxProperty("", 4, 1, 4, 1, CaddxPropertyType.BIT, "Box tamper", false), + new CaddxProperty("", 4, 1, 5, 1, CaddxPropertyType.BIT, "Siren tamper / trouble", false), + new CaddxProperty("", 4, 1, 6, 1, CaddxPropertyType.BIT, "Low Battery", false), + new CaddxProperty("", 4, 1, 7, 1, CaddxPropertyType.BIT, "AC fail", false), + + // Byte 5 + new CaddxProperty("", 5, 1, 0, 1, CaddxPropertyType.BIT, "Expander box tamper", false), + new CaddxProperty("", 5, 1, 1, 1, CaddxPropertyType.BIT, "Expander AC failure", false), + new CaddxProperty("", 5, 1, 2, 1, CaddxPropertyType.BIT, "Expander low battery", false), + new CaddxProperty("", 5, 1, 3, 1, CaddxPropertyType.BIT, "Expander loss of supervision", false), + new CaddxProperty("", 5, 1, 4, 1, CaddxPropertyType.BIT, "Expander auxiliary output over current", false), + new CaddxProperty("", 5, 1, 5, 1, CaddxPropertyType.BIT, "Auxiliary communication channel failure", false), + new CaddxProperty("", 5, 1, 6, 1, CaddxPropertyType.BIT, "Expander bell fault", false), + + // Byte 6 + new CaddxProperty("", 6, 1, 0, 1, CaddxPropertyType.BIT, "6 digit PIN enabled", false), + new CaddxProperty("", 6, 1, 1, 1, CaddxPropertyType.BIT, "Programming token in use", false), + new CaddxProperty("", 6, 1, 2, 1, CaddxPropertyType.BIT, "PIN required for local download", false), + new CaddxProperty("", 6, 1, 3, 1, CaddxPropertyType.BIT, "Global pulsing buzzer", false), + new CaddxProperty("", 6, 1, 4, 1, CaddxPropertyType.BIT, "Global Siren on", false), + new CaddxProperty("", 6, 1, 5, 1, CaddxPropertyType.BIT, "Global steady siren", false), + new CaddxProperty("", 6, 1, 6, 1, CaddxPropertyType.BIT, "Bus device has line seized", false), + new CaddxProperty("", 6, 1, 7, 1, CaddxPropertyType.BIT, "Bus device has requested sniff mode", false), + + // Byte 7 + new CaddxProperty("", 7, 1, 0, 1, CaddxPropertyType.BIT, "Dynamic battery test", false), + new CaddxProperty("", 7, 1, 1, 1, CaddxPropertyType.BIT, "AC power on", false), + new CaddxProperty("", 7, 1, 2, 1, CaddxPropertyType.BIT, "Low battery memory", false), + new CaddxProperty("", 7, 1, 3, 1, CaddxPropertyType.BIT, "Ground fault memory", false), + new CaddxProperty("", 7, 1, 4, 1, CaddxPropertyType.BIT, "Fire alarm verification being timed", false), + new CaddxProperty("", 7, 1, 5, 1, CaddxPropertyType.BIT, "Smoke power reset", false), + new CaddxProperty("", 7, 1, 6, 1, CaddxPropertyType.BIT, "50 Hz line power detected", false), + new CaddxProperty("", 7, 1, 7, 1, CaddxPropertyType.BIT, "Timing a high voltage battery charge", false), + + // Byte 8 + new CaddxProperty("", 8, 1, 0, 1, CaddxPropertyType.BIT, "Communication since last autotest", false), + new CaddxProperty("", 8, 1, 1, 1, CaddxPropertyType.BIT, "Power up delay in progress", false), + new CaddxProperty("", 8, 1, 2, 1, CaddxPropertyType.BIT, "Walk test mode", false), + new CaddxProperty("", 8, 1, 3, 1, CaddxPropertyType.BIT, "Loss of system time", false), + new CaddxProperty("", 8, 1, 4, 1, CaddxPropertyType.BIT, "Enroll requested", false), + new CaddxProperty("", 8, 1, 5, 1, CaddxPropertyType.BIT, "Test fixture mode", false), + new CaddxProperty("", 8, 1, 6, 1, CaddxPropertyType.BIT, "Control shutdown mode", false), + new CaddxProperty("", 8, 1, 7, 1, CaddxPropertyType.BIT, "Timing a cancel window", false), + + // Byte 9 + new CaddxProperty("", 9, 1, 7, 1, CaddxPropertyType.BIT, "Call back in progress", false), + + // Byte 10 + new CaddxProperty("", 10, 1, 0, 1, CaddxPropertyType.BIT, "Phone line faulted", false), + new CaddxProperty("", 10, 1, 1, 1, CaddxPropertyType.BIT, "Voltage present interrupt active", false), + new CaddxProperty("", 10, 1, 2, 1, CaddxPropertyType.BIT, "House phone off hook", false), + new CaddxProperty("", 10, 1, 3, 1, CaddxPropertyType.BIT, "Phone line monitor enabled", false), + new CaddxProperty("", 10, 1, 4, 1, CaddxPropertyType.BIT, "Sniffing", false), + new CaddxProperty("", 10, 1, 5, 1, CaddxPropertyType.BIT, "Last read was off hook", false), + new CaddxProperty("", 10, 1, 6, 1, CaddxPropertyType.BIT, "Listen in requested", false), + new CaddxProperty("", 10, 1, 7, 1, CaddxPropertyType.BIT, "Listen in trigger", false), + + // Byte 11 + new CaddxProperty("", 11, 1, 0, 1, CaddxPropertyType.BIT, "Valid partition 1", false), + new CaddxProperty("", 11, 1, 1, 1, CaddxPropertyType.BIT, "Valid partition 2", false), + new CaddxProperty("", 11, 1, 2, 1, CaddxPropertyType.BIT, "Valid partition 3", false), + new CaddxProperty("", 11, 1, 3, 1, CaddxPropertyType.BIT, "Valid partition 4", false), + new CaddxProperty("", 11, 1, 4, 1, CaddxPropertyType.BIT, "Valid partition 5", false), + new CaddxProperty("", 11, 1, 5, 1, CaddxPropertyType.BIT, "Valid partition 6", false), + new CaddxProperty("", 11, 1, 6, 1, CaddxPropertyType.BIT, "Valid partition 7", false), + new CaddxProperty("", 11, 1, 7, 1, CaddxPropertyType.BIT, "Valid partition 8", false), + + // Byte 12 Communicator stack pointer + new CaddxProperty("panel_communicator_stack_pointer", 12, 1, 0, 0, CaddxPropertyType.INT, + "Communicator stack pointer", false)), + + X10_MESSAGE_RECEIVED(0x09, null, 4, "X-10 Message Received", + "This message contains information about an X-10 command that was requested by any device on the system bus.", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "House code (0=house A)", false), + + // Byte 3 + new CaddxProperty("", 3, 1, 0, 0, CaddxPropertyType.INT, "Unit code (0=unit 1)", false), + + // Byte 4 + new CaddxProperty("", 4, 1, 0, 0, CaddxPropertyType.INT, "X-10 function code", false)), + + LOG_EVENT_MESSAGE(0x0a, null, 10, "Log Event Message", + "This message will contain all information relating to an event in the log memory.", CaddxDirection.IN, + CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 + new CaddxProperty("panel_log_event_number", 2, 1, 0, 0, CaddxPropertyType.INT, + "Event number of this message", false), + // Byte 3 + new CaddxProperty("panel_log_event_size", 3, 1, 0, 0, CaddxPropertyType.INT, + "Total log size (number of log entries allowed)", false), + + // Byte 4 + new CaddxProperty("panel_log_event_type", 4, 1, 0, 7, CaddxPropertyType.INT, "Event type", false), + // Bits 0-6 See type definitions in table that follows + // Bit 7 Non-reporting event if not set + + // Byte 5 + new CaddxProperty("panel_log_event_zud", 5, 1, 0, 0, CaddxPropertyType.INT, "Zone / User / Device number", + false), + // Byte 6 + new CaddxProperty("panel_log_event_partition", 6, 1, 0, 0, CaddxPropertyType.INT, + "Partition number (0=partition 1, if relevant)", false), + // Byte 7 + new CaddxProperty("panel_log_event_month", 7, 1, 0, 0, CaddxPropertyType.INT, "Month (1-12)", false), + // Byte 8 + new CaddxProperty("panel_log_event_day", 8, 1, 0, 0, CaddxPropertyType.INT, "Day (1-31)", false), + // Byte 9 + new CaddxProperty("panel_log_event_hour", 9, 1, 0, 0, CaddxPropertyType.INT, "Hour (0-23)", false), + // Byte 10 + new CaddxProperty("panel_log_event_minute", 10, 1, 0, 0, CaddxPropertyType.INT, "Minute (0-59)", false)), + + KEYPAD_MESSAGE_RECEIVED(0x0b, null, 3, "Keypad Message Received", + "This message contains a keystroke from a keypad that is in a Terminal Mode.", CaddxDirection.IN, + CaddxSource.KEYPAD, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("keypad_address", 1, 2, 0, 0, CaddxPropertyType.INT, "Keypad address", false), + + // Byte 3 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Key value", false)), + + PROGRAM_DATA_REPLY(0x10, null, 13, "Program Data Reply", + "This message will contain a system device’s buss address, logical location, and program data that was previously requested (via Program Data Request (3Ch)).", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Device’s buss address", false), + + // Byte 3 Upper logical location / offset + new CaddxProperty("", 3, 1, 0, 3, CaddxPropertyType.INT, "Bits 8-11 of logical location", false), + new CaddxProperty("", 3, 1, 4, 4, CaddxPropertyType.INT, "Segment size (0=byte, 1=nibble)", false), + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, "Must be 0", false), + new CaddxProperty("", 3, 1, 6, 6, CaddxPropertyType.INT, "Segment offset (0-none, 1=8 bytes)", false), + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Must be 0", false), + + // Byte 4 Bits 0-7 of logical location + new CaddxProperty("", 4, 1, 0, 0, CaddxPropertyType.INT, "Bits 0-7 of logical location", false), + + // Byte 5 Location length / data type + new CaddxProperty("", 5, 1, 0, 4, CaddxPropertyType.INT, "Number of segments in location (0=1 segment)", + false), + new CaddxProperty("", 5, 1, 5, 7, CaddxPropertyType.INT, + "Data type : 0=Binary 1=Decimal 2=Hexadecimal 3=ASCII 4=unused 5=unused 6=unused 7=unused", false), + + // Byte 6 Data byte + new CaddxProperty("", 6, 1, 0, 0, CaddxPropertyType.INT, "Data byte 0", false), + // Byte 7 Data byte + new CaddxProperty("", 7, 1, 0, 0, CaddxPropertyType.INT, "Data byte 1", false), + // Byte 8 Data byte + new CaddxProperty("", 8, 1, 0, 0, CaddxPropertyType.INT, "Data byte 2", false), + // Byte 9 Data byte + new CaddxProperty("", 9, 1, 0, 0, CaddxPropertyType.INT, "Data byte 3", false), + // Byte 10 Data byte + new CaddxProperty("", 10, 1, 0, 0, CaddxPropertyType.INT, "Data byte 4", false), + // Byte 11 Data byte + new CaddxProperty("", 11, 1, 0, 0, CaddxPropertyType.INT, "Data byte 5", false), + // Byte 12 Data byte + new CaddxProperty("", 12, 1, 0, 0, CaddxPropertyType.INT, "Data byte 6", false), + // Byte 13 Data byte + new CaddxProperty("", 13, 1, 0, 0, CaddxPropertyType.INT, "Data byte 7", false)), + + USER_INFORMATION_REPLY(0x12, null, 7, "User Information Reply", + "This message will contain all digits, attributes and partitions for the requested user PIN number that was previously requested (via User Information Request with(out) PIN (32h,33h)).", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "User Number (1=user 1)", false), + + // Byte 3 PIN digits 1 & 2 + new CaddxProperty("", 3, 1, 0, 3, CaddxPropertyType.INT, "PIN digit 1", false), + new CaddxProperty("", 3, 1, 4, 7, CaddxPropertyType.INT, "PIN digit 2", false), + + // Byte 4 PIN digits 3 & 4 + new CaddxProperty("", 4, 1, 0, 3, CaddxPropertyType.INT, "PIN digit 3", false), + new CaddxProperty("", 4, 1, 4, 7, CaddxPropertyType.INT, "PIN digit 4", false), + + // Byte 5 PIN digits 5 & 6 + new CaddxProperty("", 5, 1, 0, 3, CaddxPropertyType.INT, "PIN digit 5 (pad with 0 if 4 digit PIN)", false), + new CaddxProperty("", 5, 1, 4, 7, CaddxPropertyType.INT, "PIN digit 6 (pad with 0 if 4 digit PIN)", false), + + // Byte 6* Authority flags + new CaddxProperty("", 6, 1, 0, 1, CaddxPropertyType.BIT, + "Reserved (if bit 7 is clear) || Output 1 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 1, 1, CaddxPropertyType.BIT, + "Arm only (if bit 7 is clear) || Output 2 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 2, 1, CaddxPropertyType.BIT, + "Arm only (during close window) (if bit 7 is clear) || Output 3 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 3, 1, CaddxPropertyType.BIT, + "Master / program (if bit 7 is clear) || Output 4 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 4, 1, CaddxPropertyType.BIT, + "Arm / Disarm (if bit 7 is clear) || Arm / Disarm (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 5, 1, CaddxPropertyType.BIT, + "Bypass enable (if bit 7 is clear) || Bypass enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 6, 1, CaddxPropertyType.BIT, + "Open / close report enable (if bit 7 is clear) || Open / close report enable (if bit 7 is set)", + false), + new CaddxProperty("", 6, 1, 7, 1, CaddxPropertyType.BIT, + "Must be a 0 (if bit 7 is clear) || Must be a 1 (if bit 7 is set)", false), + + // Byte 7 Authorized partition(s) mask + new CaddxProperty("", 7, 1, 0, 1, CaddxPropertyType.BIT, "Authorized for partition 1", false), + new CaddxProperty("", 7, 1, 1, 1, CaddxPropertyType.BIT, "Authorized for partition 2", false), + new CaddxProperty("", 7, 1, 2, 1, CaddxPropertyType.BIT, "Authorized for partition 3", false), + new CaddxProperty("", 7, 1, 3, 1, CaddxPropertyType.BIT, "Authorized for partition 4", false), + new CaddxProperty("", 7, 1, 4, 1, CaddxPropertyType.BIT, "Authorized for partition 5", false), + new CaddxProperty("", 7, 1, 5, 1, CaddxPropertyType.BIT, "Authorized for partition 6", false), + new CaddxProperty("", 7, 1, 6, 1, CaddxPropertyType.BIT, "Authorized for partition 7", false), + new CaddxProperty("", 7, 1, 7, 1, CaddxPropertyType.BIT, "Authorized for partition 8", false)), + + REQUEST_FAILED(0x1c, null, 1, "Command / Request Failed", + "This message is sent in place of a ‘Positive Acknowledge’ message when a command or request was received properly, but the system was unable to carry out the task correctly. This would normally occur 2.5 seconds after receiving the initial command or request.", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false)), + + POSITIVE_ACKNOWLEDGE(0x1d, null, 1, "Positive Acknowledge", + "This message will acknowledge receipt of a message that had the ‘Acknowledge Required’ flag set in the command byte.", + CaddxDirection.IN, CaddxSource.PANEL, + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false)), + + NEGATIVE_ACKNOWLEDGE(0x1e, null, 1, "Negative Acknowledge", + "This message is sent in place of a ‘Positive Acknowledge’ message when the message received was not properly formatted. It will also be sent if an additional message is received before a reply has been returned during the 2.5 second allowable reply period of a previous message. An ‘Implied Negative Acknowledge’ is assumed when no acknowledge is returned with 3 seconds.", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false)), + + MESSAGE_REJECTED(0x1f, null, 1, "Message Rejected", + "This message is sent in place of a ‘Positive Acknowledge’ message when the message was received properly formatted, but not supported or disabled.", + CaddxDirection.IN, CaddxSource.PANEL, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false)), + + INTERFACE_CONFIGURATION_REQUEST(0x21, new int[] { 0x01, 0x1c, 0x1f }, 1, "Interface Configuration Request", + "This request will cause the return of the Interface Configuration Message (01h) containing information about the options selected on the interface.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false)), + + ZONE_NAME_REQUEST(0x23, new int[] { 0x03, 0x1c, 0x1f }, 2, "Zone Name Request", + "This request will cause the return of the Zone Name Message (03h) for the zone number that was requested.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Zone number (0= zone 1)", true)), + + ZONE_STATUS_REQUEST(0x24, new int[] { 0x04, 0x1c, 0x1f }, 2, "Zone Status Request", + "This request will cause the return of the Zone Status Message (04h) for the zone number that was requested.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("zone_number", 2, 1, 0, 0, CaddxPropertyType.INT, "Zone number (0= zone 1)", true)), + + ZONES_SNAPSHOT_REQUEST(0x25, new int[] { 0x05, 0x1c, 0x1f }, 2, "Zones Snapshot Request", + "This request will cause the return of the Zones Snapshot Message (05h) with the group of zones starting at the zone 1 plus the offset value.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Zone number offset (0= start at zone 1)", true)), + + PARTITION_STATUS_REQUEST(0x26, new int[] { 0x06, 0x1c, 0x1f }, 2, "Partition Status Request", + "This request will cause the return of the Partition Status Message (06h) for the partition number that was requested.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("partition_number", 2, 1, 0, 0, CaddxPropertyType.INT, + "Partition number (0= partition 1)", true)), + + PARTITIONS_SNAPSHOT_REQUEST(0x27, new int[] { 0x07, 0x1c, 0x1f }, 1, "Partitions Snapshot Request", + "This request will cause the return of the Partitions Snapshot Message (07h) containing all partitions.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false)), + + SYSTEM_STATUS_REQUEST(0x28, new int[] { 0x08, 0x1c, 0x1f }, 1, "System Status Request", + "This request will cause the return of the System Status Message (08h).", CaddxDirection.OUT, + CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false)), + + SEND_X_10_MESSAGE(0x29, new int[] { 0x1d, 0x1c, 0x1f }, 4, "Send X-10 Message", + "This message will contain information about an X-10 command that should be resent on the system bus.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "House code (0=house A) ", true), + + // Byte 3 + new CaddxProperty("", 3, 1, 0, 0, CaddxPropertyType.INT, "Unit code (0=unit 1)", true), + + // Byte 4 + new CaddxProperty("", 4, 1, 0, 0, CaddxPropertyType.INT, "X-10 function code (see table at message # 0Ah)", + true)), + + LOG_EVENT_REQUEST(0x2a, new int[] { 0x0a, 0x1c, 0x1f }, 2, "Log Event Request", + "This request will cause the return of the Log Event Message (0Ah).", CaddxDirection.OUT, CaddxSource.NONE, + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + new CaddxProperty("panel_log_event_number", 2, 1, 0, 0, CaddxPropertyType.INT, "Event number requested", + true)), + + SEND_KEYPAD_TEXT_MESSAGE(0x2b, new int[] { 0x1d, 0x1c, 0x1f }, 12, "Send Keypad Text Message", + "This message will contain ASCII text for a specific keypad on the bus that will be displayed during Terminal Mode.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 Keypad address + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Keypad address", false), + // Byte 3 Keypad type (0=NX-148e)(all others not supported) + new CaddxProperty("", 3, 1, 0, 0, CaddxPropertyType.INT, "Keypad type", false), + // Byte 4 Display storage location (0=top left corner + new CaddxProperty("", 4, 1, 0, 0, CaddxPropertyType.INT, "Display storage location", false), + // Byte 5 ASCII character for location +0 + new CaddxProperty("", 5, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +0", false), + // Byte 6 ASCII character for location +1 + new CaddxProperty("", 6, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +1", false), + // Byte 7 ASCII character for location +2 + new CaddxProperty("", 7, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +2", false), + // Byte 8 ASCII character for location +3 + new CaddxProperty("", 8, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +3", false), + // Byte 9 ASCII character for location +4 + new CaddxProperty("", 9, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +4", false), + // Byte 10 ASCII character for location +5 + new CaddxProperty("", 10, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +5", false), + // Byte 11 ASCII character for location +6 + new CaddxProperty("", 11, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +6", false), + // Byte 12 ASCII character for location +7 + new CaddxProperty("", 12, 1, 0, 0, CaddxPropertyType.INT, "ASCII character for location +7", false)), + + KEYPAD_TERMINAL_MODE_REQUEST(0x2c, new int[] { 0x1d, 0x1c, 0x1f }, 3, "Keypad Terminal Mode Request", + "This message will contain the address of a keypad that should enter a Terminal Mode for the time contained. Only one keypad should be in the Terminal Mode at a time.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 Keypad address + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Keypad address", false), + // Byte 3 + new CaddxProperty("", 3, 1, 0, 0, CaddxPropertyType.INT, "Number of seconds for Terminal Mode", false)), + + PROGRAM_DATA_REQUEST(0x30, new int[] { 0x10, 0x1c, 0x1f }, 4, "Program Data Request", + "This message will contain a system device’s buss address and the logical location of program data that will be returned in a Program Data Reply message (10h).", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 Device’s buss address + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Device’s buss address", false), + // Byte 3 Upper logical location / offset + // Bits 0-3 Bits 8-11 of logical location + new CaddxProperty("", 3, 1, 0, 4, CaddxPropertyType.INT, "Bits 8-11 of logical location", false), + // Bits 4,5 Must be 0 + new CaddxProperty("", 3, 1, 4, 2, CaddxPropertyType.BIT, "Must be 0", false), + // Bit 6 Segment offset (0-none, 1=8 bytes) + new CaddxProperty("", 3, 1, 6, 1, CaddxPropertyType.BIT, "Segment offset (0-none, 1=8 bytes)", false), + // Bit 7 + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Must be 0", false), + // Byte 4 Bits 0-7 of logical location + new CaddxProperty("", 4, 1, 0, 0, CaddxPropertyType.INT, "Bits 0-7 of logical location", false)), + + PROGRAM_DATA_COMMAND(0x31, new int[] { 0x1d, 0x1c, 0x1f }, 13, "Program Data Command", + "This message will contain a system device’s buss address and the logical location where the included data should be stored.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 Device’s buss address + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Device’s buss address", false), + // Byte 3 Upper logical location / offset + // Bits 0-3 Bits 8-11 of logical location + new CaddxProperty("", 3, 1, 0, 4, CaddxPropertyType.BIT, "Bits 8-11 of logical location", false), + // Bit 4 Segment size (0=byte, 1=nibble) + new CaddxProperty("", 3, 1, 4, 1, CaddxPropertyType.BIT, "Segment size (0=byte, 1=nibble)", false), + // Bit 5 Must be 1 + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, "Must be 1", false), + // Bit 6 Segment offset (0-none, 1=8 bytes) + new CaddxProperty("", 3, 1, 6, 1, CaddxPropertyType.BIT, "Segment offset (0-none, 1=8 bytes)", false), + // Bit 7 Must be 0 + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Must be 0", false), + // Byte 4 Bits 0-7 of logical location + new CaddxProperty("", 4, 1, 0, 0, CaddxPropertyType.INT, "Bits 0-7 of logical location", false), + // Byte 5 Location length / data type + // Bits 0-4 Number of segments in location (0=1 segment) + new CaddxProperty("", 5, 1, 0, 5, CaddxPropertyType.BIT, "Number of segments in location (0=1 segment)", + false), + // Bits 5-7 Data type : 5=unused + new CaddxProperty("", 5, 1, 5, 3, CaddxPropertyType.BIT, + "Data type: 0=Binary, 1=Decimal, 2=Hexadecimal, 3=ASCII, 4=unused, 5=unused, 6=unused, 7=unused", + false), + // Byte 6 Data byte 1 to store + new CaddxProperty("", 6, 1, 0, 0, CaddxPropertyType.INT, "Data byte 1 to store", false), + // Byte 7 Data byte 2 to store + new CaddxProperty("", 7, 1, 0, 0, CaddxPropertyType.INT, "Data byte 2 to store", false), + // Byte 8 Data byte 3 to store + new CaddxProperty("", 8, 1, 0, 0, CaddxPropertyType.INT, "Data byte 3 to store", false), + // Byte 9 Data byte 4 to store + new CaddxProperty("", 9, 1, 0, 0, CaddxPropertyType.INT, "Data byte 4 to store", false), + // Byte 10 Data byte 5 to store + new CaddxProperty("", 10, 1, 0, 0, CaddxPropertyType.INT, "Data byte 5 to store", false), + // Byte 11 Data byte 6 to store + new CaddxProperty("", 11, 1, 0, 0, CaddxPropertyType.INT, "Data byte 6 to store", false), + // Byte 12 Data byte 7 to store + new CaddxProperty("", 12, 1, 0, 0, CaddxPropertyType.INT, "Data byte 7 to store", false), + // Byte 13 Data byte 8 to store + new CaddxProperty("", 13, 1, 0, 0, CaddxPropertyType.INT, "Data byte 8 to store", false)), + + USER_INFORMATION_REQUEST_WITH_PIN(0x32, new int[] { 0x12, 0x1c, 0x1f }, 5, "User Information Request with PIN", + "This message will contain a user number for which information is being requested and a PIN that will be checked for Master capability before proceeding. The information will be returned in a User Information Reply message (12h).", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 (Master) PIN digits 1 & 2 + // Bits 0-3 PIN digit 1 + new CaddxProperty("", 2, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 1", false), + // Bits 4-7 PIN digit 2 + new CaddxProperty("", 2, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 2", false), + // Byte 3 (Master) PIN digits 3 & 4 + // Bits 0-3 PIN digit 3 + new CaddxProperty("", 3, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 3", false), + // Bits 4-7 PIN digit 4 + new CaddxProperty("", 3, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 4", false), + // Byte 4 (Master) PIN digits 5 & 6 + // Bits 0-3 PIN digit 5 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 4, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 5 (pad with 0 if 4 digit PIN)", false), + // Bits 4-7 PIN digit 6 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 4, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 6 (pad with 0 if 4 digit PIN)", false), + // Byte 5 User number (1=user 1) + new CaddxProperty("", 5, 1, 0, 0, CaddxPropertyType.INT, "User number (1=user 1)", false)), + + USER_INFORMATION_REQUEST_WITHOUT_PIN(0x33, new int[] { 0x12, 0x1c, 0x1f }, 2, + "User Information Request without PIN", + "This message will contain a user number for which information is being requested, no authentication will be performed. The information will be returned in a User Information Reply message (12h).", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 User number (1=user 1) + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "User number (1=user 1)", false)), + + SET_USER_CODE_COMMAND_WITH_PIN(0x34, new int[] { 0x12, 0x1c, 0x1f }, 8, "Set User Code Command with PIN", + "This message will contain all digits that should be stored as the new code for the designated User number. A PIN will be checked for Master capability before proceeding. A successful programming of the user code will result in the User Information Reply (12h) returned in place of the acknowledge.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 (Master) PIN digits 1 & 2 + // Bits 0-3 PIN digit 1 + new CaddxProperty("", 2, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 1", false), + // Bits 4-7 PIN digit 2 + new CaddxProperty("", 2, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 2", false), + // Byte 3 (Master) PIN digits 3 & 4 + // Bits 0-3 PIN digit 3 + new CaddxProperty("", 3, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 3", false), + // Bits 4-7 PIN digit 4 + new CaddxProperty("", 3, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 4", false), + // Byte 4 (Master) PIN digits 5 & 6 + // Bits 0-3 PIN digit 5 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 4, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 5 (pad with 0 if 4 digit PIN)", false), + // Bits 4-7 PIN digit 6 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 4, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 6 (pad with 0 if 4 digit PIN)", false), + // Byte 5 User number (1=user 1) + new CaddxProperty("", 5, 1, 0, 0, CaddxPropertyType.INT, "User number (1=user 1)", false), + // Byte 6 PIN digits 1 & 2 + // Bits 0-3 PIN digit 1 + new CaddxProperty("", 6, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 1", false), + // Bits 4-7 PIN digit 2 + new CaddxProperty("", 6, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 2", false), + // Byte 7 PIN digits 3 & 4 + // Bits 0-3 PIN digit 3 + new CaddxProperty("", 7, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 3", false), + // Bits 4-7 PIN digit 4 + new CaddxProperty("", 7, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 4", false), + // Byte 8 PIN digits 5 & 6 + // Bits 0-3 PIN digit 5 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 8, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 5 (pad with 0 if 4 digit PIN)", false), + // Bits 4-7 PIN digit 6 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 8, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 6 (pad with 0 if 4 digit PIN)", false)), + + SET_USER_CODE_COMMAND_WITHOUT_PIN(0x35, new int[] { 0x12, 0x1c, 0x1f }, 5, "Set User Code Command without PIN", + "This message will contain all digits that should be stored as the new code for the designated User number. No authentication will be performed. A successful programming of the user code will result in the User Information Reply (12h) returned in place of the acknowledge.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + // Byte 2 User number (1=user 1) + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "User number (1=user 1)", false), + // Byte 3 PIN digits 1 & 2 + // Bits 0-3 PIN digit 1 + new CaddxProperty("", 3, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 1", false), + // Bits 4-7 PIN digit 2 + new CaddxProperty("", 3, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 2", false), + // Byte 4 PIN digits 3 & 4 + // Bits 0-3 PIN digit 3 + new CaddxProperty("", 4, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 3", false), + // Bits 4-7 PIN digit 4 + new CaddxProperty("", 4, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 4", false), + // Byte 5 PIN digits 5 & 6 + // Bits 0-3 PIN digit 5 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 5, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 5 (pad with 0 if 4 digit PIN)", false), + // Bits 4-7 PIN digit 6 (pad with 0 if 4 digit PIN) + new CaddxProperty("", 5, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 6 (pad with 0 if 4 digit PIN)", false)), + + SET_USER_AUTHORIZATION_COMMAND_WITH_PIN(0x36, new int[] { 0x1d, 0x1c, 0x1f }, 7, + "Set User Authorization Command with PIN", + "This message will contain all attributes and partitions that should be stored as the new information for the designated User number. A PIN will be checked for Master capability before proceeding.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 (Master) PIN digits 1 & 2 + new CaddxProperty("", 2, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 1", false), + new CaddxProperty("", 2, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 2", false), + + // Byte 3 (Master) PIN digits 3 & 4 + new CaddxProperty("", 3, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 3", false), + new CaddxProperty("", 3, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 4", false), + + // Byte 4 (Master) PIN digits 5 & 6 + new CaddxProperty("", 4, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 5 (pad with 0 if 4 digit PIN)", false), + new CaddxProperty("", 4, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 6 (pad with 0 if 4 digit PIN)", false), + + // Byte 5 User number (1=user 1) + new CaddxProperty("", 5, 1, 0, 0, CaddxPropertyType.INT, "User number (1=user 1)", false), + + // Byte 6 Authority flags + new CaddxProperty("", 6, 1, 0, 1, CaddxPropertyType.BIT, + "Reserved (if bit 7 is clear) || Output 1 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 1, 1, CaddxPropertyType.BIT, + "Arm only (if bit 7 is clear) || Output 2 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 2, 1, CaddxPropertyType.BIT, + "Arm only (during close window) (if bit 7 is clear) || Output 3 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 3, 1, CaddxPropertyType.BIT, + "Master / program (if bit 7 is clear) || Output 4 enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 4, 1, CaddxPropertyType.BIT, + "Arm / Disarm (if bit 7 is clear) || Arm / Disarm (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 5, 1, CaddxPropertyType.BIT, + "Bypass enable (if bit 7 is clear) || Bypass enable (if bit 7 is set)", false), + new CaddxProperty("", 6, 1, 6, 1, CaddxPropertyType.BIT, + "Open / close report enable (if bit 7 is clear) || Open / close report enable (if bit 7 is set)", + false), + new CaddxProperty("", 6, 1, 7, 1, CaddxPropertyType.BIT, + "Must be a 0 (if bit 7 is clear) || Must be a 1 (if bit 7 is set)", false), + + // Byte 7 Authorized partition(s) mask + new CaddxProperty("", 7, 1, 0, 1, CaddxPropertyType.BIT, "Authorized for partition 1", false), + new CaddxProperty("", 7, 1, 1, 1, CaddxPropertyType.BIT, "Authorized for partition 2", false), + new CaddxProperty("", 7, 1, 2, 1, CaddxPropertyType.BIT, "Authorized for partition 3", false), + new CaddxProperty("", 7, 1, 3, 1, CaddxPropertyType.BIT, "Authorized for partition 4", false), + new CaddxProperty("", 7, 1, 4, 1, CaddxPropertyType.BIT, "Authorized for partition 5", false), + new CaddxProperty("", 7, 1, 5, 1, CaddxPropertyType.BIT, "Authorized for partition 6", false), + new CaddxProperty("", 7, 1, 6, 1, CaddxPropertyType.BIT, "Authorized for partition 7", false), + new CaddxProperty("", 7, 1, 7, 1, CaddxPropertyType.BIT, "Authorized for partition 8", false)), + + SET_USER_AUTHORIZATION_COMMAND_WITHOUT_PIN(0x37, new int[] { 0x1d, 0x1c, 0x1f }, 4, + "Set User Authorization Command without PIN", + "This message will contain all attributes and partitions that should be stored as the new information for the designated User number. No authentication will be performed.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 User number (1=user 1) + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "User number (1=user 1)", false), + + // Byte 3 Authority flags + new CaddxProperty("", 3, 1, 0, 1, CaddxPropertyType.BIT, + "Reserved (if bit 7 is clear) || Output 1 enable (if bit 7 is set)", false), + new CaddxProperty("", 3, 1, 1, 1, CaddxPropertyType.BIT, + "Arm only (if bit 7 is clear) || Output 2 enable (if bit 7 is set)", false), + new CaddxProperty("", 3, 1, 2, 1, CaddxPropertyType.BIT, + "Arm only (during close window) (if bit 7 is clear) || Output 3 enable (if bit 7 is set)", false), + new CaddxProperty("", 3, 1, 3, 1, CaddxPropertyType.BIT, + "Master / program (if bit 7 is clear) || Output 4 enable (if bit 7 is set)", false), + new CaddxProperty("", 3, 1, 4, 1, CaddxPropertyType.BIT, + "Arm / Disarm (if bit 7 is clear) || Arm / Disarm (if bit 7 is set)", false), + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, + "Bypass enable (if bit 7 is clear) || Bypass enable (if bit 7 is set)", false), + new CaddxProperty("", 3, 1, 6, 1, CaddxPropertyType.BIT, + "Open / close report enable (if bit 7 is clear) || Open / close report enable (if bit 7 is set)", + false), + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, + "Must be a 0 (if bit 7 is clear) || Must be a 1 (if bit 7 is set)", false), + + // Byte 4 Authorized partition(s) mask + new CaddxProperty("", 4, 1, 0, 1, CaddxPropertyType.BIT, "Authorized for partition 1", false), + new CaddxProperty("", 4, 1, 1, 1, CaddxPropertyType.BIT, "Authorized for partition 2", false), + new CaddxProperty("", 4, 1, 2, 1, CaddxPropertyType.BIT, "Authorized for partition 3", false), + new CaddxProperty("", 4, 1, 3, 1, CaddxPropertyType.BIT, "Authorized for partition 4", false), + new CaddxProperty("", 4, 1, 4, 1, CaddxPropertyType.BIT, "Authorized for partition 5", false), + new CaddxProperty("", 4, 1, 5, 1, CaddxPropertyType.BIT, "Authorized for partition 6", false), + new CaddxProperty("", 4, 1, 6, 1, CaddxPropertyType.BIT, "Authorized for partition 7", false), + new CaddxProperty("", 4, 1, 7, 1, CaddxPropertyType.BIT, "Authorized for partition 8", false)), + + STORE_COMMUNICATION_EVENT_COMMAND(0x3a, new int[] { 0x1d, 0x1c, 0x1f }, 6, "Store Communication Event Command", + "This message will submit an event to the control’s communication stack for possible transmission over its telephone or alternate communications path.", + CaddxDirection.OUT, CaddxSource.NONE), + + SET_CLOCK_CALENDAR_COMMAND(0x3b, new int[] { 0x1d, 0x1c, 0x1f }, 7, "Set Clock / Calendar Command", + "This message will set the clock / calendar in the system.", CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 Year (00-99) + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Year (00-99)", false), + + // Byte 3 Month (1-12) + new CaddxProperty("", 3, 1, 0, 0, CaddxPropertyType.INT, "Month (1-12)", false), + + // Byte 4 Day (1-31) + new CaddxProperty("", 4, 1, 0, 0, CaddxPropertyType.INT, "Day (1-31)", false), + + // Byte 5 Hour (0-23) + new CaddxProperty("", 5, 1, 0, 0, CaddxPropertyType.INT, "Hour (0-23)", false), + + // Byte 6 Minute (0-59) + new CaddxProperty("", 6, 1, 0, 0, CaddxPropertyType.INT, "Minute (0-59)", false), + + // Byte 7 Day + new CaddxProperty("", 7, 1, 0, 0, CaddxPropertyType.INT, "Day", false)), + + PRIMARY_KEYPAD_FUNCTION_WITH_PIN(0x3c, new int[] { 0x1d, 0x1c, 0x1f }, 6, "Primary Keypad Function with PIN", + "This message will contain a value that defines with function to perform, the partitions to use and a PIN value for the validation.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 PIN digits 1 & 2 + new CaddxProperty("", 2, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 1", false), + new CaddxProperty("", 2, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 2", false), + + // Byte 3 PIN digits 3 & 4 + new CaddxProperty("", 3, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 3", false), + new CaddxProperty("", 3, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 4", false), + + // Byte 4 PIN digits 5 & 6 + new CaddxProperty("", 4, 1, 0, 4, CaddxPropertyType.BIT, "PIN digit 5 (pad with 0 if 4 digit PIN)", false), + new CaddxProperty("", 4, 1, 4, 4, CaddxPropertyType.BIT, "PIN digit 6 (pad with 0 if 4 digit PIN)", false), + + // Byte 5 Keypad function [00h Turn off any sounder or alarm, 01h Disarm, 02h Arm in away mode, 03h Arm + // in stay mode, 04h Cancel, 05h Initiate auto-arm, 06h Start walk-test mode, 07h Stop walk-test mode, + // 08h-FFh Reserved] + new CaddxProperty("", 5, 1, 0, 0, CaddxPropertyType.INT, + "Keypad function [00h Turn off any sounder or alarm, 01h Disarm, 02h Arm in away mode, 03h Arm in stay mode, 04h Cancel, 05h Initiate auto-arm, 06h Start walk-test mode, 07h Stop walk-test mode, 08h-FFh Reserved]", + false), + + // Byte 6 Partition mask + new CaddxProperty("", 6, 1, 0, 1, CaddxPropertyType.BIT, "Perform on partition 1 (if PIN has access)", + false), + new CaddxProperty("", 6, 1, 1, 1, CaddxPropertyType.BIT, "Perform on partition 2 (if PIN has access)", + false), + new CaddxProperty("", 6, 1, 2, 1, CaddxPropertyType.BIT, "Perform on partition 3 (if PIN has access)", + false), + new CaddxProperty("", 6, 1, 3, 1, CaddxPropertyType.BIT, "Perform on partition 4 (if PIN has access)", + false), + new CaddxProperty("", 6, 1, 4, 1, CaddxPropertyType.BIT, "Perform on partition 5 (if PIN has access)", + false), + new CaddxProperty("", 6, 1, 5, 1, CaddxPropertyType.BIT, "Perform on partition 6 (if PIN has access)", + false), + new CaddxProperty("", 6, 1, 6, 1, CaddxPropertyType.BIT, "Perform on partition 7 (if PIN has access)", + false), + new CaddxProperty("", 6, 1, 7, 1, CaddxPropertyType.BIT, "Perform on partition 8 (if PIN has access)", + false)), + + PRIMARY_KEYPAD_FUNCTION_WITHOUT_PIN(0x3d, new int[] { 0x1d, 0x1c, 0x1f }, 4, "Primary Keypad Function without PIN", + "This message will contain a value that defines with function to perform, the partitions and user number to assign to the function.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 "Keypad function [00h Turn off any sounder or alarm, 01h Disarm, 02h Arm in away mode, 03h Arm + // in stay mode, 04h Cancel, 05h Initiate auto-arm, 06h Start walk-test mode, 07h Stop walk-test mode, + // 08h-FFh Reserved]", + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, + "Keypad function [00h Turn off any sounder or alarm, 01h Disarm, 02h Arm in away mode, 03h Arm in stay mode, 04h Cancel, 05h Initiate auto-arm, 06h Start walk-test mode, 07h Stop walk-test mode, 08h-FFh Reserved]", + false), + + // Byte 3 Partition mask + new CaddxProperty("", 3, 1, 0, 1, CaddxPropertyType.BIT, "Perform on partition 1 (if PIN has access)", + false), + new CaddxProperty("", 3, 1, 1, 1, CaddxPropertyType.BIT, "Perform on partition 2 (if PIN has access)", + false), + new CaddxProperty("", 3, 1, 2, 1, CaddxPropertyType.BIT, "Perform on partition 3 (if PIN has access)", + false), + new CaddxProperty("", 3, 1, 3, 1, CaddxPropertyType.BIT, "Perform on partition 4 (if PIN has access)", + false), + new CaddxProperty("", 3, 1, 4, 1, CaddxPropertyType.BIT, "Perform on partition 5 (if PIN has access)", + false), + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, "Perform on partition 6 (if PIN has access)", + false), + new CaddxProperty("", 3, 1, 6, 1, CaddxPropertyType.BIT, "Perform on partition 7 (if PIN has access)", + false), + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Perform on partition 8 (if PIN has access)", + false), + + // Byte 4 User number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "User number", false)), + + SECONDARY_KEYPAD_FUNCTION(0x3e, new int[] { 0x1d, 0x1c, 0x1f }, 3, "Secondary Keypad Function", + "This message will contain a value that defines with function to perform, and the partitions to use.", + CaddxDirection.OUT, CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 "Keypad function [00h Stay (1 button arm / toggle interiors), 01h Chime (toggle chime mode), + // 02h Exit (1 button arm / toggle instant), 03h Bypass interiors, 04h Fire panic, 05h Medical panic, + // 06h Police panic, 07h Smoke detector reset, 08h Auto callback download, 09h Manual pickup download, + // 0Ah Enable silent exit (for this arm cycle), 0Bh Perform test, 0Ch Group bypass, 0Dh Auxiliary + // function 1, 0Eh Auxiliary function 2, 0Fh Start keypad sounder, 10h-FFh Reserved]", + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, + "Keypad function [00h Stay (1 button arm / toggle interiors), 01h Chime (toggle chime mode), 02h Exit (1 button arm / toggle instant), 03h Bypass interiors, 04h Fire panic, 05h Medical panic, 06h Police panic, 07h Smoke detector reset, 08h Auto callback download, 09h Manual pickup download, 0Ah Enable silent exit (for this arm cycle), 0Bh Perform test, 0Ch Group bypass, 0Dh Auxiliary function 1, 0Eh Auxiliary function 2, 0Fh Start keypad sounder, 10h-FFh Reserved]", + false), + + // Byte 3 Partition mask + new CaddxProperty("", 3, 1, 0, 1, CaddxPropertyType.BIT, "Perform on partition 1", false), + new CaddxProperty("", 3, 1, 1, 1, CaddxPropertyType.BIT, "Perform on partition 2", false), + new CaddxProperty("", 3, 1, 2, 1, CaddxPropertyType.BIT, "Perform on partition 3", false), + new CaddxProperty("", 3, 1, 3, 1, CaddxPropertyType.BIT, "Perform on partition 4", false), + new CaddxProperty("", 3, 1, 4, 1, CaddxPropertyType.BIT, "Perform on partition 5", false), + new CaddxProperty("", 3, 1, 5, 1, CaddxPropertyType.BIT, "Perform on partition 6", false), + new CaddxProperty("", 3, 1, 6, 1, CaddxPropertyType.BIT, "Perform on partition 7", false), + new CaddxProperty("", 3, 1, 7, 1, CaddxPropertyType.BIT, "Perform on partition 8", false)), + + ZONE_BYPASS_TOGGLE(0x3f, new int[] { 0x1d, 0x1c, 0x1f }, 2, "Zone Bypass Toggle", + "This message will contain a number of a zone that should be (un)bypassed.", CaddxDirection.OUT, + CaddxSource.NONE, + + // Properties + // Byte 1 Message number + new CaddxProperty("", 1, 1, 0, 0, CaddxPropertyType.INT, "Message number", false), + + // Byte 2 Zone number (0= zone 1) + new CaddxProperty("", 2, 1, 0, 0, CaddxPropertyType.INT, "Zone number (0= zone 1)", false)); + + public final String name; + public final String description; + public final int number; + public final int @Nullable [] replyMessageNumbers; + public final int length; + public final CaddxDirection direction; + public final CaddxSource source; + public final CaddxProperty[] properties; + + CaddxMessageType(int number, int @Nullable [] replyMessageNumbers, int length, String name, String description, + CaddxDirection direction, CaddxSource source, CaddxProperty... properties) { + this.name = name; + this.description = description; + this.direction = direction; + this.source = source; + this.number = number; + this.replyMessageNumbers = replyMessageNumbers; + this.length = length; + this.properties = properties; + } + + private static final Map BY_MESSAGE_TYPE = new HashMap<>(); + + static { + for (CaddxMessageType mt : values()) { + BY_MESSAGE_TYPE.put(mt.number, mt); + } + } + + public static @Nullable CaddxMessageType valueOfMessageType(int number) { + return BY_MESSAGE_TYPE.get(number); + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxPanelListener.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxPanelListener.java new file mode 100644 index 0000000000000..516cefad803f1 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxPanelListener.java @@ -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); +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxProperty.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxProperty.java new file mode 100644 index 0000000000000..06f57f1bc11f6 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxProperty.java @@ -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(); + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxPropertyType.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxPropertyType.java new file mode 100644 index 0000000000000..bdb3596ad106d --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxPropertyType.java @@ -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 +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxProtocol.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxProtocol.java new file mode 100644 index 0000000000000..18ab7787eb692 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxProtocol.java @@ -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 +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxSource.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxSource.java new file mode 100644 index 0000000000000..20be604cf8d06 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/CaddxSource.java @@ -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 +}; diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxBridgeConfiguration.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxBridgeConfiguration.java new file mode 100644 index 0000000000000..e50f0a64f433c --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxBridgeConfiguration.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxKeypadConfiguration.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxKeypadConfiguration.java new file mode 100644 index 0000000000000..807efcc8257af --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxKeypadConfiguration.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxPartitionConfiguration.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxPartitionConfiguration.java new file mode 100644 index 0000000000000..8bd743f4dac89 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxPartitionConfiguration.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxZoneConfiguration.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxZoneConfiguration.java new file mode 100644 index 0000000000000..00ad1a1e6a7ef --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/config/CaddxZoneConfiguration.java @@ -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; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/discovery/CaddxDiscoveryService.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/discovery/CaddxDiscoveryService.java new file mode 100644 index 0000000000000..201cb1894c4b5 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/discovery/CaddxDiscoveryService.java @@ -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.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +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.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 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; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/factory/CaddxHandlerFactory.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/factory/CaddxHandlerFactory.java new file mode 100644 index 0000000000000..4964159aac4ed --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/factory/CaddxHandlerFactory.java @@ -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.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; +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.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; + } + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBaseThingHandler.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBaseThingHandler.java new file mode 100644 index 0000000000000..84c8e5ea51308 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBaseThingHandler.java @@ -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.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.types.Command; +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.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 channels = getThing().getChannels(); + + for (Channel ch : channels) { + if (channelUID == ch.getUID()) { + channel = ch; + break; + } + } + + return channel; + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBridgeHandler.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBridgeHandler.java new file mode 100644 index 0000000000000..b778fcf844ca2 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxBridgeHandler.java @@ -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.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.io.transport.serial.PortInUseException; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; +import org.eclipse.smarthome.io.transport.serial.UnsupportedCommOperationException; +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.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 thingZonesMap = new ConcurrentHashMap<>(); + private Map thingPartitionsMap = new ConcurrentHashMap<>(); + private Map 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> 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); + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxThingType.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxThingType.java new file mode 100644 index 0000000000000..9bc820103d737 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/CaddxThingType.java @@ -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; +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventMessage.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventMessage.java new file mode 100644 index 0000000000000..d24be620b04c4 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventMessage.java @@ -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"; + } + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventType.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventType.java new file mode 100644 index 0000000000000..8cfb457a70c69 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/LogEventType.java @@ -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 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); + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerKeypad.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerKeypad.java new file mode 100644 index 0000000000000..c76ae16d03519 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerKeypad.java @@ -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.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.caddx.internal.CaddxEvent; + +/** + * 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); + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPanel.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPanel.java new file mode 100644 index 0000000000000..964766b7d5354 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPanel.java @@ -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.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +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.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 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 map = new HashMap(); + 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 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 map = new HashMap(); + + 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; + } + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPartition.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPartition.java new file mode 100644 index 0000000000000..5a9ec37de49f0 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerPartition.java @@ -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.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +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.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); + } + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerZone.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerZone.java new file mode 100644 index 0000000000000..32580811fcf8c --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ThingHandlerZone.java @@ -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.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +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.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); + } + } +} diff --git a/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ZoneUserDevice.java b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ZoneUserDevice.java new file mode 100644 index 0000000000000..83539634df35c --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/java/org/openhab/binding/caddx/internal/handler/ZoneUserDevice.java @@ -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 +} diff --git a/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..27713623935ee --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + Caddx Security Binding + Binding for Caddx security system with RS232 serial interface. + Georgios Moutsos + diff --git a/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/bridge.xml new file mode 100644 index 0000000000000..96e4d779165b4 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/bridge.xml @@ -0,0 +1,60 @@ + + + + + + This bridge represents the Caddx Serial interface. + + + + + Sends an Alarm Panel Command + + + + + + serial-port + + The serial port name for the communication. Valid values + are e.g. COM1 for Windows and /dev/ttyS0 or + /dev/ttyUSB0 for Linux. + + + + + 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. + 9600 + + + + + + + + + + + + + + protocol + + The configured panel protocol. Valid values + are Binary and Ascii. + Binary + + + + + + + + + + diff --git a/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/channels.xml new file mode 100644 index 0000000000000..86d06e2e13ed2 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/channels.xml @@ -0,0 +1,126 @@ + + + + + + Switch + + Reset Switch + + + + String + + Sends a Command + + + + + String + + Panel text + + + + + Switch + + Panel flag + + + + + + Switch + + Partition Condition + + + + + Number + + Partition secondary command + + + + + + + + + + + + + + + + + + + + + + + + + + String + + Zone text + + + + + Switch + + Zone Partition + + + + + Switch + + Zone Type + + + + + Switch + + Zone Condition + + + + + Contact + + Zone Status (Open/Closed) + + + + + Switch + + Bypass Mode (OFF=Armed, ON=Bypassed) + + + + + Number + + Keypad Led (0=Off, 1=On, 2=Flashing) + + + + + + + + + + diff --git a/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/keypad.xml b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/keypad.xml new file mode 100644 index 0000000000000..b339f196825c9 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/keypad.xml @@ -0,0 +1,24 @@ + + + + + + + + + + Represents any of the keypads of the Caddx Alarm System. + + keypadAddress + + + + + The Keypad Address. + + + + diff --git a/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/panel.xml b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/panel.xml new file mode 100644 index 0000000000000..4cce0123e59a0 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/panel.xml @@ -0,0 +1,206 @@ + + + + + + + + + + The basic representation of the Caddx Alarm System. + + + + + Firmware version + + + + + Log message 10 + + + + Log message 9 + + + + Log message 8 + + + + Log message 7 + + + + Log message 6 + + + + Log message 5 + + + + Log message 4 + + + + Log message 3 + + + + Log message 2 + + + + Log message 1 + + + + + Interface Configuration Message + + + + Zone Status Message + + + + Zones Snapshot Message + + + + Partition Status Message + + + + Partitions Snapshot Message + + + + + System Status Message + + + + X-10 Message Received + + + + Log Event Message + + + + Keypad Message Received + + + + + Interface Configuration Request + + + + Zone Name Request + + + + Zone Status Request + + + + Zones Snapshot Request + + + + Partition Status Request + + + + Partitions Snapshot Request + + + + + System Status Request + + + + Send X-10 Message + + + + Log Event Request + + + + Send Keypad Text Message + + + + Keypad Terminal Mode Request + + + + + Program Data Request + + + + Program Data Command + + + + User Information Request with PIN + + + + User Information Request without PIN + + + + Set User Code Command with PIN + + + + Set User Code Command without PIN + + + + Set User Authorization Command with PIN + + + + Set User Authorization Command without PIN + + + + + Store Communication Event Command + + + + Set Clock / Calendar Command + + + + Primary Keypad Function with PIN + + + + Primary Keypad Function without PIN + + + + Secondary Keypad Function + + + + Zone Bypass Toggle + + + + + diff --git a/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/partition.xml b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/partition.xml new file mode 100644 index 0000000000000..b91e60518d557 --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/partition.xml @@ -0,0 +1,224 @@ + + + + + + + + + + Represents a controllable area within a Caddx Alarm System. + + + + + Bypass code required + + + + Fire trouble + + + + Fire + + + + Pulsing Buzzer + + + + TLM fault memory + + + + Armed + + + + Instant + + + + + Previous Alarm + + + + Siren on + + + + Steady siren on + + + + Alarm memory + + + + Tamper + + + + Cancel command entered + + + + Code entered + + + + Cancel pending + + + + + Silent exit enabled + + + + Entryguard (stay mode) + + + + Chime mode on + + + + Entry + + + + Delay expiration warning + + + + Exit1 + + + + Exit2 + + + + + Led extinguish + + + + Cross timing + + + + Recent closing being timed + + + + Exit error triggered + + + + Auto home inhibited + + + + Sensor low battery + + + + Sensor lost supervision + + + + + Zone bypassed + + + + Force arm triggered by auto arm + + + + Ready to arm + + + + Ready to force arm + + + + Valid PIN accepted + + + + Chime on (sounding) + + + + Error beep (triple beep) + + + + Tone on (activation tone) + + + + + Entry 1 + + + + Open period + + + + Alarm sent using phone number 1 + + + + Alarm sent using phone number 2 + + + + Alarm sent using phone number 3 + + + + Cancel report is in the stack + + + + Keyswitch armed + + + + Delay Trip in progress (common zone) + + + + + Partition Secondary Command + + + + partitionNumber + + + + + The Partition Number. + 1 + + + + The User Number. + 1 + + + + + diff --git a/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/zone.xml b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/zone.xml new file mode 100644 index 0000000000000..a83f024e1b84d --- /dev/null +++ b/bundles/org.openhab.binding.caddx/src/main/resources/ESH-INF/thing/zone.xml @@ -0,0 +1,206 @@ + + + + + + + + + + Represents a physical device such as a door, window, or motion sensor. + + + + + + Partition 1 + + + + Partition 2 + + + + Partition 3 + + + + Partition 4 + + + + Partition 5 + + + + Partition 6 + + + + Partition 7 + + + + Partition 8 + + + + + + Name + + + + + + Fire + + + + 24 Hour + + + + Key-switch + + + + Follower + + + + Entry / exit delay 1 + + + + Entry / exit delay 2 + + + + Interior + + + + Local only + + + + + Keypad Sounder + + + + Yelping siren + + + + Steady siren + + + + Chime + + + + Bypassable + + + + Group bypassable + + + + Force armable + + + + Entry guard + + + + + Fast loop response + + + + Double EOL tamper + + + + Trouble + + + + Cross zone + + + + Dialer delay + + + + Swinger shutdown + + + + Restorable + + + + Listen in + + + + + + Faulted (or delayed trip) + + + + Tampered + + + + Trouble + + + + Bypassed + + + + Inhibited (force armed) + + + + Low Battery + + + + Loss of supervision + + + + Alarm memory + + + + Bypass memory + + + + zoneNumber + + + + + The Zone Number. + + + + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 6586283452a17..44fe1e1f6002b 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -65,6 +65,7 @@ org.openhab.binding.bsblan org.openhab.binding.bticinosmarther org.openhab.binding.buienradar + org.openhab.binding.caddx org.openhab.binding.cbus org.openhab.binding.chromecast org.openhab.binding.cm11a