From 164c983d399bd33cccc10e4631190a372578a10a Mon Sep 17 00:00:00 2001 From: Konstantin Polihronov Date: Wed, 15 Jan 2020 10:23:17 +0200 Subject: [PATCH] [paradoxalarm] Limit maximum zones and partitions with new parameters and more channels added to partition thing (#6792) * Implementation of maxZones and maxPartitions parameters * Reworked creation of EvoCommunicator to use builder pattern * Added support for additional, non-mandatory parameters maxZones and maxPartitions which limit the maximum amount of zones and partitions that are used during refresh. * Changed Factory to create builder instead of communicator * Renamed classes to represent properly the new object creation design Signed-off-by: Konstantin Polihronov --- .../README.md | 49 ++++++- .../communication/EvoCommunicator.java | 120 ++++++++++++++++-- .../communication/ICommunicatorBuilder.java | 39 ++++++ .../communication/ParadoxBuilderFactory.java | 41 ++++++ .../ParadoxCommunicatorFactory.java | 71 ----------- .../ParadoxAlarmBindingConstants.java | 27 +++- .../ParadoxIP150BridgeConfiguration.java | 18 +++ .../handlers/ParadoxIP150BridgeHandler.java | 22 ++-- .../handlers/ParadoxPartitionHandler.java | 37 +++++- .../internal/model/ParadoxPanel.java | 11 +- .../internal/model/Partition.java | 3 +- .../internal/model/PartitionState.java | 65 +--------- .../ESH-INF/thing/ip150connector.xml | 10 ++ .../resources/ESH-INF/thing/partition.xml | 114 ++++++++++++++++- .../src/test/java/main/Main.java | 26 ++-- 15 files changed, 460 insertions(+), 193 deletions(-) create mode 100644 bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ICommunicatorBuilder.java create mode 100644 bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxBuilderFactory.java delete mode 100644 bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxCommunicatorFactory.java diff --git a/bundles/org.openhab.binding.paradoxalarm/README.md b/bundles/org.openhab.binding.paradoxalarm/README.md index 05bf0749fe8e8..f3b509830ec52 100644 --- a/bundles/org.openhab.binding.paradoxalarm/README.md +++ b/bundles/org.openhab.binding.paradoxalarm/README.md @@ -1,10 +1,9 @@ # Paradox Alarm System binding This binding is intended to provide basic support for Paradox Alarm system. - +Currently the binding does not support active communication, i.e. you cannot change states (arming, disarming). The intention is to use it only for monitoring of your security system. With the power of openHAB this binding can be used for complex decision rules combining motion/magnetic sensor or whole partitions states with different scenarios. - Examples: * All partitions are armed, therefore there is no one at home. @@ -25,7 +24,7 @@ Currently binding supports the following panels: EVO192, EVO48(not tested), EVO9 ## Things configuration -### IP150 parameters +### IP150 bridge parameters | Parameter | Description | |-------------------|----------------------------------------| @@ -34,10 +33,12 @@ Currently binding supports the following panels: EVO192, EVO48(not tested), EVO9 | pcPassword | The code 3012 setting. Default value is 0000.| | ipAddress | IP address of your IP150.| | port | The port used for data communication. Default value is 10000.| -| panelType | Not mandatory. Will be used if discovery does not identify the panel. Otherwise provide EVO48, EVO96, EVO192, etc...| +| panelType | Optional parameter. Will be used if discovery does not identify the panel. Otherwise provide EVO48, EVO96, EVO192, etc...| | reconnectWaitTime | Value is in seconds. The time to wait before a reconnect occurs after socket timeout.| +| maxPartitions | Optional parameter which sets maximum partitions to use during refresh. If not set, maximum allowed amount from panelType will be used.| +| maxZones | Optional parameter which sets maximum zones to use during refresh. If not set, maximum allowed amount from panelType will be used.| -### IP150 channels +### IP150 bridge channels | Channel | Description | |---------------------|------------------------------------------------| @@ -55,6 +56,36 @@ Currently binding supports the following panels: EVO192, EVO48(not tested), EVO9 |--------|------------------------------------------------------------------------------------| | id | The numeric ID of the zone/partition | +### Partition channels: + +| Channel | Type | Description | +|--------------------------|---------|---------------------------------------------------------------------------------------------| +| partitionLabel | String | Label of partition inside Paradox configuration | +| state | String |State of partition (armed, disarmed, in alarm) | +| additionalState | String | This used to be a channel where all different states were consolidated as semi-colon separated string. With implementation of each state as channel additional states should be no longer used. (deprecated channel) | +| readyToArm | Switch | Partition is Ready to arm | +| inExitDelay | Switch | Partition is in Exit delay | +| inEntryDelay | Switch | Partition in Entry Delay | +| inTrouble | Switch | Partition has trouble | +| alarmInMemory | Switch | Partition has alarm in memory | +| zoneBypass | Switch | Partition is in Zone Bypass | +| zoneInTamperTrouble | Switch | Partition is in Tamper Trouble | +| zoneInLowBatteryTrouble | Switch | Partition has zone in Low Battery Trouble | +| zoneInFireLoopTrouble | Switch | Partition has zone in Fire Loop Trouble | +| zoneInSupervisionTrouble | Switch | Partition has zone in Supervision Trouble | +| stayInstantReady | Switch | Partition is in state Stay Instant Ready | +| forceReady | Switch | Partition is in state Force Ready | +| bypassReady | Switch | Partition is in state Bypass Ready | +| inhibitReady | Switch | Partition is in state Inhibit Ready | +| allZonesClosed | Contact | All zones in partition are currently closed | + +### Zone channels: + +| Channel | Type | Description | +|-----------------|---------|--------------------------------------------------------------------------------| +| zoneLabel | String | Label of zone inside Paradox configuration | +| openedState | Contact | Zone opened / closed | +| tamperedState | Switch | Zone is tampered / not tampered | ## Example things configuration ```java @@ -136,3 +167,11 @@ Currently binding supports the following panels: EVO192, EVO48(not tested), EVO9 } } ``` +## Acknowledgements +This binding would not be possible without the reverse engineering of the byte level protocol and the development by other authors in python, C# and other languages. Many thanks to the following authors and their respective github repositories for their development that helped in creating this binding: + +João Paulo Barraca - https://github.com/ParadoxAlarmInterface/pai + +Jean Henning - repository not available + +Tertuish - https://github.com/Tertiush/ParadoxIP150v2 / https://github.com/Tertiush/ParadoxIP150 \ No newline at end of file diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/EvoCommunicator.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/EvoCommunicator.java index 7e7759f7f1fd4..e57745e2d41cc 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/EvoCommunicator.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/EvoCommunicator.java @@ -29,6 +29,7 @@ import org.openhab.binding.paradoxalarm.internal.communication.messages.ParadoxIPPacket; import org.openhab.binding.paradoxalarm.internal.communication.messages.RamRequestPayload; import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxException; +import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxRuntimeException; import org.openhab.binding.paradoxalarm.internal.model.EntityType; import org.openhab.binding.paradoxalarm.internal.model.PanelType; import org.openhab.binding.paradoxalarm.internal.model.ZoneStateFlags; @@ -49,15 +50,20 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm private MemoryMap memoryMap; - // Map of EntityTypes (Key:EntityType (Zone, Partition), Value: Map private Map> entityLabelsMap = new HashMap<>(); private PanelType panelType = PanelType.UNKNOWN; + private Integer maxPartitions; + private Integer maxZones; - public EvoCommunicator(String ipAddress, int tcpPort, String ip150Password, String pcPassword, - ScheduledExecutorService scheduler, PanelType panelType) throws UnknownHostException, IOException { + private EvoCommunicator(String ipAddress, int tcpPort, String ip150Password, String pcPassword, + ScheduledExecutorService scheduler, PanelType panelType, Integer maxPartitions, Integer maxZones) + throws UnknownHostException, IOException { super(ipAddress, tcpPort, ip150Password, pcPassword, scheduler); this.panelType = panelType; + this.maxPartitions = maxPartitions; + this.maxZones = maxZones; + logger.debug("PanelType={}, max partitions={}, max Zones={}", panelType, maxPartitions, maxZones); initializeMemoryMap(); } @@ -106,7 +112,7 @@ private void retrievePartitionLabel(int partitionNo) { try { IPPacketPayload payload = new EpromRequestPayload(address, labelLength); ParadoxIPPacket readEpromIPPacket = new ParadoxIPPacket(payload) - .setMessageType(HeaderMessageType.SERIAL_PASSTHRU_REQUEST).setUnknown0((byte) 0x14); + .setMessageType(HeaderMessageType.SERIAL_PASSTHRU_REQUEST).setUnknown0((byte) 0x14); IRequest epromRequest = new EpromRequest(partitionNo, EntityType.PARTITION, readEpromIPPacket); submitRequest(epromRequest); @@ -229,8 +235,8 @@ private void readRAM(int blockNo) { submitRequest(ramRequest); } catch (ParadoxException e) { logger.debug( - "Unable to create request payload from provided bytes to read. blockNo={}, bytes to read={}. Exception={}", - blockNo, RAM_BLOCK_SIZE, e.getMessage()); + "Unable to create request payload from provided bytes to read. blockNo={}, bytes to read={}. Exception={}", + blockNo, RAM_BLOCK_SIZE, e.getMessage()); } } @@ -284,11 +290,109 @@ public void initializeData() { } private void initializeEpromData() { - for (int i = 0; i < panelType.getPartitions(); i++) { + for (int i = 0; i < maxPartitions; i++) { retrievePartitionLabel(i); } - for (int i = 0; i < panelType.getZones(); i++) { + for (int i = 0; i < maxZones; i++) { retrieveZoneLabel(i); } } + + public static class EvoCommunicatorBuilder implements ICommunicatorBuilder { + + private final Logger logger = LoggerFactory.getLogger(EvoCommunicatorBuilder.class); + + // Mandatory parameters + private PanelType panelType; + private String ipAddress; + private String ip150Password; + private ScheduledExecutorService scheduler; + + // Non mandatory or with predefined values + private Integer maxPartitions; + private Integer maxZones; + private int tcpPort = 10000; + private String pcPassword = "0000"; + + EvoCommunicatorBuilder(PanelType panelType) { + this.panelType = panelType; + } + + @Override + public IParadoxCommunicator build() { + if (panelType != PanelType.EVO48 && panelType != PanelType.EVO96 && panelType != PanelType.EVO192) { + throw new ParadoxRuntimeException("Unknown or unsupported panel type. Type=" + panelType); + } + + if (ipAddress == null || ipAddress.isEmpty()) { + throw new ParadoxRuntimeException("IP address cannot be empty !"); + } + + if (ip150Password == null || ip150Password.isEmpty()) { + throw new ParadoxRuntimeException("Password for IP150 cannot be empty !"); + } + + if (scheduler == null) { + throw new ParadoxRuntimeException("Scheduler is mandatory parameter !"); + } + + if (maxPartitions == null || maxPartitions < 1) { + this.maxPartitions = panelType.getPartitions(); + } + + if (maxZones == null || maxZones < 1) { + this.maxZones = panelType.getZones(); + } + + try { + return new EvoCommunicator(ipAddress, tcpPort, ip150Password, pcPassword, scheduler, panelType, + maxPartitions, maxZones); + } catch (IOException e) { + logger.warn("Unable to create communicator for Panel={}. Message={}", panelType, e.getMessage()); + throw new ParadoxRuntimeException(e); + } + } + + @Override + public ICommunicatorBuilder withMaxZones(Integer maxZones) { + this.maxZones = maxZones; + return this; + } + + @Override + public ICommunicatorBuilder withMaxPartitions(Integer maxPartitions) { + this.maxPartitions = maxPartitions; + return this; + } + + @Override + public ICommunicatorBuilder withIp150Password(String ip150Password) { + this.ip150Password = ip150Password; + return this; + } + + @Override + public ICommunicatorBuilder withPcPassword(String pcPassword) { + this.pcPassword = pcPassword; + return this; + } + + @Override + public ICommunicatorBuilder withIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + return this; + } + + @Override + public ICommunicatorBuilder withTcpPort(Integer tcpPort) { + this.tcpPort = tcpPort; + return this; + } + + @Override + public ICommunicatorBuilder withScheduler(ScheduledExecutorService scheduler) { + this.scheduler = scheduler; + return this; + } + } } diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ICommunicatorBuilder.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ICommunicatorBuilder.java new file mode 100644 index 0000000000000..2d2600dc4ea40 --- /dev/null +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ICommunicatorBuilder.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.paradoxalarm.internal.communication; + +import java.util.concurrent.ScheduledExecutorService; + +/** + * The {@link ICommunicatorBuilder} is representing the functionality of communicator builders. + * The idea is to ease initialization of communicators which can have lots of parameters. + * + * @author Konstantin Polihronov - Initial contribution + */ +public interface ICommunicatorBuilder { + ICommunicatorBuilder withMaxZones(Integer zones); + + ICommunicatorBuilder withMaxPartitions(Integer partitions); + + ICommunicatorBuilder withIp150Password(String ip150Password); + + ICommunicatorBuilder withPcPassword(String pcPassword); + + ICommunicatorBuilder withIpAddress(String ipAddress); + + ICommunicatorBuilder withTcpPort(Integer tcpPort); + + ICommunicatorBuilder withScheduler(ScheduledExecutorService scheduler); + + IParadoxCommunicator build(); +} diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxBuilderFactory.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxBuilderFactory.java new file mode 100644 index 0000000000000..b6b24e68923df --- /dev/null +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxBuilderFactory.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.paradoxalarm.internal.communication; + +import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxRuntimeException; +import org.openhab.binding.paradoxalarm.internal.model.PanelType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link ParadoxBuilderFactory} used to create the proper communicator builder objects for different panel + * types. + * + * @author Konstantin Polihronov - Initial contribution + */ +public class ParadoxBuilderFactory { + + private final Logger logger = LoggerFactory.getLogger(ParadoxBuilderFactory.class); + + public ICommunicatorBuilder createBuilder(PanelType panelType) { + switch (panelType) { + case EVO48: + case EVO96: + case EVO192: + logger.debug("Creating new builder for Paradox {} system", panelType); + return new EvoCommunicator.EvoCommunicatorBuilder(panelType); + default: + throw new ParadoxRuntimeException("Unsupported panel type: " + panelType); + } + } +} diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxCommunicatorFactory.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxCommunicatorFactory.java deleted file mode 100644 index 9cf00fdaec1b8..0000000000000 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/communication/ParadoxCommunicatorFactory.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.paradoxalarm.internal.communication; - -import java.io.IOException; -import java.net.UnknownHostException; -import java.util.concurrent.ScheduledExecutorService; - -import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxException; -import org.openhab.binding.paradoxalarm.internal.model.PanelType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link ParadoxCommunicatorFactory} used to create the proper communication implementation objects based on panel - * type. - * - * @author Konstantin Polihronov - Initial contribution - */ -public class ParadoxCommunicatorFactory { - - private final Logger logger = LoggerFactory.getLogger(ParadoxCommunicatorFactory.class); - - private String ipAddress; - private int tcpPort; - private String ip150Password; - private String pcPassword; - private ScheduledExecutorService scheduler; - - public ParadoxCommunicatorFactory(String ipAddress, int tcpPort, String ip150Password, String pcPassword, - ScheduledExecutorService scheduler) { - this.ipAddress = ipAddress; - this.tcpPort = tcpPort; - this.ip150Password = ip150Password; - this.pcPassword = pcPassword; - this.scheduler = scheduler; - } - - public IParadoxCommunicator createCommunicator(String panelTypeStr) { - PanelType panelType = PanelType.from(panelTypeStr); - try { - return createCommunicator(panelType); - } catch (IOException | ParadoxException e) { - logger.warn("Unable to create communicator for Panel {}. Exception={}", panelTypeStr, e.getMessage()); - return null; - } - } - - public IParadoxCommunicator createCommunicator(PanelType panelType) - throws UnknownHostException, IOException, ParadoxException { - switch (panelType) { - case EVO48: - case EVO96: - case EVO192: - logger.debug("Creating new communicator for Paradox {} system", panelType); - return new EvoCommunicator(ipAddress, tcpPort, ip150Password, pcPassword, scheduler, panelType); - default: - throw new ParadoxException("Unsupported panel type: " + panelType); - } - } -} diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxAlarmBindingConstants.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxAlarmBindingConstants.java index 5e6e1a15522f5..13203997792f0 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxAlarmBindingConstants.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxAlarmBindingConstants.java @@ -42,14 +42,14 @@ public class ParadoxAlarmBindingConstants { // List of all Thing Type UIDs public static final ThingTypeUID COMMUNICATOR_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, - PARADOX_COMMUNICATOR_THING_TYPE_ID); + PARADOX_COMMUNICATOR_THING_TYPE_ID); public static final ThingTypeUID PANEL_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, PARADOX_PANEL_THING_TYPE_ID); public static final ThingTypeUID PARTITION_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, PARTITION_THING_TYPE_ID); public static final ThingTypeUID ZONE_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, ZONE_THING_TYPE_ID); public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( - Stream.of(COMMUNICATOR_THING_TYPE_UID, PANEL_THING_TYPE_UID, PARTITION_THING_TYPE_UID, ZONE_THING_TYPE_UID) - .collect(Collectors.toSet())); + Stream.of(COMMUNICATOR_THING_TYPE_UID, PANEL_THING_TYPE_UID, PARTITION_THING_TYPE_UID, ZONE_THING_TYPE_UID) + .collect(Collectors.toSet())); // List of all Channel UIDs public static final String IP150_COMMUNICATION_COMMAND_CHANNEL_UID = "communicationCommand"; @@ -61,9 +61,26 @@ public class ParadoxAlarmBindingConstants { public static final String PANEL_APPLICATION_VERSION_PROPERTY_NAME = "applicationVersion"; public static final String PANEL_BOOTLOADER_VERSION_PROPERTY_NAME = "bootloaderVersion"; - public static final String PARTITION_ADDITIONAL_STATES_CHANNEL_UID = "additionalStates"; - public static final String PARTITION_STATE_CHANNEL_UID = "state"; public static final String PARTITION_LABEL_CHANNEL_UID = "partitionLabel"; + public static final String PARTITION_STATE_CHANNEL_UID = "state"; + @Deprecated // After implementation of channels for every possible state, the summarized additional states is no + // longer needed. We'll keep it for backward compatibility + public static final String PARTITION_ADDITIONAL_STATES_CHANNEL_UID = "additionalStates"; + public static final String PARTITION_READY_TO_ARM_CHANNEL_UID = "readyToArm"; + public static final String PARTITION_IN_EXIT_DELAY_CHANNEL_UID = "inExitDelay"; + public static final String PARTITION_IN_ENTRY_DELAY_CHANNEL_UID = "inEntryDelay"; + public static final String PARTITION_IN_TROUBLE_CHANNEL_UID = "inTrouble"; + public static final String PARTITION_ALARM_IN_MEMORY_CHANNEL_UID = "alarmInMemory"; + public static final String PARTITION_ZONE_BYPASS_CHANNEL_UID = "zoneBypass"; + public static final String PARTITION_ZONE_IN_TAMPER_CHANNEL_UID = "zoneInTamperTrouble"; + public static final String PARTITION_ZONE_IN_LOW_BATTERY_CHANNEL_UID = "zoneInLowBatteryTrouble"; + public static final String PARTITION_ZONE_IN_FIRE_LOOP_CHANNEL_UID = "zoneInFireLoopTrouble"; + public static final String PARTITION_ZONE_IN_SUPERVISION_TROUBLE_CHANNEL_UID = "zoneInSupervisionTrouble"; + public static final String PARTITION_STAY_INSTANT_READY_CHANNEL_UID = "stayInstantReady"; + public static final String PARTITION_FORCE_READY_CHANNEL_UID = "forceReady"; + public static final String PARTITION_BYPASS_READY_CHANNEL_UID = "bypassReady"; + public static final String PARTITION_INHIBIT_READY_CHANNEL_UID = "inhibitReady"; + public static final String PARTITION_ALL_ZONES_CLOSED_CHANNEL_UID = "allZonesClosed"; public static final String ZONE_LABEL_CHANNEL_UID = "zoneLabel"; public static final String ZONE_OPENED_CHANNEL_UID = "opened"; diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeConfiguration.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeConfiguration.java index 34288cfbbfefd..58e5c4f6d95bd 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeConfiguration.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeConfiguration.java @@ -26,6 +26,8 @@ public class ParadoxIP150BridgeConfiguration { private int port; private String panelType; private int reconnectWaitTime; + private Integer maxZones; + private Integer maxPartitions; public int getRefresh() { return refresh; @@ -83,4 +85,20 @@ public void setReconnectWaitTime(int reconnectWaitTime) { this.reconnectWaitTime = reconnectWaitTime; } + public Integer getMaxZones() { + return maxZones; + } + + public void setMaxZones(Integer maxZones) { + this.maxZones = maxZones; + } + + public Integer getMaxPartitions() { + return maxPartitions; + } + + public void setMaxPartitions(Integer maxPartitions) { + this.maxPartitions = maxPartitions; + } + } diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeHandler.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeHandler.java index 4ac86e9577668..31dfe266ed03d 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeHandler.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxIP150BridgeHandler.java @@ -35,12 +35,13 @@ import org.eclipse.smarthome.core.types.RefreshType; import org.openhab.binding.paradoxalarm.internal.communication.CommunicationState; import org.openhab.binding.paradoxalarm.internal.communication.GenericCommunicator; +import org.openhab.binding.paradoxalarm.internal.communication.ICommunicatorBuilder; import org.openhab.binding.paradoxalarm.internal.communication.IDataUpdateListener; import org.openhab.binding.paradoxalarm.internal.communication.IP150Command; import org.openhab.binding.paradoxalarm.internal.communication.IParadoxCommunicator; import org.openhab.binding.paradoxalarm.internal.communication.IParadoxInitialLoginCommunicator; import org.openhab.binding.paradoxalarm.internal.communication.ISocketTimeOutListener; -import org.openhab.binding.paradoxalarm.internal.communication.ParadoxCommunicatorFactory; +import org.openhab.binding.paradoxalarm.internal.communication.ParadoxBuilderFactory; import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxRuntimeException; import org.openhab.binding.paradoxalarm.internal.model.PanelType; import org.openhab.binding.paradoxalarm.internal.model.ParadoxInformationConstants; @@ -146,21 +147,16 @@ private synchronized void doPostOnlineTask(IParadoxInitialLoginCommunicator init protected void createDiscoveredCommunicatorJob(PanelType panelType) { // If not detected properly, use the value from config - String panelTypeStr; - if (panelType != PanelType.UNKNOWN) { - panelTypeStr = panelType.name(); - } else { - panelTypeStr = config.getPanelType(); + if (panelType == PanelType.UNKNOWN) { + panelType = PanelType.from(config.getPanelType()); } logger.debug("Phase2 - Creating communicator for panel {}", panelType); - String ipAddress = config.getIpAddress(); - int tcpPort = config.getPort(); - String ip150Password = config.getIp150Password(); - String pcPassword = config.getPcPassword(); - ParadoxCommunicatorFactory factory = new ParadoxCommunicatorFactory(ipAddress, tcpPort, ip150Password, - pcPassword, scheduler); - communicator = factory.createCommunicator(panelTypeStr); + ICommunicatorBuilder builder = new ParadoxBuilderFactory().createBuilder(panelType); + communicator = builder.withIp150Password(config.getIp150Password()).withPcPassword(config.getPcPassword()) + .withIpAddress(config.getIpAddress()).withTcpPort(config.getPort()) + .withMaxPartitions(config.getMaxPartitions()).withMaxZones(config.getMaxZones()) + .withScheduler(scheduler).build(); ParadoxPanel panel = ParadoxPanel.getInstance(); panel.setCommunicator(communicator); diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxPartitionHandler.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxPartitionHandler.java index 532439dc59efb..27e24daea6d42 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxPartitionHandler.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/handlers/ParadoxPartitionHandler.java @@ -17,6 +17,8 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.thing.Thing; import org.openhab.binding.paradoxalarm.internal.model.ParadoxPanel; @@ -46,7 +48,40 @@ protected void updateEntity() { updateState(PARTITION_LABEL_CHANNEL_UID, new StringType(partition.getLabel())); updateState(PARTITION_STATE_CHANNEL_UID, new StringType(partition.getState().getMainState())); updateState(PARTITION_ADDITIONAL_STATES_CHANNEL_UID, - new StringType(partition.getState().getAdditionalState())); + new StringType("Deprecated field. Use direct channels instead")); + updateState(PARTITION_READY_TO_ARM_CHANNEL_UID, booleanToSwitchState(partition.getState().isReadyToArm())); + updateState(PARTITION_IN_EXIT_DELAY_CHANNEL_UID, + booleanToSwitchState(partition.getState().isInExitDelay())); + updateState(PARTITION_IN_ENTRY_DELAY_CHANNEL_UID, + booleanToSwitchState(partition.getState().isInEntryDelay())); + updateState(PARTITION_IN_TROUBLE_CHANNEL_UID, booleanToSwitchState(partition.getState().isInTrouble())); + updateState(PARTITION_ALARM_IN_MEMORY_CHANNEL_UID, + booleanToSwitchState(partition.getState().isHasAlarmInMemory())); + updateState(PARTITION_ZONE_BYPASS_CHANNEL_UID, booleanToSwitchState(partition.getState().isInZoneBypass())); + updateState(PARTITION_ZONE_IN_TAMPER_CHANNEL_UID, + booleanToSwitchState(partition.getState().isHasZoneInTamperTrouble())); + updateState(PARTITION_ZONE_IN_LOW_BATTERY_CHANNEL_UID, + booleanToSwitchState(partition.getState().isHasZoneInLowBatteryTrouble())); + updateState(PARTITION_ZONE_IN_FIRE_LOOP_CHANNEL_UID, + booleanToSwitchState(partition.getState().isHasZoneInFireLoopTrouble())); + updateState(PARTITION_ZONE_IN_SUPERVISION_TROUBLE_CHANNEL_UID, + booleanToSwitchState(partition.getState().isHasZoneInSupervisionTrouble())); + updateState(PARTITION_STAY_INSTANT_READY_CHANNEL_UID, + booleanToSwitchState(partition.getState().isStayInstantReady())); + updateState(PARTITION_FORCE_READY_CHANNEL_UID, booleanToSwitchState(partition.getState().isForceReady())); + updateState(PARTITION_BYPASS_READY_CHANNEL_UID, booleanToSwitchState(partition.getState().isBypassReady())); + updateState(PARTITION_INHIBIT_READY_CHANNEL_UID, + booleanToSwitchState(partition.getState().isInhibitReady())); + updateState(PARTITION_ALL_ZONES_CLOSED_CHANNEL_UID, + booleanToContactState(partition.getState().isAreAllZoneclosed())); } } + + private OpenClosedType booleanToContactState(boolean value) { + return value ? OpenClosedType.OPEN : OpenClosedType.CLOSED; + } + + private OnOffType booleanToSwitchState(boolean value) { + return value ? OnOffType.ON : OnOffType.OFF; + } } diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/ParadoxPanel.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/ParadoxPanel.java index 4002e8b465970..32db382dc4edc 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/ParadoxPanel.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/ParadoxPanel.java @@ -33,7 +33,7 @@ public class ParadoxPanel implements IDataUpdateListener { private final Logger logger = LoggerFactory.getLogger(ParadoxPanel.class); - private static ParadoxPanel paradoxPanel; + private static ParadoxPanel paradoxPanel = new ParadoxPanel(); private ParadoxInformation panelInformation; private List partitions; @@ -56,18 +56,11 @@ public void createModelEntities() { updateEntitiesStates(); } else { throw new ParadoxRuntimeException( - "Unsupported panel type. Type: " + panelInformation.getPanelType().name()); + "Unsupported panel type. Type: " + panelInformation.getPanelType().name()); } } public static ParadoxPanel getInstance() { - if (paradoxPanel == null) { - synchronized (ParadoxPanel.class) { - if (paradoxPanel == null) { - paradoxPanel = new ParadoxPanel(); - } - } - } return paradoxPanel; } diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/Partition.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/Partition.java index d653bf2fd1b93..6147c8b6bc9cf 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/Partition.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/Partition.java @@ -37,8 +37,7 @@ public PartitionState getState() { public Partition setState(PartitionState state) { this.state = state; - logger.debug("Partition {}:\t{},\tAdditional:{}", getLabel(), getState().getMainState(), - getState().getAdditionalState()); + logger.debug("Partition {}:\t{}", getLabel(), getState().getMainState()); return this; } } diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/PartitionState.java b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/PartitionState.java index fd169bb746006..d10292ac718e8 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/PartitionState.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/java/org/openhab/binding/paradoxalarm/internal/model/PartitionState.java @@ -50,73 +50,10 @@ public String getMainState() { if (isInAlarm) { return "InAlarm"; } else { - return isArmed ? "Armed" : "Disarmed"; + return isArmed || isArmedInAway || isArmedInStay || isArmedInNoEntry ? "Armed" : "Disarmed"; } } - public String getAdditionalState() { - StringBuilder sb = new StringBuilder(); - if (isInAlarm) { - append(sb, "In alarm"); - } else if (isInSilentAlarm) { - append(sb, "Silent alarm"); - } else if (isInAudibleAlarm) { - append(sb, "Audible alarm"); - } else if (isInFireAlarm) { - append(sb, "Fire alarm"); - } - - if (isReadyToArm) { - append(sb, "Ready to arm"); - } - if (isInTrouble) { - append(sb, "Trouble"); - } - if (hasAlarmInMemory) { - append(sb, "Alarm in memory"); - } - if (isInZoneBypass) { - append(sb, "Zone bypassed"); - } - - if (hasZoneInTamperTrouble) { - append(sb, "Tamper trouble"); - } - if (hasZoneInLowBatteryTrouble) { - append(sb, "Low battery trouble"); - } - if (hasZoneInFireLoopTrouble) { - append(sb, "Fire Loop trouble"); - } - if (hasZoneInSupervisionTrouble) { - append(sb, "Supervision trouble"); - } - - if (isStayInstantReady) { - append(sb, "Stay instant ready"); - } - if (isForceReady) { - append(sb, "Force ready"); - } - if (isBypassReady) { - append(sb, "Bypass ready"); - } - if (isInhibitReady) { - append(sb, "Inhibit ready"); - } - - if (areAllZoneclosed) { - append(sb, "All zones closed"); - } - - return sb.toString(); - } - - public void append(StringBuilder sb, String value) { - sb.append(value); - sb.append(";"); - } - @Override public String toString() { return "PartitionState [isArmed=" + isArmed + ", isArmedInAway=" + isArmedInAway + ", isArmedInStay=" diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/ip150connector.xml b/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/ip150connector.xml index bc0c2d0334e3d..f138639905b5a 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/ip150connector.xml +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/ip150connector.xml @@ -48,6 +48,16 @@ The time to wait before a reconnect occurs after socket timeout. Value is in seconds 30 + + + Maximum number of configured zones to check (from zone 1 to maxZones) + 0 + + + + Maximum number of configured partitions to check (from partition 1 to maxPartitions) + 0 + diff --git a/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/partition.xml b/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/partition.xml index 2bf774e77f780..57d3041f9ce5d 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/partition.xml +++ b/bundles/org.openhab.binding.paradoxalarm/src/main/resources/ESH-INF/thing/partition.xml @@ -16,6 +16,21 @@ + + + + + + + + + + + + + + + @@ -30,19 +45,108 @@ String Label of partition - + String State of partition - + String - Additional states provided by panel - + Additional states provided by panel (deprecated channel) + + + + switch + + Partition ready to arm + + + + switch + + Partition in Exit Delay + + + + switch + + Partition in Entry Delay + + + + switch + + Partition in Trouble + + + + switch + + Partition has Alarm in Memory + + + + switch + + Partition has Zone Bypass + + + + switch + + Partition has in Tamper Trouble + + + + switch + + Partition has in Low Battery Trouble + + + + switch + + Partition has in Fire Loop Trouble + + + + switch + + Partition has in Supervision Trouble + + + + switch + + Partition is Stay Instant Ready + + + + switch + + Partition is Force Ready + + + + switch + + Partition is Bypass Ready + + + + switch + + Partition is Inhibit Ready + + + + contact + + Partition has All Zones closed + - diff --git a/bundles/org.openhab.binding.paradoxalarm/src/test/java/main/Main.java b/bundles/org.openhab.binding.paradoxalarm/src/test/java/main/Main.java index 8d652f294809a..ee2a54e66e42c 100644 --- a/bundles/org.openhab.binding.paradoxalarm/src/test/java/main/Main.java +++ b/bundles/org.openhab.binding.paradoxalarm/src/test/java/main/Main.java @@ -21,9 +21,11 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import org.openhab.binding.paradoxalarm.internal.communication.ICommunicatorBuilder; import org.openhab.binding.paradoxalarm.internal.communication.IParadoxCommunicator; -import org.openhab.binding.paradoxalarm.internal.communication.ParadoxCommunicatorFactory; +import org.openhab.binding.paradoxalarm.internal.communication.ParadoxBuilderFactory; import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxException; +import org.openhab.binding.paradoxalarm.internal.model.PanelType; import org.openhab.binding.paradoxalarm.internal.model.ParadoxPanel; import org.openhab.binding.paradoxalarm.internal.model.Partition; import org.openhab.binding.paradoxalarm.internal.model.Zone; @@ -50,8 +52,6 @@ public class Main { private static ScheduledExecutorService scheduler; - private static final String PANEL_TYPE = "EVO192"; - private static final String LOG_LEVEL = "DEBUG"; private static IParadoxCommunicator communicator; @@ -63,17 +63,21 @@ public static void main(String[] args) { try { scheduler = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); + + ParadoxBuilderFactory factory = new ParadoxBuilderFactory(); + ICommunicatorBuilder builder = factory.createBuilder(PanelType.EVO192); + communicator = builder.withIp150Password(ip150Password).withPcPassword(pcPassword).withIpAddress(ipAddress) + .withTcpPort(port).withMaxPartitions(4).withMaxZones(60).withScheduler(scheduler).build(); + ParadoxPanel panel = ParadoxPanel.getInstance(); - ParadoxCommunicatorFactory factory = new ParadoxCommunicatorFactory(ipAddress, port, ip150Password, - pcPassword, scheduler); - communicator = factory.createCommunicator(PANEL_TYPE); + panel.setCommunicator(communicator); + communicator.setListeners(Arrays.asList(panel)); panel.setCommunicator(communicator); // scheduler.schedule(communicator::initializeEpromData, 2, TimeUnit.SECONDS); // scheduler.schedule(communicator::refreshMemoryMap, 2, TimeUnit.SECONDS); - logger.debug("Phase1 - Identify communicator"); scheduler.scheduleWithFixedDelay(() -> { updateDataCache(communicator, false); @@ -113,10 +117,12 @@ private static List initializePartitions(IParadoxCommunicator paradox } private static void readArguments(String[] args) { - if (args == null || args.length < 8 || !"--password".equals(args[0]) || args[1] == null || args[1].isEmpty() || args[2] == null || !"--pc_password".equals(args[2]) || args[3] == null || - args[3].isEmpty() || !"--ip_address".equals(args[4]) || args[5] == null || args[5].isEmpty() || !"--port".equals(args[6]) || args[7] == null || args[7].isEmpty()) { + if (args == null || args.length < 8 || !"--password".equals(args[0]) || args[1] == null || args[1].isEmpty() + || args[2] == null || !"--pc_password".equals(args[2]) || args[3] == null || args[3].isEmpty() + || !"--ip_address".equals(args[4]) || args[5] == null || args[5].isEmpty() || !"--port".equals(args[6]) + || args[7] == null || args[7].isEmpty()) { logger.error( - "Usage: application --password --pc_password --ip_address
--port \n (pc password default is 0000, can be obtained by checking section 3012), default port is 10000"); + "Usage: application --password --pc_password --ip_address
--port \n (pc password default is 0000, can be obtained by checking section 3012), default port is 10000"); System.exit(0); } else { logger.info("Arguments retrieved successfully from CLI.");