diff --git a/CODEOWNERS b/CODEOWNERS index 1d0ebb2377e..2cd894f9638 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -272,6 +272,7 @@ /bundles/org.openhab.binding.pushover/ @cweitkamp /bundles/org.openhab.binding.pushsafer/ @appzer @cweitkamp /bundles/org.openhab.binding.qbus/ @QbusKoen +/bundles/org.openhab.binding.qolsysiq/ @digitaldan /bundles/org.openhab.binding.radiothermostat/ @mlobstein /bundles/org.openhab.binding.regoheatpump/ @crnjan /bundles/org.openhab.binding.revogi/ @andibraeu diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index d5178fa3bc0..eb7f0257249 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1356,6 +1356,11 @@ org.openhab.binding.qbus ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.qolsysiq + ${project.version} + org.openhab.addons.bundles org.openhab.binding.radiothermostat diff --git a/bundles/org.openhab.binding.qolsysiq/NOTICE b/bundles/org.openhab.binding.qolsysiq/NOTICE new file mode 100644 index 00000000000..38d625e3492 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/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/openhab-addons diff --git a/bundles/org.openhab.binding.qolsysiq/README.md b/bundles/org.openhab.binding.qolsysiq/README.md new file mode 100644 index 00000000000..7be0e7fe23e --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/README.md @@ -0,0 +1,125 @@ +# Qolsys IQ Binding + +This binding directly controls a [Qolsys IQ](https://qolsys.com/security/) security panel. +This allows for local monitoring of alarm and zone statuses as well as arming, disarming and triggering alarms. + +Qolsys (a division of Johnson Controls) is a popular manufacturer of alarm systems. +The Qolsys IQ line of panels supports both wireless and hard wire sensors and features built in Cellular and Wi-Fi dual path communication that natively integrates with Alarm.com monitoring and supervision. + +This binding directly interfaces with the panel and does not require cloud access. + +![Qolsys IQ 4](doc/qolsysiq4.png) + +## Supported Things + +| Thing | Description | Thing Type | Thing UID | +|---------------------|-------------------------------------------------------------------------------------------|------------|-----------| +| Qolsys IQ Panel | A Qolsys IQ security panel (all current models, which is 2+ and 4 at the time of writing) | Bridge | panel | +| Qolsys IQ Partition | A logical partition which can be armed, disarmed, and is responsible for managing zones | Bridge | partition | +| Qolsys IQ Zone | A generic zone sensor | Thing | zone | + +## Discovery + +### Qolsys IQ Panel (Bridge) + +The Qolsys IQ Panel must be manually added using a host name or ip address along with a secure access token from the panel settings. +To enable 3rd party control and retrieve the access token follow the following steps on the security panel touch screen: + +`Settings` --> `Advanced Settings` --> `Installation` --> `Dealer Settings` -> `6 Digit User Code` (set to enabled) + +`Settings` --> `Advanced Settings` --> `Installation` --> `Devices` --> `Wi-Fi Devices` --> `Control4` (set to enabled) + + *Panel will reboot* + +`Settings` --> `Advanced Settings` --> `Installation` --> `Devices` --> `Wi-Fi Devices` --> `Reveal Secure Token` (copy token to use in panel configuration) + +At this point you may add the panel thing in openHAB using the secure token along with the IP or host name of the panel. + +### Partition (Bridge) + +Once a panel is added, partitions will be automatically discovered and appear in the inbox. + +### Zone (Thing) + +Once a partition is added, zones will be automatically discovered and appear in the inbox. + +## Thing Configuration + +### `panel` Thing Configuration + +| Name | Type | Description | Default | Required | Advanced | +|-------------------|---------|-----------------------------------------------------|---------|----------|----------| +| hostname | text | Hostname or IP address of the device | N/A | yes | no | +| port | integer | Port the device is listening on | 12345 | no | no | +| key | text | Access token / key found in the panel settings menu | N/A | yes | no | + +### `partition` Thing Configuration + +| Name | Type | Description | Default | Required | Advanced | +|------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|----------|----------| +| id | integer | Partition id of the panel, staring with '0' for the first partition | N/A | yes | no | +| disarmCode | text | Optional disarm code to use when receiving a disarm command without a code. Required for integrations like Alexa and Homekit who do not provide codes when disarming. Leave blank to always require a code | blank | no | no | +| armCode | text | Optional arm code to use when receiving arm commands without a code. Only required if the panel has been configured to require arm codes. Leave blank to always require a code | blank | no | yes | + +### `zone` Thing Configuration + +| Name | Type | Description | Default | Required | Advanced | +|---------|---------|---------------------------------------------------------------------------------------------------------|---------|----------|----------| +| id | integer | Id of the zone, staring with '1' for the first zone | N/A | yes | no | + +## Channels + +### Panel Channels + +None. + +### Partition Channels + +| Channel | Type | Read/Write | Description | State Options | Command Options | +|-------------|--------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|----------------------------| +| armState | String | RW | Reports the current partition arm state or sends an arm or disarm command to the system. Security codes can be appended to the command using a colon delimiter (e.g. 'DISARM:123456'). Codes appended to the command will be used in place of the `armCode` configuration property if set. | ALARM, ARM_AWAY, ARM_STAY, DISARM, ENTRY_DELAY, EXIT_DELAY | ARM_AWAY, ARM_STAY, DISARM | +| alarmState | String | RW | Reports on the current alarm state, or triggers an instant alarm | AUXILIARY, FIRE, POLICE, ZONEOPEN, NONE | AUXILIARY, FIRE, POLICE | +| armingDelay | Number | R | The arming delay countdown currently in progress | Seconds remaining | N/A | +| errorEvent | String | R | Last error event message reported by the partition. Clears after 30 seconds | Error text | N/A | + +### Zone Channels + +| Channel | Type | Read/Write | Description | State Options | +|---------|---------|------------|------------------------|---------------------------------------------| +| status | String | R | The zone status | ACTIVE, CLOSED, OPEN, FAILURE, IDLE, TAMPER | +| state | Number | R | The zone state | Number | +| contact | Contact | R | The zone contact state | OPEN, CLOSED | + +## Full Example + +### qolsysiq.things + +``` +Bridge qolsysiq:panel:home "Home Security Panel" [ hostname="192.168.3.123", port=12345, key="AAABBB00" ] { + Bridge partition 0 "Partition Main" [ id=0, armCode="123456" ] { + Thing zone 1 "Window" [ id=1 ] + Thing zone 2 "Motion" [ id=2 ] + } +} +``` + +### qolsysiq.items + +Sample items file with both Alexa and Homekit voice control + +``` +Group PartitionMain "Alarm System" ["Equipment"] {alexa="SecurityPanel", homekit = "SecuritySystem"} +String PartitionMain_PartitionArmState "Partition Arm State" (PartitionMain) ["Point"] {channel="qolsysiq:partition:home:0:armState", alexa="ArmState" [DISARMED="DISARM",ARMED_STAY="ARM_STAY",ARMED_AWAY="ARM_AWAY:EXIT_DELAY"], homekit = "SecuritySystem.CurrentSecuritySystemState,SecuritySystem.TargetSecuritySystemState" [STAY_ARM="ARM_STAY", AWAY_ARM="ARM_AWAY", DISARM="DISARM", DISARMED="DISARM", TRIGGERED="ALARM"]} +String PartitionMain_PartitionAlarmState "Partition Alarm State" (PartitionMain) ["Point"] {channel="qolsysiq:partition:home:0:alarmState"} +Number PartitionMain_PartitionArmingDelay "Partition Arming Delay" (PartitionMain) ["Point"] {channel="qolsysiq:partition:home:0:armingDelay"} +String PartitionMain_ErrorEvent "Error Event" (PartitionMain) ["Point"] {channel="qolsysiq:partition:home:0:errorEvent" } + +Group ZoneKitchenWindows "Qolsys IQ Zone: Kitchen Windows" ["Equipment"] +Number ZoneKitchenWindows_ZoneState "Kitchen Windows Zone State" (ZoneKitchenWindows) ["Point"] {channel="qolsysiq:zone:home:0:1:state"} +String ZoneKitchenWindows_ZoneStatus "Kitchen Windows Zone Status" (ZoneKitchenWindows) ["Point"] {channel="qolsysiq:zone:home:0:1:status"} +Contact ZoneKitchenWindows_ZoneContact "Kitchen Windows Zone Contact" (ZoneKitchenWindows) ["Point"] {channel="qolsysiq:zone:home:0:1:contact"} + +Group ZoneMotionDetector1 "Motion Detector 1" ["Equipment"] +Number ZoneMotionDetector_ZoneState1 "Motion Detector 1 Zone State" (ZoneMotionDetector1) ["Point"] {channel="qolsysiq:zone:home:0:2:state"} +String ZoneMotionDetector_ZoneStatus1 "Motion Detector 1 Zone Status" (ZoneMotionDetector1) ["Point"] {channel="qolsysiq:zone:home:0:2:status"} +``` diff --git a/bundles/org.openhab.binding.qolsysiq/doc/qolsysiq4.png b/bundles/org.openhab.binding.qolsysiq/doc/qolsysiq4.png new file mode 100644 index 00000000000..35fbb25c1d7 Binary files /dev/null and b/bundles/org.openhab.binding.qolsysiq/doc/qolsysiq4.png differ diff --git a/bundles/org.openhab.binding.qolsysiq/pom.xml b/bundles/org.openhab.binding.qolsysiq/pom.xml new file mode 100644 index 00000000000..91c7b73da3b --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.4.0-SNAPSHOT + + + org.openhab.binding.qolsysiq + + openHAB Add-ons :: Bundles :: QolsysIQ Binding + + diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/feature/feature.xml b/bundles/org.openhab.binding.qolsysiq/src/main/feature/feature.xml new file mode 100644 index 00000000000..b02bdd6f50b --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.qolsysiq/${project.version} + + diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/QolsysIQBindingConstants.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/QolsysIQBindingConstants.java new file mode 100644 index 00000000000..4028893edb6 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/QolsysIQBindingConstants.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link QolsysIQBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysIQBindingConstants { + + public static final String BINDING_ID = "qolsysiq"; + + public static final ThingTypeUID THING_TYPE_PANEL = new ThingTypeUID(BINDING_ID, "panel"); + public static final ThingTypeUID THING_TYPE_PARTITION = new ThingTypeUID(BINDING_ID, "partition"); + public static final ThingTypeUID THING_TYPE_ZONE = new ThingTypeUID(BINDING_ID, "zone"); + + public static final String CHANNEL_PARTITION_ARM_STATE = "armState"; + public static final String CHANNEL_PARTITION_ALARM_STATE = "alarmState"; + public static final String CHANNEL_PARTITION_COMMAND_DELAY = "armingDelay"; + public static final String CHANNEL_PARTITION_ERROR_EVENT = "errorEvent"; + + public static final String CHANNEL_ZONE_STATE = "state"; + public static final String CHANNEL_ZONE_STATUS = "status"; + public static final String CHANNEL_ZONE_CONTACT = "contact"; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/QolsysIQHandlerFactory.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/QolsysIQHandlerFactory.java new file mode 100644 index 00000000000..ae1edf8b30b --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/QolsysIQHandlerFactory.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal; + +import static org.openhab.binding.qolsysiq.internal.QolsysIQBindingConstants.*; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.qolsysiq.internal.handler.QolsysIQPanelHandler; +import org.openhab.binding.qolsysiq.internal.handler.QolsysIQPartitionHandler; +import org.openhab.binding.qolsysiq.internal.handler.QolsysIQZoneHandler; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link QolsysIQHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.qolsysiq", service = ThingHandlerFactory.class) +public class QolsysIQHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_PANEL, THING_TYPE_PARTITION, + THING_TYPE_ZONE); + + @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 (THING_TYPE_PANEL.equals(thingTypeUID)) { + return new QolsysIQPanelHandler((Bridge) thing); + } + + if (THING_TYPE_PARTITION.equals(thingTypeUID)) { + return new QolsysIQPartitionHandler((Bridge) thing); + } + + if (THING_TYPE_ZONE.equals(thingTypeUID)) { + return new QolsysIQZoneHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/QolsysIQClientListener.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/QolsysIQClientListener.java new file mode 100644 index 00000000000..61e7566efed --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/QolsysIQClientListener.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.qolsysiq.internal.client.dto.event.AlarmEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ArmingEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ErrorEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.SecureArmInfoEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.SummaryInfoEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneActiveEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneAddEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneUpdateEvent; + +/** + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public interface QolsysIQClientListener { + /** + * Callback when the connection has been disconnected + * + * @param reason + */ + void disconnected(Exception reason); + + /** + * {@link AlarmEvent} message callback + * + * @param event + */ + void alarmEvent(AlarmEvent event); + + /** + * {@link ArmingEvent} message callback + * + * @param event + */ + void armingEvent(ArmingEvent event); + + /** + * {@link ErrorEvent} message callback + * + * @param event + */ + void errorEvent(ErrorEvent event); + + /** + * {@link SummaryInfoEvent} message callback + * + * @param event + */ + void summaryInfoEvent(SummaryInfoEvent event); + + /** + * {@link SecureArmInfoEvent} message callback + * + * @param event + */ + void secureArmInfoEvent(SecureArmInfoEvent event); + + /** + * {@link ZoneActiveEvent} message callback + * + * @param event + */ + void zoneActiveEvent(ZoneActiveEvent event); + + /** + * {@link ZoneUpdateEvent} message callback + * + * @param event + */ + void zoneUpdateEvent(ZoneUpdateEvent event); + + /** + * {@link ZoneAddEvent} message callback + * + * @param event + */ + void zoneAddEvent(ZoneAddEvent event); +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/QolsysiqClient.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/QolsysiqClient.java new file mode 100644 index 00000000000..ae738962845 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/QolsysiqClient.java @@ -0,0 +1,390 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.lang.reflect.Type; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.qolsysiq.internal.client.dto.action.Action; +import org.openhab.binding.qolsysiq.internal.client.dto.event.AlarmEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ArmingEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ErrorEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.Event; +import org.openhab.binding.qolsysiq.internal.client.dto.event.EventType; +import org.openhab.binding.qolsysiq.internal.client.dto.event.InfoEventType; +import org.openhab.binding.qolsysiq.internal.client.dto.event.SecureArmInfoEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.SummaryInfoEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneActiveEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneAddEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneEventType; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneUpdateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; + +/** + * A client that can communicate with a Qolsys IQ Panel + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysiqClient { + private static final String MESSAGE_ACK = "ACK"; + private final Logger logger = LoggerFactory.getLogger(QolsysiqClient.class); + private final Gson gson = new GsonBuilder().registerTypeAdapter(Event.class, new EventDeserializer()) + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + private List listeners = Collections.synchronizedList(new ArrayList<>()); + private @Nullable SSLSocket socket; + private @Nullable BufferedReader reader; + private @Nullable BufferedWriter writer; + private @Nullable Thread readerThread; + private @Nullable ScheduledFuture heartBeatFuture; + private ScheduledExecutorService scheduler; + private Object writeLock = new Object(); + private long lastResponseTime; + private boolean hasACK = false; + private boolean connected; + private String host; + private int port; + private int heartbeatSeconds; + private String threadName; + private SSLSocketFactory sslsocketfactory; + + /** + * Creates a new QolsysiqClient + * + * @param host + * @param port + * @param heartbeatSeconds + * @param scheduler for the heart beat task + * @param threadName + */ + public QolsysiqClient(String host, int port, int heartbeatSeconds, ScheduledExecutorService scheduler, + String threadName) throws IOException { + this.host = host; + this.port = port; + this.heartbeatSeconds = heartbeatSeconds; + this.scheduler = scheduler; + this.threadName = threadName; + + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, acceptAlltrustManagers(), null); + sslsocketfactory = sslContext.getSocketFactory(); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + throw new IOException(e); + } + } + + /** + * Connects to the panel + * + * @throws IOException + */ + public synchronized void connect() throws IOException { + logger.debug("connect"); + if (connected) { + logger.debug("connect: already connected, ignoring"); + return; + } + + SSLSocket socket = (SSLSocket) sslsocketfactory.createSocket(host, port); + socket.startHandshake(); + writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + this.socket = socket; + + Thread readerThread = new Thread(this::readEvents, threadName); + readerThread.setDaemon(true); + readerThread.start(); + this.readerThread = readerThread; + connected = true; + try { + // send an initial message to confirm a connection and record a response time + writeMessage(""); + } catch (IOException e) { + // clean up before bubbling up exception + disconnect(); + throw e; + } + heartBeatFuture = scheduler.scheduleWithFixedDelay(() -> { + if (connected) { + try { + if (System.currentTimeMillis() - lastResponseTime > (heartbeatSeconds + 5) * 1000) { + throw new IOException("No responses received"); + } + writeMessage(""); + } catch (IOException e) { + logger.debug("Problem sending heartbeat", e); + disconnectAndNotify(e); + } + } + }, heartbeatSeconds, heartbeatSeconds, TimeUnit.SECONDS); + } + + /** + * Disconnects from the panel + */ + public void disconnect() { + connected = false; + + ScheduledFuture heartbeatFuture = this.heartBeatFuture; + if (heartbeatFuture != null) { + heartbeatFuture.cancel(true); + } + + Thread readerThread = this.readerThread; + if (readerThread != null && readerThread.isAlive()) { + readerThread.interrupt(); + } + + SSLSocket socket = this.socket; + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + logger.debug("Error closing SSL socket: {}", e.getMessage()); + } + this.socket = null; + } + BufferedReader reader = this.reader; + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + logger.debug("Error closing reader: {}", e.getMessage()); + } + this.reader = null; + } + BufferedWriter writer = this.writer; + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + logger.debug("Error closing writer: {}", e.getMessage()); + } + this.writer = null; + } + } + + /** + * Sends an Action message to the panel + * + * @param action + * @throws IOException + */ + public void sendAction(Action action) throws IOException { + logger.debug("sendAction {}", action.type); + writeMessage(gson.toJson(action)); + } + + /** + * Adds a QolsysIQClientListener + * + * @param listener + */ + public void addListener(QolsysIQClientListener listener) { + synchronized (listeners) { + listeners.add(listener); + } + } + + /** + * Removes a QolsysIQClientListener + * + * @param listener + */ + public void removeListener(QolsysIQClientListener listener) { + synchronized (listeners) { + listeners.remove(listener); + } + } + + private synchronized void writeMessage(String message) throws IOException { + if (!connected) { + logger.debug("writeMessage: not connected, ignoring {}", message); + return; + } + synchronized (writeLock) { + hasACK = false; + logger.trace("writeMessage: {}", message); + BufferedWriter writer = this.writer; + if (writer != null) { + writer.write(message); + writer.newLine(); + writer.flush(); + try { + writeLock.wait(5000); + } catch (InterruptedException e) { + logger.debug("write lock interupted"); + } + if (!hasACK) { + logger.trace("writeMessage: no ACK for {}", message); + throw new IOException("No response to message: " + message); + } + } + } + } + + private void readEvents() { + String message; + BufferedReader reader = this.reader; + try { + while (connected && reader != null && (message = reader.readLine()) != null) { + logger.trace("Message: {}", message); + lastResponseTime = System.currentTimeMillis(); + if (MESSAGE_ACK.equals(message)) { + synchronized (writeLock) { + hasACK = true; + writeLock.notify(); + } + continue; + } + try { + Event event = gson.fromJson(message, Event.class); + if (event == null) { + logger.debug("Could not deserialize message: {}", message); + continue; + } + synchronized (listeners) { + if (event instanceof AlarmEvent) { + listeners.forEach(listener -> listener.alarmEvent((AlarmEvent) event)); + } else if (event instanceof ArmingEvent) { + listeners.forEach(listener -> listener.armingEvent((ArmingEvent) event)); + } else if (event instanceof ErrorEvent) { + listeners.forEach(listener -> listener.errorEvent((ErrorEvent) event)); + } else if (event instanceof SecureArmInfoEvent) { + listeners.forEach(listener -> listener.secureArmInfoEvent((SecureArmInfoEvent) event)); + } else if (event instanceof SummaryInfoEvent) { + listeners.forEach(listener -> listener.summaryInfoEvent((SummaryInfoEvent) event)); + } else if (event instanceof ZoneActiveEvent) { + listeners.forEach(listener -> listener.zoneActiveEvent((ZoneActiveEvent) event)); + } else if (event instanceof ZoneUpdateEvent) { + listeners.forEach(listener -> listener.zoneUpdateEvent((ZoneUpdateEvent) event)); + } else if (event instanceof ZoneAddEvent) { + listeners.forEach(listener -> listener.zoneAddEvent((ZoneAddEvent) event)); + } + } + } catch (JsonSyntaxException e) { + logger.debug("Could not parse messge", e); + } + } + if (connected) { + throw new IOException("socket disconencted"); + } + } catch (IOException e) { + disconnectAndNotify(e); + } + } + + private void disconnectAndNotify(Exception e) { + if (connected) { + disconnect(); + synchronized (listeners) { + listeners.forEach(listener -> listener.disconnected(e)); + } + } + } + + private TrustManager[] acceptAlltrustManagers() { + return new TrustManager[] { new X509TrustManager() { + @Override + public void checkClientTrusted(final X509Certificate @Nullable [] chain, final @Nullable String authType) { + } + + @Override + public void checkServerTrusted(final X509Certificate @Nullable [] chain, final @Nullable String authType) { + } + + @Override + public X509Certificate @Nullable [] getAcceptedIssuers() { + return null; + } + } }; + } + + class EventDeserializer implements JsonDeserializer { + @Override + public @Nullable Event deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + JsonElement event = jsonObject.get("event"); + if (event != null) { + switch (EventType.valueOf(event.getAsString())) { + case ALARM: + return context.deserialize(jsonObject, AlarmEvent.class); + case ARMING: + return context.deserialize(jsonObject, ArmingEvent.class); + case ERROR: + return context.deserialize(jsonObject, ErrorEvent.class); + case INFO: + JsonElement infoType = jsonObject.get("info_type"); + if (infoType != null) { + switch (InfoEventType.valueOf(infoType.getAsString())) { + case SECURE_ARM: + return context.deserialize(jsonObject, SecureArmInfoEvent.class); + case SUMMARY: + return context.deserialize(jsonObject, SummaryInfoEvent.class); + } + } + break; + case ZONE_EVENT: + JsonElement zoneEventType = jsonObject.get("zone_event_type"); + if (zoneEventType != null) { + switch (ZoneEventType.valueOf(zoneEventType.getAsString())) { + case ZONE_ACTIVE: + return context.deserialize(jsonObject, ZoneActiveEvent.class); + case ZONE_UPDATE: + return context.deserialize(jsonObject, ZoneUpdateEvent.class); + case ZONE_ADD: + return context.deserialize(jsonObject, ZoneAddEvent.class); + default: + break; + } + } + } + } + return null; + } + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/Action.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/Action.java new file mode 100644 index 00000000000..2f7142798b8 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/Action.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +import com.google.gson.annotations.SerializedName; + +/** + * The base type for various action messages sent to a panel + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class Action { + @SerializedName("action") + public ActionType type; + public Integer version = 0; + public String source = "C4"; + public String token; + + public Action(ActionType type) { + this(type, ""); + } + + public Action(ActionType type, String token) { + this.type = type; + this.token = token; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ActionType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ActionType.java new file mode 100644 index 00000000000..af9184ac29a --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ActionType.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * The type of {@link Action} sent to a panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum ActionType { + ALARM, + ARMING, + INFO +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/AlarmAction.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/AlarmAction.java new file mode 100644 index 00000000000..b2b6e194054 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/AlarmAction.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * An {@link ActionType.ALARM} type of {@link Action} message sent to the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class AlarmAction extends Action { + public AlarmActionType alarmType; + + public AlarmAction(AlarmActionType alarmType) { + this(alarmType, ""); + } + + public AlarmAction(AlarmActionType alarmType, String token) { + super(ActionType.ALARM, token); + this.alarmType = alarmType; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/AlarmActionType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/AlarmActionType.java new file mode 100644 index 00000000000..dc8c57721a8 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/AlarmActionType.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * The type of {@link AlarmAction} sent to a panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum AlarmActionType { + AUXILIARY, + FIRE, + POLCIE +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmAwayArmingAction.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmAwayArmingAction.java new file mode 100644 index 00000000000..3149b577aaa --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmAwayArmingAction.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * An {@link ArmingActionType.ARM_AWAY} type of {@link ArmingAction} message sent to the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class ArmAwayArmingAction extends ArmingAction { + public Integer delay; + + public ArmAwayArmingAction(String token, Integer partitionId, Integer delay) { + super(ArmingActionType.ARM_AWAY, token, partitionId); + this.delay = delay; + } + + public ArmAwayArmingAction(String token, Integer partitionId) { + this(token, partitionId, null); + } + + public ArmAwayArmingAction(Integer partitionId) { + this("", partitionId, null); + } + + public ArmAwayArmingAction(Integer partitionId, Integer delay) { + this("", partitionId, delay); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmingAction.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmingAction.java new file mode 100644 index 00000000000..fdf0bb88b6d --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmingAction.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * An {@link ActionType.ARMING} type of {@link ArmingAction} message sent to the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class ArmingAction extends Action { + public ArmingActionType armingType; + public Integer partitionId; + public String usercode; + + public ArmingAction(ArmingActionType armingType, Integer partitionId) { + this(armingType, "", partitionId, null); + } + + public ArmingAction(ArmingActionType armingType, Integer partitionId, String usercode) { + this(armingType, "", partitionId, usercode); + } + + public ArmingAction(ArmingActionType armingType, String token, Integer partitionId) { + this(armingType, token, partitionId, null); + } + + public ArmingAction(ArmingActionType armingType, String token, Integer partitionId, String usercode) { + super(ActionType.ARMING, token); + this.armingType = armingType; + this.partitionId = partitionId; + this.usercode = usercode; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmingActionType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmingActionType.java new file mode 100644 index 00000000000..9951b68184d --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/ArmingActionType.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * The type of {@link ArmingAction} sent to a panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum ArmingActionType { + ARM_AWAY, + ARM_STAY, + DISARM; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/InfoAction.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/InfoAction.java new file mode 100644 index 00000000000..4d1e6a5ad0b --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/InfoAction.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * An {@link ActionType.INFO} type of {@link InfoAction} message sent to the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class InfoAction extends Action { + public InfoActionType infoType; + + public InfoAction(InfoActionType infoType) { + this(infoType, ""); + } + + public InfoAction(InfoActionType infoType, String token) { + super(ActionType.INFO, token); + this.infoType = infoType; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/InfoActionType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/InfoActionType.java new file mode 100644 index 00000000000..a62a7a39119 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/action/InfoActionType.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.action; + +/** + * The type of {@link InfoAction} sent to a panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum InfoActionType { + SUMMARY, + SECURE_ARM +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/AlarmEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/AlarmEvent.java new file mode 100644 index 00000000000..0570e8dcc43 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/AlarmEvent.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import org.openhab.binding.qolsysiq.internal.client.dto.model.AlarmType; + +/** + * An {@link EventType.ALARM} type of {@link Event} message sent from the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class AlarmEvent extends Event { + public AlarmType alarmType; + public Integer partitionId; + + public AlarmEvent() { + super(EventType.ALARM); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ArmingEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ArmingEvent.java new file mode 100644 index 00000000000..42bc3c0af26 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ArmingEvent.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import org.openhab.binding.qolsysiq.internal.client.dto.model.PartitionStatus; + +/** + * An {@link EventType.ARMING} type of {@link Event} message sent from the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class ArmingEvent extends Event { + public PartitionStatus armingType; + public Integer partitionId; + public Integer delay; + + public ArmingEvent() { + super(EventType.ARMING); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ErrorEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ErrorEvent.java new file mode 100644 index 00000000000..1c04abf9e97 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ErrorEvent.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +/** + * An {@link EventType.ERROR} type of {@link Event} message sent from the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class ErrorEvent extends Event { + public String errorType; + public String description; + public Integer partitionId; + + public ErrorEvent() { + super(EventType.ERROR); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/Event.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/Event.java new file mode 100644 index 00000000000..77bc1daa4a8 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/Event.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import com.google.gson.annotations.SerializedName; + +/** + * The base type for various event messages sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class Event { + @SerializedName("event") + public EventType eventType; + public String nonce; + @SerializedName("requestID") + public String requestID; + + public Event(EventType eventType) { + this.eventType = eventType; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/EventType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/EventType.java new file mode 100644 index 00000000000..ba2621d937b --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/EventType.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +/** + * The type of {@link Event} sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum EventType { + ALARM, + ARMING, + ERROR, + INFO, + ZONE_EVENT; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/InfoEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/InfoEvent.java new file mode 100644 index 00000000000..775a2f0808c --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/InfoEvent.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +/** + * An {@link EventType.INFO} type of {@link Event} message sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class InfoEvent extends Event { + public InfoEventType infoType; + + public InfoEvent(InfoEventType infoType) { + super(EventType.INFO); + this.infoType = infoType; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/InfoEventType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/InfoEventType.java new file mode 100644 index 00000000000..b6afce9fe7b --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/InfoEventType.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +/** + * The type of {@link InfoEvent} sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum InfoEventType { + SUMMARY, + SECURE_ARM; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/SecureArmInfoEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/SecureArmInfoEvent.java new file mode 100644 index 00000000000..dee4d302506 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/SecureArmInfoEvent.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +/** + * A {@link InfoEventType.SECURE_ARM} type of {@link InfoEvent} message sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class SecureArmInfoEvent extends InfoEvent { + public Integer partitionId; + public Boolean value; + + public SecureArmInfoEvent() { + super(InfoEventType.SECURE_ARM); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/SummaryInfoEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/SummaryInfoEvent.java new file mode 100644 index 00000000000..f80b854a3af --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/SummaryInfoEvent.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import java.util.List; + +import org.openhab.binding.qolsysiq.internal.client.dto.model.Partition; + +/** + * A {@link InfoEventType.SUMMARY} type of {@link InfoEvent} message sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class SummaryInfoEvent extends InfoEvent { + public List partitionList; + + public SummaryInfoEvent() { + super(InfoEventType.SUMMARY); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneActiveEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneActiveEvent.java new file mode 100644 index 00000000000..1b7d74971ed --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneActiveEvent.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import org.openhab.binding.qolsysiq.internal.client.dto.model.ZoneActiveState; + +/** + * A {@link ZoneEventType.ZONE_ACTIVE} type of {@link ZoneEvent} message sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class ZoneActiveEvent extends ZoneEvent { + public ZoneActiveState zone; + + public ZoneActiveEvent() { + super(ZoneEventType.ZONE_ACTIVE); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneAddEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneAddEvent.java new file mode 100644 index 00000000000..999b8a4458e --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneAddEvent.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import org.openhab.binding.qolsysiq.internal.client.dto.model.Zone; + +/** + * A {@link ZoneEventType.ZONE_ADD} type of {@link ZoneEvent} message sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class ZoneAddEvent extends ZoneEvent { + public Zone zone; + + public ZoneAddEvent() { + super(ZoneEventType.ZONE_ADD); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneEvent.java new file mode 100644 index 00000000000..26599f216af --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneEvent.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import com.google.gson.annotations.SerializedName; + +/** + * A Zone {@link Event} message sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class ZoneEvent extends Event { + @SerializedName("zone_event_type") + public ZoneEventType type; + + public ZoneEvent(ZoneEventType type) { + super(EventType.ZONE_EVENT); + this.type = type; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneEventType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneEventType.java new file mode 100644 index 00000000000..d40a5be5692 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneEventType.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +/** + * The type of {@link ZoneEvent} sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum ZoneEventType { + ZONE_ACTIVE, + ZONE_ADD, + ZONE_UPDATE; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneUpdateEvent.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneUpdateEvent.java new file mode 100644 index 00000000000..b01ca09699d --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/event/ZoneUpdateEvent.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.event; + +import org.openhab.binding.qolsysiq.internal.client.dto.model.Zone; + +/** + * A {@link ZoneEventType.ZONE_UPDATE} type of {@link ZoneEvent} message sent by the panel + * + * @author Dan Cunningham - Initial contribution + */ +public class ZoneUpdateEvent extends ZoneEvent { + public Zone zone; + + public ZoneUpdateEvent() { + super(ZoneEventType.ZONE_UPDATE); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/AlarmType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/AlarmType.java new file mode 100644 index 00000000000..9535846775a --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/AlarmType.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.model; + +import com.google.gson.annotations.SerializedName; + +/** + * The type of alarm + * + * @author Dan Cunningham - Initial contribution + */ +public enum AlarmType { + AUXILIARY, + FIRE, + POLICE, + @SerializedName("") + ZONEOPEN, + NONE; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/Partition.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/Partition.java new file mode 100644 index 00000000000..fc02d71b97d --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/Partition.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.model; + +import java.util.List; + +/** + * A logical alarm partition that can be armed, report state and contain zones + * + * @author Dan Cunningham - Initial contribution + */ +public class Partition { + public Integer partitionId; + public String name; + public PartitionStatus status; + public Boolean secureArm; + public List zoneList; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/PartitionStatus.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/PartitionStatus.java new file mode 100644 index 00000000000..cc38eddab9b --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/PartitionStatus.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.model; + +/** + * The current status of an alarm panel + * + * @author Dan Cunningham - Initial contribution + */ +public enum PartitionStatus { + ALARM, + ARM_AWAY, + ARM_STAY, + DISARM, + ENTRY_DELAY, + EXIT_DELAY; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/Zone.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/Zone.java new file mode 100644 index 00000000000..39010306c58 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/Zone.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.model; + +/** + * A zone sensor + * + * @author Dan Cunningham - Initial contribution + */ +public class Zone { + public String id; + public String type; + public String name; + public String group; + public ZoneStatus status; + public Integer state; + public Integer zoneId; + public Integer zonePhysicalType; + public Integer zoneAlarmType; + public ZoneType zoneType; + public Integer partitionId; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneActiveState.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneActiveState.java new file mode 100644 index 00000000000..4a05d525cba --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneActiveState.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.model; + +/** + * The active state of a zone + * + * @author Dan Cunningham - Initial contribution + */ +public class ZoneActiveState { + public Integer zoneId; + public ZoneStatus status; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneStatus.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneStatus.java new file mode 100644 index 00000000000..f37a44119e0 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneStatus.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Represents the status of a zone + * + * @author Dan Cunningham - Initial contribution + */ +public enum ZoneStatus { + @SerializedName("Active") + ACTIVE, + @SerializedName("Closed") + CLOSED, + @SerializedName("Open") + OPEN, + @SerializedName("Failure") + FAILURE, + @SerializedName("Idle") + IDlE, + @SerializedName("Tamper") + TAMPER; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneType.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneType.java new file mode 100644 index 00000000000..e78f838b442 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/client/dto/model/ZoneType.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.client.dto.model; + +import com.google.gson.annotations.SerializedName; + +/** + * The zone physical type + * + * Big thanks to the folks at https://community.home-assistant.io/t/qolsys-iq-panel-2-and-3rd-party-integration/231405 + * + * @author Dan Cunningham - Initial contribution + */ +public enum ZoneType { + @SerializedName("0") + UNKNOWN, + @SerializedName("1") + CONTACT, + @SerializedName("2") + MOTION, + @SerializedName("3") + SOUND, + @SerializedName("4") + BREAKAGE, + @SerializedName("5") + SMOKE_HEAT, + @SerializedName("6") + CARBON_MONOXIDE, + @SerializedName("7") + RADON, + @SerializedName("8") + TEMPERATURE, + @SerializedName("9") + PANIC_BUTTON, + @SerializedName("10") + CONTROL, + @SerializedName("11") + CAMERA, + @SerializedName("12") + LIGHT, + @SerializedName("13") + GPS, + @SerializedName("14") + SIREN, + @SerializedName("15") + WATER, + @SerializedName("16") + TILT, + @SerializedName("17") + FREEZE, + @SerializedName("18") + TAKEOVER_MODULE, + @SerializedName("19") + GLASSBREAK, + @SerializedName("20") + TRANSLATOR, + @SerializedName("21") + MEDICAL_PENDANT, + @SerializedName("22") + WATER_IQ_FLOOD, + @SerializedName("23") + WATER_OTHER_FLOOD, + @SerializedName("30") + IMAGE_SENSOR, + @SerializedName("100") + WIRED_SENSOR, + @SerializedName("101") + RF_SENSOR, + @SerializedName("102") + KEYFOB, + @SerializedName("103") + WALLFOB, + @SerializedName("104") + RF_KEYPAD, + @SerializedName("105") + PANEL, + @SerializedName("106") + WTTS_OR_SECONDARY, + @SerializedName("107") + SHOCK, + @SerializedName("108") + SHOCK_SENSOR_MULTI_FUNCTION, + @SerializedName("109") + DOOR_BELL, + @SerializedName("110") + CONTACT_MULTI_FUNCTION, + @SerializedName("111") + SMOKE_MULTI_FUNCTION, + @SerializedName("112") + TEMPERATURE_MULTI_FUNCTION, + @SerializedName("113") + SHOCK_OTHERS, + @SerializedName("114") + OCCUPANCY_SENSOR, + @SerializedName("115") + BLUETOOTH, + @SerializedName("116") + PANEL_GLASS_BREAK, + @SerializedName("117") + POWERG_SIREN, + @SerializedName("118") + BLUETOOTH_SPEAKER, + @SerializedName("119") + PANEL_MOTION, + @SerializedName("120") + ZWAVE_SIREN, + @SerializedName("121") + COUNT; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQPanelConfiguration.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQPanelConfiguration.java new file mode 100644 index 00000000000..8d09f3b3749 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQPanelConfiguration.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link QolsysIQPanelConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysIQPanelConfiguration { + public String hostname = ""; + public int port = 12345; + public String key = ""; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQPartitionConfiguration.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQPartitionConfiguration.java new file mode 100644 index 00000000000..06107dc3261 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQPartitionConfiguration.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link QolsysIQPartitionConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysIQPartitionConfiguration { + public int id = 0; + public String armCode = ""; + public String disarmCode = ""; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQZoneConfiguration.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQZoneConfiguration.java new file mode 100644 index 00000000000..14800fe158d --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/config/QolsysIQZoneConfiguration.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link QolsysIQZoneConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysIQZoneConfiguration { + public int id = 0; +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/discovery/QolsysIQChildDiscoveryService.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/discovery/QolsysIQChildDiscoveryService.java new file mode 100644 index 00000000000..796c6c480cd --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/discovery/QolsysIQChildDiscoveryService.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.discovery; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.qolsysiq.internal.QolsysIQBindingConstants; +import org.openhab.binding.qolsysiq.internal.handler.QolsysIQChildDiscoveryHandler; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple discovery service that can be used by Partition and Zone Handlers + * + * @author Dan Cunningham - Initial contribution + * + */ +@NonNullByDefault +public class QolsysIQChildDiscoveryService extends AbstractDiscoveryService + implements DiscoveryService, ThingHandlerService { + private final Logger logger = LoggerFactory.getLogger(QolsysIQChildDiscoveryService.class); + + private static final Set SUPPORTED_DISCOVERY_THING_TYPES_UIDS = Set + .of(QolsysIQBindingConstants.THING_TYPE_PARTITION, QolsysIQBindingConstants.THING_TYPE_ZONE); + + private @Nullable ThingHandler thingHandler; + + public QolsysIQChildDiscoveryService() throws IllegalArgumentException { + super(SUPPORTED_DISCOVERY_THING_TYPES_UIDS, 5, false); + } + + @Override + public void setThingHandler(ThingHandler handler) { + if (handler instanceof QolsysIQChildDiscoveryHandler) { + ((QolsysIQChildDiscoveryHandler) handler).setDiscoveryService(this); + this.thingHandler = handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return thingHandler; + } + + @Override + protected void startScan() { + ThingHandler handler = this.thingHandler; + if (handler != null) { + ((QolsysIQChildDiscoveryHandler) handler).startDiscovery(); + } + } + + @Override + public void activate() { + super.activate(null); + } + + @Override + public void deactivate() { + super.deactivate(); + } + + public void discoverQolsysIQChildThing(ThingUID thingUID, ThingUID bridgeUID, Integer id, String label) { + logger.trace("discoverQolsysIQChildThing: {} {} {} {}", thingUID, bridgeUID, id, label); + DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel(label).withProperty("id", id) + .withRepresentationProperty("id").withBridge(bridgeUID).build(); + thingDiscovered(result); + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQChildDiscoveryHandler.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQChildDiscoveryHandler.java new file mode 100644 index 00000000000..f9b818fc090 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQChildDiscoveryHandler.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.qolsysiq.internal.discovery.QolsysIQChildDiscoveryService; + +/** + * Callback for our custom discovery service + * + * @author Dan Cunningham - Initial contribution + * + */ +@NonNullByDefault +public interface QolsysIQChildDiscoveryHandler { + /** + * Sets a {@link QolsysIQChildDiscoveryService} to call when device information is received + * + * @param service + */ + public void setDiscoveryService(QolsysIQChildDiscoveryService service); + + /** + * Initiates the discovery process + */ + public void startDiscovery(); +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQPanelHandler.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQPanelHandler.java new file mode 100644 index 00000000000..c0c736f0f6a --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQPanelHandler.java @@ -0,0 +1,327 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.handler; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.qolsysiq.internal.QolsysIQBindingConstants; +import org.openhab.binding.qolsysiq.internal.client.QolsysIQClientListener; +import org.openhab.binding.qolsysiq.internal.client.QolsysiqClient; +import org.openhab.binding.qolsysiq.internal.client.dto.action.Action; +import org.openhab.binding.qolsysiq.internal.client.dto.action.InfoAction; +import org.openhab.binding.qolsysiq.internal.client.dto.action.InfoActionType; +import org.openhab.binding.qolsysiq.internal.client.dto.event.AlarmEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ArmingEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ErrorEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.SecureArmInfoEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.SummaryInfoEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneActiveEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneAddEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneUpdateEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.model.Partition; +import org.openhab.binding.qolsysiq.internal.config.QolsysIQPanelConfiguration; +import org.openhab.binding.qolsysiq.internal.discovery.QolsysIQChildDiscoveryService; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link QolsysIQPanelHandler} connects to a security panel and routes messages to child partitions. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysIQPanelHandler extends BaseBridgeHandler + implements QolsysIQClientListener, QolsysIQChildDiscoveryHandler { + private final Logger logger = LoggerFactory.getLogger(QolsysIQPanelHandler.class); + private static final int QUICK_RETRY_SECONDS = 1; + private static final int LONG_RETRY_SECONDS = 30; + private static final int HEARTBEAT_SECONDS = 30; + private @Nullable QolsysiqClient apiClient; + private @Nullable ScheduledFuture retryFuture; + private @Nullable QolsysIQChildDiscoveryService discoveryService; + private List partitions = Collections.synchronizedList(new LinkedList()); + private String key = ""; + + public QolsysIQPanelHandler(Bridge bridge) { + super(bridge); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("handleCommand {}", command); + if (command instanceof RefreshType) { + refresh(); + } + } + + @Override + public void initialize() { + logger.debug("initialize"); + updateStatus(ThingStatus.UNKNOWN); + scheduler.execute(() -> { + connect(); + }); + } + + @Override + public void dispose() { + stopRetryFuture(); + disconnect(); + } + + @Override + public Collection> getServices() { + return Collections.singleton(QolsysIQChildDiscoveryService.class); + } + + @Override + public void setDiscoveryService(QolsysIQChildDiscoveryService service) { + this.discoveryService = service; + } + + @Override + public void startDiscovery() { + refresh(); + } + + @Override + public void disconnected(Exception reason) { + logger.debug("disconnected", reason); + setOfflineAndReconnect(reason, QUICK_RETRY_SECONDS); + } + + @Override + public void alarmEvent(AlarmEvent event) { + logger.debug("AlarmEvent {}", event.partitionId); + QolsysIQPartitionHandler handler = partitionHandler(event.partitionId); + if (handler != null) { + handler.alarmEvent(event); + } + } + + @Override + public void armingEvent(ArmingEvent event) { + logger.debug("ArmingEvent {}", event.partitionId); + QolsysIQPartitionHandler handler = partitionHandler(event.partitionId); + if (handler != null) { + handler.armingEvent(event); + } + } + + @Override + public void errorEvent(ErrorEvent event) { + logger.debug("ErrorEvent {}", event.partitionId); + QolsysIQPartitionHandler handler = partitionHandler(event.partitionId); + if (handler != null) { + handler.errorEvent(event); + } + } + + @Override + public void summaryInfoEvent(SummaryInfoEvent event) { + logger.debug("SummaryInfoEvent"); + synchronized (partitions) { + partitions.clear(); + partitions.addAll(event.partitionList); + } + updatePartitions(); + discoverChildDevices(); + } + + @Override + public void secureArmInfoEvent(SecureArmInfoEvent event) { + logger.debug("ArmingEvent {}", event.value); + QolsysIQPartitionHandler handler = partitionHandler(event.partitionId); + if (handler != null) { + handler.secureArmInfoEvent(event); + } + } + + @Override + public void zoneActiveEvent(ZoneActiveEvent event) { + logger.debug("ZoneActiveEvent {} {}", event.zone.zoneId, event.zone.status); + partitions.forEach(p -> { + if (p.zoneList.stream().filter(z -> z.zoneId.equals(event.zone.zoneId)).findAny().isPresent()) { + QolsysIQPartitionHandler handler = partitionHandler(p.partitionId); + if (handler != null) { + handler.zoneActiveEvent(event); + } + } + }); + } + + @Override + public void zoneUpdateEvent(ZoneUpdateEvent event) { + logger.debug("ZoneUpdateEvent {}", event.zone.name); + partitions.forEach(p -> { + if (p.zoneList.stream().filter(z -> z.zoneId.equals(event.zone.zoneId)).findAny().isPresent()) { + QolsysIQPartitionHandler handler = partitionHandler(p.partitionId); + if (handler != null) { + handler.zoneUpdateEvent(event); + } + } + }); + } + + @Override + public void zoneAddEvent(ZoneAddEvent event) { + logger.debug("ZoneAddEvent {}", event.zone.name); + partitions.forEach(p -> { + if (p.zoneList.stream().filter(z -> z.zoneId.equals(event.zone.zoneId)).findAny().isPresent()) { + QolsysIQPartitionHandler handler = partitionHandler(p.partitionId); + if (handler != null) { + handler.zoneAddEvent(event); + } + } + }); + } + + /** + * Sends the action to the panel. This will replace the token of the action passed in with the one configured here + * + * @param action + */ + protected void sendAction(Action action) { + action.token = key; + QolsysiqClient client = this.apiClient; + if (client != null) { + try { + client.sendAction(action); + } catch (IOException e) { + logger.debug("Could not send action", e); + setOfflineAndReconnect(e, QUICK_RETRY_SECONDS); + } + } + } + + protected synchronized void refresh() { + sendAction(new InfoAction(InfoActionType.SUMMARY)); + } + + /** + * Connect the client + */ + private synchronized void connect() { + if (getThing().getStatus() == ThingStatus.ONLINE) { + logger.debug("connect: Bridge is already connected"); + return; + } + QolsysIQPanelConfiguration config = getConfigAs(QolsysIQPanelConfiguration.class); + key = config.key; + + try { + QolsysiqClient apiClient = new QolsysiqClient(config.hostname, config.port, HEARTBEAT_SECONDS, scheduler, + "OH-binding-" + getThing().getUID().getAsString()); + apiClient.connect(); + apiClient.addListener(this); + this.apiClient = apiClient; + refresh(); + updateStatus(ThingStatus.ONLINE); + } catch (IOException e) { + logger.debug("Could not connect"); + setOfflineAndReconnect(e, LONG_RETRY_SECONDS); + } + } + + /** + * Disconnects the client and removes listeners + */ + private void disconnect() { + logger.debug("disconnect"); + QolsysiqClient apiClient = this.apiClient; + if (apiClient != null) { + apiClient.removeListener(this); + apiClient.disconnect(); + this.apiClient = null; + } + } + + private void startRetryFuture(int seconds) { + stopRetryFuture(); + logger.debug("startRetryFuture"); + this.retryFuture = scheduler.schedule(this::connect, seconds, TimeUnit.SECONDS); + } + + private void stopRetryFuture() { + logger.debug("stopRetryFuture"); + ScheduledFuture retryFuture = this.retryFuture; + if (retryFuture != null) { + retryFuture.cancel(true); + this.retryFuture = null; + } + } + + private void setOfflineAndReconnect(Exception reason, int seconds) { + logger.debug("setOfflineAndReconnect"); + disconnect(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, reason.getMessage()); + startRetryFuture(seconds); + } + + private void updatePartitions() { + synchronized (partitions) { + partitions.forEach(p -> { + QolsysIQPartitionHandler handler = partitionHandler(p.partitionId); + if (handler != null) { + handler.updatePartition(p); + } + }); + } + } + + private void discoverChildDevices() { + synchronized (partitions) { + QolsysIQChildDiscoveryService discoveryService = this.discoveryService; + if (discoveryService != null) { + partitions.forEach(p -> { + ThingUID bridgeUID = getThing().getUID(); + ThingUID thingUID = new ThingUID(QolsysIQBindingConstants.THING_TYPE_PARTITION, bridgeUID, + String.valueOf(p.partitionId)); + discoveryService.discoverQolsysIQChildThing(thingUID, bridgeUID, p.partitionId, + "Qolsys IQ Partition: " + p.name); + }); + } + } + } + + private @Nullable QolsysIQPartitionHandler partitionHandler(int partitionId) { + for (Thing thing : getThing().getThings()) { + ThingHandler handler = thing.getHandler(); + if (handler instanceof QolsysIQPartitionHandler) { + if (((QolsysIQPartitionHandler) handler).getPartitionId() == partitionId) { + return (QolsysIQPartitionHandler) handler; + } + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQPartitionHandler.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQPartitionHandler.java new file mode 100644 index 00000000000..4dca6fde854 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQPartitionHandler.java @@ -0,0 +1,369 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.handler; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.qolsysiq.internal.QolsysIQBindingConstants; +import org.openhab.binding.qolsysiq.internal.client.dto.action.AlarmAction; +import org.openhab.binding.qolsysiq.internal.client.dto.action.AlarmActionType; +import org.openhab.binding.qolsysiq.internal.client.dto.action.ArmingAction; +import org.openhab.binding.qolsysiq.internal.client.dto.action.ArmingActionType; +import org.openhab.binding.qolsysiq.internal.client.dto.event.AlarmEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ArmingEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ErrorEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.SecureArmInfoEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneActiveEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneAddEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneUpdateEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.model.AlarmType; +import org.openhab.binding.qolsysiq.internal.client.dto.model.Partition; +import org.openhab.binding.qolsysiq.internal.client.dto.model.PartitionStatus; +import org.openhab.binding.qolsysiq.internal.client.dto.model.Zone; +import org.openhab.binding.qolsysiq.internal.config.QolsysIQPartitionConfiguration; +import org.openhab.binding.qolsysiq.internal.discovery.QolsysIQChildDiscoveryService; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingStatusInfo; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.BridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link QolsysIQPartitionHandler} manages security partitions + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysIQPartitionHandler extends BaseBridgeHandler implements QolsysIQChildDiscoveryHandler { + private final Logger logger = LoggerFactory.getLogger(QolsysIQPartitionHandler.class); + private static final int CLEAR_ERROR_MESSSAGE_TIME = 30; + private @Nullable QolsysIQChildDiscoveryService discoveryService; + private @Nullable ScheduledFuture delayFuture; + private @Nullable ScheduledFuture errorFuture; + private @Nullable String armCode; + private @Nullable String disarmCode; + private List zones = Collections.synchronizedList(new LinkedList()); + private int partitionId; + + public QolsysIQPartitionHandler(Bridge bridge) { + super(bridge); + } + + @Override + public void initialize() { + QolsysIQPartitionConfiguration config = getConfigAs(QolsysIQPartitionConfiguration.class); + partitionId = config.id; + armCode = config.armCode.isBlank() ? null : config.armCode; + disarmCode = config.disarmCode.isBlank() ? null : config.disarmCode; + logger.debug("initialize partition {}", partitionId); + initializePartition(); + } + + @Override + public void dispose() { + cancelExitDelayJob(); + cancelErrorDelayJob(); + } + + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + if (bridgeStatusInfo.getStatus() != ThingStatus.ONLINE) { + cancelExitDelayJob(); + cancelErrorDelayJob(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } else { + initializePartition(); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + refresh(); + return; + } + + QolsysIQPanelHandler panel = panelHandler(); + if (panel != null) { + if (channelUID.getId().equals(QolsysIQBindingConstants.CHANNEL_PARTITION_ALARM_STATE)) { + try { + panel.sendAction(new AlarmAction(AlarmActionType.valueOf(command.toString()))); + } catch (IllegalArgumentException e) { + logger.debug("Unknown alarm type {} to channel {}", command, channelUID); + } + return; + } + + // support ARM_AWAY and ARM_AWAY:123456 , same for other arm / disarm modes + if (channelUID.getId().equals(QolsysIQBindingConstants.CHANNEL_PARTITION_ARM_STATE)) { + String armingTypeName = command.toString(); + String code = null; + if (armingTypeName.contains(":")) { + String[] split = armingTypeName.split(":"); + armingTypeName = split[0]; + if (split.length > 1 && split[1].length() > 0) { + code = split[1]; + } + } + try { + ArmingActionType armingType = ArmingActionType.valueOf(armingTypeName); + if (code == null) { + if (armingType == ArmingActionType.DISARM) { + code = disarmCode; + } else { + code = armCode; + } + } + panel.sendAction(new ArmingAction(armingType, getPartitionId(), code)); + } catch (IllegalArgumentException e) { + logger.debug("Unknown arm type {} to channel {}", armingTypeName, channelUID); + } + } + } + } + + @Override + public Collection> getServices() { + return Collections.singleton(QolsysIQChildDiscoveryService.class); + } + + @Override + public void setDiscoveryService(QolsysIQChildDiscoveryService service) { + this.discoveryService = service; + } + + @Override + public void startDiscovery() { + refresh(); + } + + /** + * The partition id + * + * @return + */ + public int getPartitionId() { + return partitionId; + } + + public void zoneActiveEvent(ZoneActiveEvent event) { + QolsysIQZoneHandler handler = zoneHandler(event.zone.zoneId); + if (handler != null) { + handler.zoneActiveEvent(event); + } + } + + public void zoneUpdateEvent(ZoneUpdateEvent event) { + QolsysIQZoneHandler handler = zoneHandler(event.zone.zoneId); + if (handler != null) { + handler.zoneUpdateEvent(event); + } + } + + protected void alarmEvent(AlarmEvent event) { + if (event.alarmType != AlarmType.NONE && event.alarmType != AlarmType.ZONEOPEN) { + updatePartitionStatus(PartitionStatus.ALARM); + } + updateAlarmState(event.alarmType); + } + + protected void armingEvent(ArmingEvent event) { + updatePartitionStatus(event.armingType); + updateDelay(event.delay == null ? 0 : event.delay); + } + + protected void errorEvent(ErrorEvent event) { + cancelErrorDelayJob(); + updateState(QolsysIQBindingConstants.CHANNEL_PARTITION_ERROR_EVENT, new StringType(event.description)); + errorFuture = scheduler.schedule(this::clearErrorEvent, CLEAR_ERROR_MESSSAGE_TIME, TimeUnit.SECONDS); + } + + protected void secureArmInfoEvent(SecureArmInfoEvent event) { + setSecureArm(event.value); + } + + public void zoneAddEvent(ZoneAddEvent event) { + discoverZone(event.zone); + } + + protected void updatePartition(Partition partition) { + updatePartitionStatus(partition.status); + setSecureArm(partition.secureArm); + if (partition.status != PartitionStatus.ALARM) { + updateAlarmState(AlarmType.NONE); + } + synchronized (zones) { + zones.clear(); + zones.addAll(partition.zoneList); + zones.forEach(z -> { + QolsysIQZoneHandler zoneHandler = zoneHandler(z.zoneId); + if (zoneHandler != null) { + zoneHandler.updateZone(z); + } + }); + } + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } + discoverChildDevices(); + } + + protected @Nullable Zone getZone(Integer zoneId) { + synchronized (zones) { + return zones.stream().filter(z -> z.zoneId.equals(zoneId)).findAny().orElse(null); + } + } + + private void initializePartition() { + QolsysIQPanelHandler panel = panelHandler(); + if (panel == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED); + } else if (panel.getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } else { + updateStatus(ThingStatus.UNKNOWN); + scheduler.execute(() -> { + panel.refresh(); + }); + } + } + + private void refresh() { + QolsysIQPanelHandler panel = panelHandler(); + if (panel != null) { + panel.refresh(); + } + } + + private void updatePartitionStatus(PartitionStatus status) { + updateState(QolsysIQBindingConstants.CHANNEL_PARTITION_ARM_STATE, new StringType(status.toString())); + cancelErrorDelayJob(); + if (status == PartitionStatus.DISARM) { + updateAlarmState(AlarmType.NONE); + updateDelay(0); + } + } + + private void setSecureArm(Boolean secure) { + Map props = new HashMap(); + props.put("secureArm", String.valueOf(secure)); + getThing().setProperties(props); + } + + private void updateDelay(Integer delay) { + cancelExitDelayJob(); + if (delay <= 0) { + updateState(QolsysIQBindingConstants.CHANNEL_PARTITION_COMMAND_DELAY, new DecimalType(0)); + return; + } + + final long endTime = System.currentTimeMillis() + (delay * 1000); + delayFuture = scheduler.scheduleAtFixedRate(() -> { + long remaining = endTime - System.currentTimeMillis(); + logger.debug("updateDelay remaining {}", remaining / 1000); + if (remaining <= 0) { + cancelExitDelayJob(); + } else { + updateState(QolsysIQBindingConstants.CHANNEL_PARTITION_COMMAND_DELAY, + new DecimalType(remaining / 1000)); + } + }, 1, 1, TimeUnit.SECONDS); + } + + private void updateAlarmState(AlarmType alarmType) { + updateState(QolsysIQBindingConstants.CHANNEL_PARTITION_ALARM_STATE, new StringType(alarmType.toString())); + } + + private void clearErrorEvent() { + updateState(QolsysIQBindingConstants.CHANNEL_PARTITION_ERROR_EVENT, UnDefType.NULL); + } + + private void cancelExitDelayJob() { + ScheduledFuture delayFuture = this.delayFuture; + if (delayFuture != null) { + delayFuture.cancel(true); + this.delayFuture = null; + } + updateState(QolsysIQBindingConstants.CHANNEL_PARTITION_COMMAND_DELAY, new DecimalType(0)); + } + + private void cancelErrorDelayJob() { + ScheduledFuture errorFuture = this.errorFuture; + if (errorFuture != null) { + errorFuture.cancel(true); + this.errorFuture = null; + } + clearErrorEvent(); + } + + private void discoverChildDevices() { + synchronized (zones) { + zones.forEach(z -> discoverZone(z)); + } + } + + private void discoverZone(Zone z) { + QolsysIQChildDiscoveryService discoveryService = this.discoveryService; + if (discoveryService != null) { + ThingUID bridgeUID = getThing().getUID(); + ThingUID thingUID = new ThingUID(QolsysIQBindingConstants.THING_TYPE_ZONE, bridgeUID, + String.valueOf(z.zoneId)); + discoveryService.discoverQolsysIQChildThing(thingUID, bridgeUID, z.zoneId, "Qolsys IQ Zone: " + z.name); + } + } + + private @Nullable QolsysIQZoneHandler zoneHandler(int zoneId) { + for (Thing thing : getThing().getThings()) { + ThingHandler handler = thing.getHandler(); + if (handler instanceof QolsysIQZoneHandler) { + if (((QolsysIQZoneHandler) handler).getZoneId() == zoneId) { + return (QolsysIQZoneHandler) handler; + } + } + } + return null; + } + + private @Nullable QolsysIQPanelHandler panelHandler() { + Bridge bridge = getBridge(); + if (bridge != null) { + BridgeHandler handler = bridge.getHandler(); + if (handler instanceof QolsysIQPanelHandler) { + return (QolsysIQPanelHandler) handler; + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQZoneHandler.java b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQZoneHandler.java new file mode 100644 index 00000000000..fbcc4420945 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/java/org/openhab/binding/qolsysiq/internal/handler/QolsysIQZoneHandler.java @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2010-2022 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.qolsysiq.internal.handler; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.qolsysiq.internal.QolsysIQBindingConstants; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneActiveEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.event.ZoneUpdateEvent; +import org.openhab.binding.qolsysiq.internal.client.dto.model.Zone; +import org.openhab.binding.qolsysiq.internal.client.dto.model.ZoneStatus; +import org.openhab.binding.qolsysiq.internal.config.QolsysIQZoneConfiguration; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingStatusInfo; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.BridgeHandler; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link QolsysIQZoneHandler} manages security zones. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class QolsysIQZoneHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(QolsysIQZoneHandler.class); + + private int zoneId; + + public QolsysIQZoneHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + logger.debug("initialize"); + zoneId = getConfigAs(QolsysIQZoneConfiguration.class).id; + initializeZone(); + } + + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusChanged) { + logger.debug("bridgeStatusChanged {}", bridgeStatusChanged); + initializeZone(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + public int getZoneId() { + return zoneId; + } + + protected void updateZone(Zone zone) { + logger.debug("updateZone {}", zone.zoneId); + updateState(QolsysIQBindingConstants.CHANNEL_ZONE_STATE, new DecimalType(zone.state)); + updateZoneStatus(zone.status); + Map props = new HashMap(); + props.put("type", zone.type); + props.put("name", zone.name); + props.put("group", zone.group); + props.put("zoneID", zone.id); + props.put("zonePhysicalType", String.valueOf(zone.zonePhysicalType)); + props.put("zoneAlarmType", String.valueOf(zone.zoneAlarmType)); + props.put("zoneType", zone.zoneType.toString()); + props.put("partitionId", String.valueOf(zone.partitionId)); + getThing().setProperties(props); + } + + protected void zoneActiveEvent(ZoneActiveEvent event) { + if (event.zone.zoneId == getZoneId()) { + updateZoneStatus(event.zone.status); + } + } + + protected void zoneUpdateEvent(ZoneUpdateEvent event) { + if (event.zone.zoneId == getZoneId()) { + updateZone(event.zone); + } + } + + private void initializeZone() { + Bridge bridge = getBridge(); + BridgeHandler handler = bridge == null ? null : bridge.getHandler(); + if (bridge != null && handler instanceof QolsysIQPartitionHandler) { + if (handler.getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + return; + } + Zone z = ((QolsysIQPartitionHandler) handler).getZone(getZoneId()); + if (z == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Zone not found in partition"); + return; + } + updateZone(z); + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED); + } + } + + private void updateZoneStatus(@Nullable ZoneStatus status) { + if (status != null) { + updateState(QolsysIQBindingConstants.CHANNEL_ZONE_STATUS, new StringType(status.toString())); + updateState(QolsysIQBindingConstants.CHANNEL_ZONE_CONTACT, + status == ZoneStatus.CLOSED || status == ZoneStatus.IDlE ? OpenClosedType.CLOSED + : OpenClosedType.OPEN); + } else { + logger.debug("updateZoneStatus: null status"); + } + } +} diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 00000000000..1b734542c64 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + QolsysIQ Binding + This is the binding for Qolsys IQ Alarm Systems. + + diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/i18n/qolsysiq.properties b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/i18n/qolsysiq.properties new file mode 100644 index 00000000000..ad5424eb206 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/i18n/qolsysiq.properties @@ -0,0 +1,72 @@ +## mvn i18n:generate-default-translations + +# binding + +binding.qolsysiq.name = QolsysIQ Binding +binding.qolsysiq.description = This is the binding for Qolsys IQ Alarm Systems. + +# thing types + +thing-type.qolsysiq.panel.label = Qolsys IQ Panel +thing-type.qolsysiq.panel.description = A Qolsys IQ Panel Bridge +thing-type.qolsysiq.partition.label = Partition +thing-type.qolsysiq.partition.description = A Qolsys IQ Partition +thing-type.qolsysiq.zone.label = Zone +thing-type.qolsysiq.zone.description = A Qolsys IQ Zone + +# thing types config + +thing-type.config.qolsysiq.panel.hostname.label = Hostname +thing-type.config.qolsysiq.panel.hostname.description = Hostname or IP address of the panel +thing-type.config.qolsysiq.panel.key.label = key +thing-type.config.qolsysiq.panel.key.description = Key to access the device +thing-type.config.qolsysiq.panel.port.label = Port +thing-type.config.qolsysiq.panel.port.description = The port to connect to on the panel. +thing-type.config.qolsysiq.partition.armCode.label = Arm Code +thing-type.config.qolsysiq.partition.armCode.description = Optional arm code to use when receiving arm commands without a code. Only required if the panel has been configured to require arm codes. Leave blank to always require a code +thing-type.config.qolsysiq.partition.disarmCode.label = Disarm Code +thing-type.config.qolsysiq.partition.disarmCode.description = Optional disarm code to use when receiving a disarm command without a code. Required for integrations like Alexa and Homekit who do not provide codes when disarming. Leave blank to always require a code +thing-type.config.qolsysiq.partition.id.label = Partition ID +thing-type.config.qolsysiq.partition.id.description = The Partition ID. +thing-type.config.qolsysiq.zone.id.label = Zone ID +thing-type.config.qolsysiq.zone.id.description = The Zone ID. + +# channel types + +channel-type.qolsysiq.alarmState.label = Partition Alarm State +channel-type.qolsysiq.alarmState.description = Reports on the current alarm state, or triggers an instant alarm. +channel-type.qolsysiq.alarmState.state.option.AUXILIARY = Auxiliary +channel-type.qolsysiq.alarmState.state.option.FIRE = Fire +channel-type.qolsysiq.alarmState.state.option.POLICE = Police +channel-type.qolsysiq.alarmState.state.option.ZONEOPEN = Zone Open +channel-type.qolsysiq.alarmState.state.option.NONE = None +channel-type.qolsysiq.alarmState.command.option.AUXILIARY = Auxiliary +channel-type.qolsysiq.alarmState.command.option.FIRE = Fire +channel-type.qolsysiq.alarmState.command.option.POLICE = Police +channel-type.qolsysiq.armState.label = Partition Arm State +channel-type.qolsysiq.armState.description = Reports the current partition arm state or sends a arm or disarm command to the system. For security codes, append the 6 digit code to the command separated by a colon (e.g. 'DISARM:123456') +channel-type.qolsysiq.armState.state.option.ALARM = In Alarm +channel-type.qolsysiq.armState.state.option.ARM_AWAY = Armed Away +channel-type.qolsysiq.armState.state.option.ARM_STAY = Armed Stay +channel-type.qolsysiq.armState.state.option.DISARM = Disarmed +channel-type.qolsysiq.armState.state.option.ENTRY_DELAY = Entry Delay +channel-type.qolsysiq.armState.state.option.EXIT_DELAY = Exit Delay +channel-type.qolsysiq.armState.command.option.ARM_AWAY = Arm Away +channel-type.qolsysiq.armState.command.option.ARM_STAY = Arm Stay +channel-type.qolsysiq.armState.command.option.DISARM = Disarm +channel-type.qolsysiq.armingDelay.label = Partition Arming Delay +channel-type.qolsysiq.armingDelay.description = The arming delay currently in progress +channel-type.qolsysiq.contact.label = Zone Contact +channel-type.qolsysiq.contact.description = The zone contact state. +channel-type.qolsysiq.errorEvent.label = Error Event +channel-type.qolsysiq.errorEvent.description = Last error event message reported by the partition. Clears after 30 seconds +channel-type.qolsysiq.zoneState.label = Zone State +channel-type.qolsysiq.zoneState.description = The zone state. +channel-type.qolsysiq.zoneStatus.label = Zone Status +channel-type.qolsysiq.zoneStatus.description = The zone status. +channel-type.qolsysiq.zoneStatus.state.option.ACTIVE = Active +channel-type.qolsysiq.zoneStatus.state.option.CLOSED = Closed +channel-type.qolsysiq.zoneStatus.state.option.OPEN = Open +channel-type.qolsysiq.zoneStatus.state.option.FAILURE = Failure +channel-type.qolsysiq.zoneStatus.state.option.IDlE = Idle +channel-type.qolsysiq.zoneStatus.state.option.TAMPER = Tamper diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/panel.xml b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/panel.xml new file mode 100644 index 00000000000..65ba24377b4 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/panel.xml @@ -0,0 +1,28 @@ + + + + + A Qolsys IQ Panel Bridge + + + network-address + + Hostname or IP address of the panel + + + + The port to connect to on the panel. + 12345 + true + + + password + + Key to access the device + + + + diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/partition.xml b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/partition.xml new file mode 100644 index 00000000000..cb56226e544 --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/partition.xml @@ -0,0 +1,103 @@ + + + + + + + + A Qolsys IQ Partition + + + + + + + + false + + id + + + + The Partition ID. + + + + + Optional disarm code to use when receiving a disarm command without a code. Required for integrations + like Alexa and Homekit who do not provide codes when disarming. Leave blank to always require a code + + + + + Optional arm code to use when receiving arm commands without a code. Only required if the panel has + been configured to require arm codes. Leave blank to always require a code + true + + + + + String + + Reports the current partition arm state or sends a arm or disarm command to the system. For security + codes, append the 6 digit code to the command separated by a colon (e.g. 'DISARM:123456') + Alarm + + + + + + + + + + + + + + + + + + veto + + + String + + Reports on the current alarm state, or triggers an instant alarm. + Alarm + + + + + + + + + + + + + + + + + veto + + + Number + + The arming delay currently in progress + Alarm + + + + String + + Last error event message reported by the partition. Clears after 30 seconds + + + diff --git a/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/zone.xml b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/zone.xml new file mode 100644 index 00000000000..b53a9d5e76f --- /dev/null +++ b/bundles/org.openhab.binding.qolsysiq/src/main/resources/OH-INF/thing/zone.xml @@ -0,0 +1,63 @@ + + + + + + + + A Qolsys IQ Zone + + + + + + + + + + + + + + + + id + + + + The Zone ID. + + + + + String + + The zone status. + + + + + + + + + + + + + Number + + The zone state. + + + + + Contact + + The zone contact state. + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 316b6a9ad2d..08386b72ddf 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -305,6 +305,7 @@ org.openhab.binding.pushover org.openhab.binding.pushsafer org.openhab.binding.qbus + org.openhab.binding.qolsysiq org.openhab.binding.radiothermostat org.openhab.binding.regoheatpump org.openhab.binding.revogi