mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 23:22:02 +01:00
[paradoxalarm] Implement zone bypass command and additional zone states (#14557)
* Reduce warnings 1 Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * 2 Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * 3 Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Initial files and package refactoring Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Implemented zone commands without checksum calculation Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * More stuff * Added the checksum functionality * Added more examples to the test Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Implement ZoneCommand and necessary classes * Refactor the common logic * Extract interface Command * CHange the Response class to use Switch/case Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Fully implement the test for creating zone command payload Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Fix build / add headers and author to the new files Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Add command handling to the zone handler Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Add command channel to the Zone thing Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Research of zone states and some TODO notes Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Retrieval of zone special states from the panel Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Fix build Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Add the new channels to the metadata file Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Add new channels to zone handler Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Fix indexing in memory map and add more logging Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Refactoring and potential NPE access fixes Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Add new property "label" to the discovered zones and partitions Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Fix zone command issues * Fix checksum creation * Fix the parse and confirmation of the response Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Add the new channels to the README.md Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Fixed issue with not updating new channels in the zones * A silly copy/paste mistake Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Change the type of the new channels from contact to switch As per community discussion this makes more sense and will be more intuitive - when something is true -> make it ON, when it's false -> make it OFF. OPEN and CLOSED are not fitting so well here... Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Fix issue that the channel label is always NULL * For both zone and partitions Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> * Add new types and channels to the i18n Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> --------- Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
This commit is contained in:
parent
51f3bf6b86
commit
33dd5e7f70
@ -97,11 +97,21 @@ Currently binding supports the following panels: EVO192, EVO48(not tested), EVO9
|
|||||||
|
|
||||||
### Zone channels:
|
### Zone channels:
|
||||||
|
|
||||||
| Channel | Type | Description |
|
| Channel | Type | Description |
|
||||||
|-----------------|---------|--------------------------------------------------------------------------------|
|
|------------------------------------|---------|--------------------------------------------------------------------------------|
|
||||||
| zoneLabel | String | Label of zone inside Paradox configuration |
|
| zoneLabel | String | Label of zone inside Paradox configuration |
|
||||||
| openedState | Contact | Zone opened / closed |
|
| openedState | Contact | Zone opened / closed |
|
||||||
| tamperedState | Switch | Zone is tampered / not tampered |
|
| tamperedState | Switch | Zone is tampered |
|
||||||
|
| supervisionTrouble | Switch | Zone is in supervision trouble |
|
||||||
|
| inTxDelay | Switch | Zone is in txDelay |
|
||||||
|
| shutdown | Switch | Zone is shutdown |
|
||||||
|
| bypassed | Switch | Zone is bypassed |
|
||||||
|
| hasActivatedIntellizoneDelay | Switch | Zone is has an activated Intellizone delay |
|
||||||
|
| hasActivatedEntryDelay | Switch | Zone is has an activated entry delay |
|
||||||
|
| presentlyInAlarm | Switch | Zone is currently in alarm |
|
||||||
|
| generatedAlarm | Switch | Zone has generated an alarm |
|
||||||
|
| command | String | Command for zone. Can be (BYPASS, CLEAR_BYPASS) |
|
||||||
|
|
||||||
|
|
||||||
## Example things configuration
|
## Example things configuration
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
|||||||
if (payload != null && payload.length >= RAM_BLOCK_SIZE) {
|
if (payload != null && payload.length >= RAM_BLOCK_SIZE) {
|
||||||
RamRequest request = (RamRequest) response.getRequest();
|
RamRequest request = (RamRequest) response.getRequest();
|
||||||
int ramBlockNumber = request.getRamBlockNumber();
|
int ramBlockNumber = request.getRamBlockNumber();
|
||||||
memoryMap.updateElement(ramBlockNumber, payload);
|
memoryMap.updateElement(ramBlockNumber - 1, payload);
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Result for ramBlock={} is [{}]", ramBlockNumber, ParadoxUtil.byteArrayToString(payload));
|
logger.trace("Result for ramBlock={} is [{}]", ramBlockNumber, ParadoxUtil.byteArrayToString(payload));
|
||||||
}
|
}
|
||||||
@ -176,7 +176,21 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
|||||||
|
|
||||||
byte[] firstPage = memoryMap.getElement(0);
|
byte[] firstPage = memoryMap.getElement(0);
|
||||||
byte[] secondPage = memoryMap.getElement(8);
|
byte[] secondPage = memoryMap.getElement(8);
|
||||||
|
createZoneOpenedFlags(result, firstPage, secondPage);
|
||||||
|
createZoneTamperedFlags(result, firstPage, secondPage);
|
||||||
|
createZoneLowbatteryFlags(result, firstPage, secondPage);
|
||||||
|
|
||||||
|
createSpecialZoneFlags(result, memoryMap);
|
||||||
|
|
||||||
|
ParadoxUtil.printByteArray("Zone opened flags", result.getZonesOpened());
|
||||||
|
ParadoxUtil.printByteArray("Zone tampered flags", result.getZonesTampered());
|
||||||
|
ParadoxUtil.printByteArray("Zone low battery flags", result.getZonesLowBattery());
|
||||||
|
ParadoxUtil.printByteArray("Zone special flags", result.getZoneSpecialFlags());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createZoneOpenedFlags(ZoneStateFlags result, byte[] firstPage, byte[] secondPage) {
|
||||||
int pageOffset = panelType == PanelType.EVO48 ? 34 : 40;
|
int pageOffset = panelType == PanelType.EVO48 ? 34 : 40;
|
||||||
byte[] firstBlock = Arrays.copyOfRange(firstPage, 28, pageOffset);
|
byte[] firstBlock = Arrays.copyOfRange(firstPage, 28, pageOffset);
|
||||||
if (panelType != PanelType.EVO192) {
|
if (panelType != PanelType.EVO192) {
|
||||||
@ -186,9 +200,11 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
|||||||
byte[] zonesOpened = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
byte[] zonesOpened = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
||||||
result.setZonesOpened(zonesOpened);
|
result.setZonesOpened(zonesOpened);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pageOffset = panelType == PanelType.EVO48 ? 46 : 52;
|
private void createZoneTamperedFlags(ZoneStateFlags result, byte[] firstPage, byte[] secondPage) {
|
||||||
firstBlock = Arrays.copyOfRange(firstPage, 40, pageOffset);
|
int pageOffset = panelType == PanelType.EVO48 ? 46 : 52;
|
||||||
|
byte[] firstBlock = Arrays.copyOfRange(firstPage, 40, pageOffset);
|
||||||
if (panelType != PanelType.EVO192) {
|
if (panelType != PanelType.EVO192) {
|
||||||
result.setZonesTampered(firstBlock);
|
result.setZonesTampered(firstBlock);
|
||||||
} else {
|
} else {
|
||||||
@ -196,18 +212,51 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
|||||||
byte[] zonesTampered = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
byte[] zonesTampered = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
||||||
result.setZonesTampered(zonesTampered);
|
result.setZonesTampered(zonesTampered);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pageOffset = panelType == PanelType.EVO48 ? 58 : 64;
|
private void createZoneLowbatteryFlags(ZoneStateFlags result, byte[] firstPage, byte[] secondPage) {
|
||||||
firstBlock = Arrays.copyOfRange(firstPage, 52, pageOffset);
|
int pageOffset = panelType == PanelType.EVO48 ? 58 : 64;
|
||||||
|
byte[] firstBlock = Arrays.copyOfRange(firstPage, 52, pageOffset);
|
||||||
if (panelType != PanelType.EVO192) {
|
if (panelType != PanelType.EVO192) {
|
||||||
result.setZonesTampered(firstBlock);
|
result.setZonesLowBattery(firstBlock);
|
||||||
} else {
|
} else {
|
||||||
byte[] secondBlock = Arrays.copyOfRange(secondPage, 24, 36);
|
byte[] secondBlock = Arrays.copyOfRange(secondPage, 24, 36);
|
||||||
byte[] zonesLowBattery = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
byte[] zonesLowBattery = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
||||||
result.setZonesLowBattery(zonesLowBattery);
|
result.setZonesLowBattery(zonesLowBattery);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
@SuppressWarnings("incomplete-switch")
|
||||||
|
private void createSpecialZoneFlags(ZoneStateFlags result, MemoryMap memoryMap) {
|
||||||
|
byte[] page2 = memoryMap.getElement(1);
|
||||||
|
byte[] page3 = memoryMap.getElement(2);
|
||||||
|
byte[] page7 = memoryMap.getElement(8);
|
||||||
|
byte[] page8 = memoryMap.getElement(9);
|
||||||
|
byte[] page9 = memoryMap.getElement(10);
|
||||||
|
|
||||||
|
switch (panelType) {
|
||||||
|
case EVO48:
|
||||||
|
byte[] firstBlock = Arrays.copyOfRange(page2, 0, 48);
|
||||||
|
result.setZoneSpecialFlags(firstBlock);
|
||||||
|
break;
|
||||||
|
case EVO96:
|
||||||
|
firstBlock = Arrays.copyOf(page2, 64);
|
||||||
|
byte[] secondBlock = Arrays.copyOfRange(page3, 0, 32);
|
||||||
|
byte[] specialZoneFlags = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
||||||
|
result.setZoneSpecialFlags(specialZoneFlags);
|
||||||
|
break;
|
||||||
|
case EVO192:
|
||||||
|
case EVOHD:
|
||||||
|
firstBlock = Arrays.copyOf(page2, 64);
|
||||||
|
secondBlock = Arrays.copyOfRange(page3, 0, 32);
|
||||||
|
byte[] thirdBlock = Arrays.copyOfRange(page7, 36, 64);
|
||||||
|
byte[] fourthBlock = Arrays.copyOf(page8, 64);
|
||||||
|
byte[] fifthBlock = Arrays.copyOfRange(page9, 0, 4);
|
||||||
|
specialZoneFlags = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock, thirdBlock, fourthBlock,
|
||||||
|
fifthBlock);
|
||||||
|
result.setZoneSpecialFlags(specialZoneFlags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initializeMemoryMap() {
|
public void initializeMemoryMap() {
|
||||||
|
@ -41,6 +41,6 @@ public class MemoryMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void updateElement(int index, byte[] elementValue) {
|
public synchronized void updateElement(int index, byte[] elementValue) {
|
||||||
ramCache.set(index - 1, elementValue);
|
ramCache.set(index, elementValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,5 +21,6 @@ public enum RequestType {
|
|||||||
LOGON_SEQUENCE,
|
LOGON_SEQUENCE,
|
||||||
RAM,
|
RAM,
|
||||||
EPROM,
|
EPROM,
|
||||||
PARTITION_COMMAND
|
PARTITION_COMMAND,
|
||||||
|
ZONE_COMMAND
|
||||||
}
|
}
|
||||||
|
@ -103,36 +103,52 @@ public class Response implements IResponse {
|
|||||||
byte highNibble = ParadoxUtil.getHighNibble(receivedCommand);
|
byte highNibble = ParadoxUtil.getHighNibble(receivedCommand);
|
||||||
RequestType requestType = request.getType();
|
RequestType requestType = request.getType();
|
||||||
|
|
||||||
// For EPROM and RAM messages received command must be 0x5x
|
switch (requestType) {
|
||||||
if (requestType == RequestType.EPROM || requestType == RequestType.RAM) {
|
// For EPROM and RAM messages received command must be 0x5x
|
||||||
if (highNibble == 0x5) {
|
case EPROM:
|
||||||
header = Arrays.copyOfRange(packetBytes, 0, 22);
|
case RAM:
|
||||||
payload = Arrays.copyOfRange(packetBytes, 22, packetBytes.length - 1);
|
if (highNibble == 0x5) {
|
||||||
return;
|
header = Arrays.copyOfRange(packetBytes, 0, 22);
|
||||||
}
|
payload = Arrays.copyOfRange(packetBytes, 22, packetBytes.length - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// For logon sequence packets there are various commands but their high nibbles should be either 0x0, 0x1 or
|
// For logon sequence packets there are various commands but their high nibbles should be either 0x0, 0x1 or
|
||||||
// 0x7
|
// 0x7
|
||||||
} else if (requestType == RequestType.LOGON_SEQUENCE) {
|
case LOGON_SEQUENCE:
|
||||||
switch (highNibble) {
|
switch (highNibble) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
case 0x1:
|
case 0x1:
|
||||||
case 0x7:
|
case 0x7:
|
||||||
|
header = Arrays.copyOfRange(packetBytes, 0, 16);
|
||||||
|
payload = Arrays.copyOfRange(packetBytes, 16, packetBytes.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARTITION_COMMAND:
|
||||||
|
if (highNibble == 0x4) {
|
||||||
header = Arrays.copyOfRange(packetBytes, 0, 16);
|
header = Arrays.copyOfRange(packetBytes, 0, 16);
|
||||||
payload = Arrays.copyOfRange(packetBytes, 16, packetBytes.length);
|
payload = Arrays.copyOfRange(packetBytes, 16, 16 + packetBytes[1]);
|
||||||
|
logger.debug("Received a valid response for partition command");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (requestType == RequestType.PARTITION_COMMAND) {
|
break;
|
||||||
if (highNibble == 0x4) {
|
|
||||||
header = Arrays.copyOfRange(packetBytes, 0, 16);
|
case ZONE_COMMAND:
|
||||||
payload = Arrays.copyOfRange(packetBytes, 16, 16 + packetBytes[1]);
|
if (highNibble == 0xD) {
|
||||||
logger.debug("Received valid response for partition command");
|
header = Arrays.copyOfRange(packetBytes, 0, 16);
|
||||||
return;
|
payload = Arrays.copyOfRange(packetBytes, 16, 16 + packetBytes[1]);
|
||||||
}
|
logger.debug("Received a valid response for zone command");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other cases are considered wrong results for the parser and are probably live events which cannot be
|
// All other cases are considered wrong results for the parser and are probably live events which cannot be
|
||||||
// parsed currently
|
// parsed currently
|
||||||
logger.debug("Message command not expected. Received command={}", receivedCommand);
|
logger.debug("Message command not expected. Received command={}", String.format("0x%08X", receivedCommand));
|
||||||
header = null;
|
header = null;
|
||||||
payload = null;
|
payload = null;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.communication.messages.IPPacket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
|
*/
|
||||||
|
public class ZoneCommandRequest extends Request {
|
||||||
|
|
||||||
|
public ZoneCommandRequest(RequestType type, IPPacket packet, IResponseReceiver receiver) {
|
||||||
|
super(type, packet, receiver);
|
||||||
|
}
|
||||||
|
}
|
@ -50,8 +50,8 @@ public class EncryptionHandler {
|
|||||||
private static final int PAYLOAD_RATE_LENGTH = 16;
|
private static final int PAYLOAD_RATE_LENGTH = 16;
|
||||||
private static final int ROUNDS = 14;
|
private static final int ROUNDS = 14;
|
||||||
|
|
||||||
private static final int[] lTable = new int[TABLE_SIZE];
|
private static final int[] L_TABLE = new int[TABLE_SIZE];
|
||||||
private static final int[] aTable = new int[TABLE_SIZE];
|
private static final int[] A_TABLE = new int[TABLE_SIZE];
|
||||||
|
|
||||||
private static EncryptionHandler instance = new EncryptionHandler(new byte[] {});
|
private static EncryptionHandler instance = new EncryptionHandler(new byte[] {});
|
||||||
static {
|
static {
|
||||||
@ -62,7 +62,7 @@ public class EncryptionHandler {
|
|||||||
int a = 1;
|
int a = 1;
|
||||||
int d;
|
int d;
|
||||||
for (int index = 0; index < 255; index++) {
|
for (int index = 0; index < 255; index++) {
|
||||||
aTable[index] = a & 0xFF;
|
A_TABLE[index] = a & 0xFF;
|
||||||
/* Multiply by three */
|
/* Multiply by three */
|
||||||
d = (a & 0x80) & 0xFF;
|
d = (a & 0x80) & 0xFF;
|
||||||
a <<= 1;
|
a <<= 1;
|
||||||
@ -70,13 +70,13 @@ public class EncryptionHandler {
|
|||||||
a ^= 0x1b;
|
a ^= 0x1b;
|
||||||
a &= 0xFF;
|
a &= 0xFF;
|
||||||
}
|
}
|
||||||
a ^= aTable[index];
|
a ^= A_TABLE[index];
|
||||||
a &= 0xFF;
|
a &= 0xFF;
|
||||||
/* Set the log table value */
|
/* Set the log table value */
|
||||||
lTable[aTable[index]] = index & 0xFF;
|
L_TABLE[A_TABLE[index]] = index & 0xFF;
|
||||||
}
|
}
|
||||||
aTable[255] = aTable[0];
|
A_TABLE[255] = A_TABLE[0];
|
||||||
lTable[0] = 0;
|
L_TABLE[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int[] expandedKey = new int[KEY_LENGTH];
|
private final int[] expandedKey = new int[KEY_LENGTH];
|
||||||
@ -196,9 +196,7 @@ public class EncryptionHandler {
|
|||||||
if (i % 8 == 0) {
|
if (i % 8 == 0) {
|
||||||
int tmp = temp[0];
|
int tmp = temp[0];
|
||||||
|
|
||||||
for (int j = 1; j < 4; j++) {
|
System.arraycopy(temp, 1, temp, 0, temp.length - 1);
|
||||||
temp[j - 1] = temp[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
temp[3] = tmp;
|
temp[3] = tmp;
|
||||||
temp[0] ^= EncryptionHandlerConstants.RCON[(i / 8 - 1)];
|
temp[0] ^= EncryptionHandlerConstants.RCON[(i / 8 - 1)];
|
||||||
@ -212,9 +210,9 @@ public class EncryptionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int gmul(int c, int b) {
|
private int gmul(int c, int b) {
|
||||||
int s = lTable[c] + lTable[b];
|
int s = L_TABLE[c] + L_TABLE[b];
|
||||||
s %= 255;
|
s %= 255;
|
||||||
s = aTable[s];
|
s = A_TABLE[s];
|
||||||
if (b == 0 || c == 0) {
|
if (b == 0 || c == 0) {
|
||||||
s = 0;
|
s = 0;
|
||||||
}
|
}
|
||||||
@ -267,8 +265,7 @@ public class EncryptionHandler {
|
|||||||
int[] tmpArray = new int[] { 0, 0, 0, 0 };
|
int[] tmpArray = new int[] { 0, 0, 0, 0 };
|
||||||
for (int i = 1; i < 4; i++) {
|
for (int i = 1; i < 4; i++) {
|
||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
int[][][] shifts = EncryptionHandlerConstants.SHIFTS;
|
int index = i * 4 + (j + EncryptionHandlerConstants.SHIFTS[0][i][d]) % 4;
|
||||||
int index = i * 4 + (j + shifts[0][i][d]) % 4;
|
|
||||||
tmpArray[j] = a[index];
|
tmpArray[j] = a[index];
|
||||||
}
|
}
|
||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.messages;
|
||||||
|
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.IRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* More generic interface for creating command requests
|
||||||
|
*
|
||||||
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
|
*/
|
||||||
|
public interface Command {
|
||||||
|
IRequest getRequest(int id);
|
||||||
|
}
|
@ -10,9 +10,16 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.paradoxalarm.internal.communication.messages;
|
package org.openhab.binding.paradoxalarm.internal.communication.messages.partition;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.IRequest;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.PartitionCommandRequest;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.RequestType;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.Command;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderMessageType;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.ParadoxIPPacket;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -23,8 +30,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public enum PartitionCommand {
|
public enum PartitionCommand implements Command {
|
||||||
UNKNOWN(0),
|
|
||||||
ARM(2),
|
ARM(2),
|
||||||
STAY_ARM(3),
|
STAY_ARM(3),
|
||||||
INSTANT_ARM(4),
|
INSTANT_ARM(4),
|
||||||
@ -32,7 +38,7 @@ public enum PartitionCommand {
|
|||||||
DISARM(6),
|
DISARM(6),
|
||||||
BEEP(8);
|
BEEP(8);
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PartitionCommand.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(PartitionCommand.class);
|
||||||
|
|
||||||
private int command;
|
private int command;
|
||||||
|
|
||||||
@ -44,12 +50,20 @@ public enum PartitionCommand {
|
|||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PartitionCommand parse(String command) {
|
public static @Nullable PartitionCommand parse(String command) {
|
||||||
try {
|
try {
|
||||||
return PartitionCommand.valueOf(command);
|
return PartitionCommand.valueOf(command);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
logger.debug("Unable to parse command={}. Fallback to UNKNOWN.", command);
|
LOGGER.debug("Unable to parse command={}. Fallback to UNKNOWN.", command);
|
||||||
return PartitionCommand.UNKNOWN;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRequest getRequest(int partitionId) {
|
||||||
|
PartitionCommandPayload payload = new PartitionCommandPayload(partitionId, this);
|
||||||
|
ParadoxIPPacket packet = new ParadoxIPPacket(payload.getBytes())
|
||||||
|
.setMessageType(HeaderMessageType.SERIAL_PASSTHRU_REQUEST);
|
||||||
|
return new PartitionCommandRequest(RequestType.PARTITION_COMMAND, packet, null);
|
||||||
|
}
|
||||||
}
|
}
|
@ -10,31 +10,32 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.paradoxalarm.internal.communication.messages;
|
package org.openhab.binding.paradoxalarm.internal.communication.messages.partition;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.IPayload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link CommandPayload} Class that structures the payload for partition commands.
|
* The {@link PartitionCommandPayload} Class that structures the payload for partition commands.
|
||||||
*
|
*
|
||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class CommandPayload implements IPayload {
|
public class PartitionCommandPayload implements IPayload {
|
||||||
|
|
||||||
private static final int BYTES_LENGTH = 15;
|
private static final int BYTES_LENGTH = 15;
|
||||||
|
|
||||||
private final byte MESSAGE_START = 0x40;
|
private static final byte MESSAGE_START = 0x40;
|
||||||
private final byte PAYLOAD_SIZE = 0x0f;
|
private static final byte PAYLOAD_SIZE = 0x0f;
|
||||||
private final byte[] EMPTY_FOUR_BYTES = { 0, 0, 0, 0 };
|
private static final byte[] EMPTY_FOUR_BYTES = { 0, 0, 0, 0 };
|
||||||
private final byte CHECKSUM = 0;
|
private static final byte CHECKSUM = 0;
|
||||||
|
|
||||||
private final int partitionNumber;
|
private final int partitionNumber;
|
||||||
private final PartitionCommand command;
|
private final PartitionCommand command;
|
||||||
|
|
||||||
public CommandPayload(int partitionNumber, PartitionCommand command) {
|
public PartitionCommandPayload(int partitionNumber, PartitionCommand command) {
|
||||||
this.partitionNumber = partitionNumber;
|
this.partitionNumber = partitionNumber;
|
||||||
this.command = command;
|
this.command = command;
|
||||||
}
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.messages.zone;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.IRequest;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.RequestType;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.ZoneCommandRequest;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.Command;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderMessageType;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.ParadoxIPPacket;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public enum ZoneCommand implements Command {
|
||||||
|
CLEAR_BYPASS(0),
|
||||||
|
BYPASS(8);
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(ZoneCommand.class);
|
||||||
|
|
||||||
|
private byte command;
|
||||||
|
|
||||||
|
ZoneCommand(int command) {
|
||||||
|
this.command = (byte) command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getCommand() {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable ZoneCommand parse(@Nullable String command) {
|
||||||
|
if (command == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ZoneCommand.valueOf(command);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOGGER.debug("Unable to parse command={}. Fallback to UNKNOWN.", command);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRequest getRequest(int zoneId) {
|
||||||
|
ZoneCommandPayload payload = new ZoneCommandPayload(zoneId, this);
|
||||||
|
ParadoxIPPacket packet = new ParadoxIPPacket(payload.getBytes(), false)
|
||||||
|
.setMessageType(HeaderMessageType.SERIAL_PASSTHRU_REQUEST);
|
||||||
|
return new ZoneCommandRequest(RequestType.ZONE_COMMAND, packet, null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.messages.zone;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.IPayload;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
|
*/
|
||||||
|
public class ZoneCommandPayload implements IPayload {
|
||||||
|
|
||||||
|
private static final int BYTES_LENGTH = 31;
|
||||||
|
|
||||||
|
private static final byte MESSAGE_START = (byte) 0xD0;
|
||||||
|
private static final byte PAYLOAD_SIZE = 0x1f;
|
||||||
|
private static final byte ZONE_FLAG = 0x08; // "bypassed" flag (5th bit)
|
||||||
|
private static final byte[] EMPTY_TWO_BYTES = { 0, 0 };
|
||||||
|
private static final byte CHECKSUM = 0;
|
||||||
|
|
||||||
|
private int zoneNumber;
|
||||||
|
private ZoneCommand command;
|
||||||
|
|
||||||
|
public ZoneCommandPayload(int zoneNumber, ZoneCommand command) {
|
||||||
|
this.zoneNumber = zoneNumber;
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() {
|
||||||
|
byte[] bufferArray = new byte[BYTES_LENGTH];
|
||||||
|
ByteBuffer buf = ByteBuffer.wrap(bufferArray);
|
||||||
|
buf.put(MESSAGE_START);
|
||||||
|
buf.put(PAYLOAD_SIZE);
|
||||||
|
buf.put(ZONE_FLAG);
|
||||||
|
buf.put(command.getCommand());
|
||||||
|
buf.put(EMPTY_TWO_BYTES);
|
||||||
|
buf.put(calculateMessageBytes());
|
||||||
|
buf.put(ParadoxUtil.calculateChecksum(bufferArray));
|
||||||
|
return bufferArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total zone message consists of 24 bytes (8 bits each) which results of 192 bits for each zone in case of
|
||||||
|
* Evo192 (i.e. 24x8).
|
||||||
|
* The low nible of each byte represents the first 4 zones of each group as a bit, the high nible is
|
||||||
|
* the second 4 zones. The zone groups are considered every 8 zones represented by a byte in this array (1-8,9-16,
|
||||||
|
* 17-24, etc)<br>
|
||||||
|
*
|
||||||
|
* Example: So if we address zone 1 for example the value of first byte will be 0x01, for zone 2 - 0x02, for zone 3
|
||||||
|
* - 0x04
|
||||||
|
* (third bit set to 1), for zone 4 - 0x08(fourth bit set to 1),<br>
|
||||||
|
* for zone 5 - 0x10, for zone 6 - 0x20, for zone 7 - 0x40, for zone 8 - 0x80.<br>
|
||||||
|
* For examples see TestGetBytes.java
|
||||||
|
*
|
||||||
|
* @return 24 bytes array with the needed zone to be set to bypass/clear bypass
|
||||||
|
*/
|
||||||
|
private byte[] calculateMessageBytes() {
|
||||||
|
byte[] zoneMessage = new byte[24];
|
||||||
|
int byteIndex = (zoneNumber - 1) / 8;
|
||||||
|
byte zoneByteGroup = zoneMessage[byteIndex];
|
||||||
|
int bitNumber = calculateBitNumber();
|
||||||
|
zoneMessage[byteIndex] = ParadoxUtil.setBit(zoneByteGroup, bitNumber - 1, 1);
|
||||||
|
return zoneMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateBitNumber() {
|
||||||
|
int residual = zoneNumber % 8;
|
||||||
|
return residual != 0 ? residual : 8;
|
||||||
|
}
|
||||||
|
}
|
@ -89,7 +89,7 @@ public class ParadoxDiscoveryService extends AbstractDiscoveryService {
|
|||||||
ThingUID thingUID = new ThingUID(PARTITION_THING_TYPE_UID, bridgeUid, thingId);
|
ThingUID thingUID = new ThingUID(PARTITION_THING_TYPE_UID, bridgeUid, thingId);
|
||||||
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUid)
|
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUid)
|
||||||
.withLabel("Partition " + label).withProperty(PARTITION_THING_TYPE_ID, thingId)
|
.withLabel("Partition " + label).withProperty(PARTITION_THING_TYPE_ID, thingId)
|
||||||
.withProperty("id", partition.getId()).build();
|
.withProperty("label", label).withProperty("id", partition.getId()).build();
|
||||||
logger.debug("Partition DiscoveryResult={}", result);
|
logger.debug("Partition DiscoveryResult={}", result);
|
||||||
|
|
||||||
thingDiscovered(result);
|
thingDiscovered(result);
|
||||||
@ -105,7 +105,7 @@ public class ParadoxDiscoveryService extends AbstractDiscoveryService {
|
|||||||
ThingUID thingUID = new ThingUID(ZONE_THING_TYPE_UID, bridgeUid, thingId);
|
ThingUID thingUID = new ThingUID(ZONE_THING_TYPE_UID, bridgeUid, thingId);
|
||||||
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUid)
|
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUid)
|
||||||
.withLabel("Zone " + label).withProperty(ZONE_THING_TYPE_ID, thingId)
|
.withLabel("Zone " + label).withProperty(ZONE_THING_TYPE_ID, thingId)
|
||||||
.withProperty("id", zone.getId()).build();
|
.withProperty("id", zone.getId()).withProperty("label", label).build();
|
||||||
logger.debug("Zone DiscoveryResult={}", result);
|
logger.debug("Zone DiscoveryResult={}", result);
|
||||||
|
|
||||||
thingDiscovered(result);
|
thingDiscovered(result);
|
||||||
|
@ -67,7 +67,7 @@ public class ParadoxAlarmBindingConstants {
|
|||||||
public static final String PANEL_BOARD_VOLTAGE = "boardVoltage";
|
public static final String PANEL_BOARD_VOLTAGE = "boardVoltage";
|
||||||
public static final String PANEL_BATTERY_VOLTAGE = "batteryVoltage";
|
public static final String PANEL_BATTERY_VOLTAGE = "batteryVoltage";
|
||||||
|
|
||||||
public static final String PARTITION_LABEL_CHANNEL_UID = "partitionLabel";
|
public static final String PARTITION_LABEL_CHANNEL_UID = "label";
|
||||||
public static final String PARTITION_STATE_CHANNEL_UID = "state";
|
public static final String PARTITION_STATE_CHANNEL_UID = "state";
|
||||||
@Deprecated // After implementation of channels for every possible state, the summarized additional states is no
|
@Deprecated // After implementation of channels for every possible state, the summarized additional states is no
|
||||||
// longer needed. We'll keep it for backward compatibility
|
// longer needed. We'll keep it for backward compatibility
|
||||||
@ -88,11 +88,20 @@ public class ParadoxAlarmBindingConstants {
|
|||||||
public static final String PARTITION_INHIBIT_READY_CHANNEL_UID = "inhibitReady";
|
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 PARTITION_ALL_ZONES_CLOSED_CHANNEL_UID = "allZonesClosed";
|
||||||
|
|
||||||
public static final String ZONE_LABEL_CHANNEL_UID = "zoneLabel";
|
public static final String ZONE_LABEL_CHANNEL_UID = "label";
|
||||||
public static final String ZONE_OPENED_CHANNEL_UID = "opened";
|
public static final String ZONE_OPENED_CHANNEL_UID = "opened";
|
||||||
public static final String ZONE_TAMPERED_CHANNEL_UID = "tampered";
|
public static final String ZONE_TAMPERED_CHANNEL_UID = "tampered";
|
||||||
public static final String ZONE_LOW_BATTERY_CHANNEL_UID = "lowBattery";
|
public static final String ZONE_LOW_BATTERY_CHANNEL_UID = "lowBattery";
|
||||||
|
|
||||||
|
public static final String ZONE_SUPERVISION_TROUBLE_UID = "supervisionTrouble";
|
||||||
|
public static final String ZONE_IN_TX_DELAY_UID = "inTxDelay";
|
||||||
|
public static final String ZONE_SHUTDOWN_UID = "shutdown";
|
||||||
|
public static final String ZONE_BYPASSED_UID = "bypassed";
|
||||||
|
public static final String ZONE_HAS_ACTIVATED_INTELLIZONE_DELAY_UID = "hasActivatedIntellizoneDelay";
|
||||||
|
public static final String ZONE_HAS_ACTIVATED_ENTRY_DELAY_UID = "hasActivatedEntryDelay";
|
||||||
|
public static final String ZONE_PRESENTLY_IN_ALARM_UID = "presentlyInAlarm";
|
||||||
|
public static final String ZONE_GENERATED_ALARM_UID = "generatedAlarm";
|
||||||
|
|
||||||
// Misc constants
|
// Misc constants
|
||||||
public static final StringType STATE_OFFLINE = new StringType("Offline");
|
public static final StringType STATE_OFFLINE = new StringType("Offline");
|
||||||
public static final StringType STATE_ONLINE = new StringType("Online");
|
public static final StringType STATE_ONLINE = new StringType("Online");
|
||||||
|
@ -24,8 +24,6 @@ import org.openhab.core.library.types.QuantityType;
|
|||||||
import org.openhab.core.library.types.StringType;
|
import org.openhab.core.library.types.StringType;
|
||||||
import org.openhab.core.library.unit.Units;
|
import org.openhab.core.library.unit.Units;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link ParadoxPanelHandler} This is the handler that takes care of the panel related stuff.
|
* The {@link ParadoxPanelHandler} This is the handler that takes care of the panel related stuff.
|
||||||
@ -35,8 +33,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class ParadoxPanelHandler extends EntityBaseHandler {
|
public class ParadoxPanelHandler extends EntityBaseHandler {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(ParadoxPanelHandler.class);
|
|
||||||
|
|
||||||
public ParadoxPanelHandler(Thing thing) {
|
public ParadoxPanelHandler(Thing thing) {
|
||||||
super(thing);
|
super(thing);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import static org.openhab.binding.paradoxalarm.internal.handlers.ParadoxAlarmBin
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.PartitionCommand;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.partition.PartitionCommand;
|
||||||
import org.openhab.binding.paradoxalarm.internal.model.ParadoxPanel;
|
import org.openhab.binding.paradoxalarm.internal.model.ParadoxPanel;
|
||||||
import org.openhab.binding.paradoxalarm.internal.model.Partition;
|
import org.openhab.binding.paradoxalarm.internal.model.Partition;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
@ -78,26 +78,6 @@ public class ParadoxPartitionHandler extends EntityBaseHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Partition getPartition() {
|
|
||||||
int index = calculateEntityIndex();
|
|
||||||
ParadoxIP150BridgeHandler bridge = (ParadoxIP150BridgeHandler) getBridge().getHandler();
|
|
||||||
ParadoxPanel panel = bridge.getPanel();
|
|
||||||
List<Partition> partitions = panel.getPartitions();
|
|
||||||
if (partitions == null) {
|
|
||||||
logger.debug(
|
|
||||||
"Partitions collection of Paradox Panel object is null. Probably not yet initialized. Skipping update.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (partitions.size() <= index) {
|
|
||||||
logger.debug("Attempted to access partition out of bounds of current partitions list. Index: {}, List: {}",
|
|
||||||
index, partitions);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Partition partition = partitions.get(index);
|
|
||||||
return partition;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OpenClosedType booleanToContactState(boolean value) {
|
private OpenClosedType booleanToContactState(boolean value) {
|
||||||
return value ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
|
return value ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
|
||||||
}
|
}
|
||||||
@ -124,4 +104,24 @@ public class ParadoxPartitionHandler extends EntityBaseHandler {
|
|||||||
super.handleCommand(channelUID, command);
|
super.handleCommand(channelUID, command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Partition getPartition() {
|
||||||
|
int index = calculateEntityIndex();
|
||||||
|
ParadoxIP150BridgeHandler bridge = (ParadoxIP150BridgeHandler) getBridge().getHandler();
|
||||||
|
ParadoxPanel panel = bridge.getPanel();
|
||||||
|
List<Partition> partitions = panel.getPartitions();
|
||||||
|
if (partitions == null) {
|
||||||
|
logger.debug(
|
||||||
|
"Partitions collection of Paradox Panel object is null. Probably not yet initialized. Skipping update.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (partitions.size() <= index) {
|
||||||
|
logger.debug("Attempted to access partition out of bounds of current partitions list. Index: {}, List: {}",
|
||||||
|
index, partitions);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Partition partition = partitions.get(index);
|
||||||
|
return partition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,14 @@ import java.util.List;
|
|||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
import org.openhab.binding.paradoxalarm.internal.model.ParadoxPanel;
|
import org.openhab.binding.paradoxalarm.internal.model.ParadoxPanel;
|
||||||
import org.openhab.binding.paradoxalarm.internal.model.Zone;
|
import org.openhab.binding.paradoxalarm.internal.model.Zone;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.model.ZoneState;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
import org.openhab.core.library.types.OpenClosedType;
|
import org.openhab.core.library.types.OpenClosedType;
|
||||||
import org.openhab.core.library.types.StringType;
|
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.Thing;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -41,15 +45,21 @@ public class ParadoxZoneHandler extends EntityBaseHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateEntity() {
|
protected void updateEntity() {
|
||||||
int index = calculateEntityIndex();
|
ParadoxIP150BridgeHandler bridgeHandler = getBridgeHandler();
|
||||||
ParadoxIP150BridgeHandler bridge = (ParadoxIP150BridgeHandler) getBridge().getHandler();
|
if (bridgeHandler == null) {
|
||||||
ParadoxPanel panel = bridge.getPanel();
|
logger.debug("Paradox bridge handler is null. Skipping update.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParadoxPanel panel = bridgeHandler.getPanel();
|
||||||
List<Zone> zones = panel.getZones();
|
List<Zone> zones = panel.getZones();
|
||||||
if (zones == null) {
|
if (zones == null) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Zones collection of Paradox Panel object is null. Probably not yet initialized. Skipping update.");
|
"Zones collection of Paradox Panel object is null. Probably not yet initialized. Skipping update.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int index = calculateEntityIndex();
|
||||||
if (zones.size() <= index) {
|
if (zones.size() <= index) {
|
||||||
logger.debug("Attempted to access zone out of bounds of current zone list. Index: {}, List: {}", index,
|
logger.debug("Attempted to access zone out of bounds of current zone list. Index: {}, List: {}", index,
|
||||||
zones);
|
zones);
|
||||||
@ -59,9 +69,23 @@ public class ParadoxZoneHandler extends EntityBaseHandler {
|
|||||||
Zone zone = zones.get(index);
|
Zone zone = zones.get(index);
|
||||||
if (zone != null) {
|
if (zone != null) {
|
||||||
updateState(ZONE_LABEL_CHANNEL_UID, new StringType(zone.getLabel()));
|
updateState(ZONE_LABEL_CHANNEL_UID, new StringType(zone.getLabel()));
|
||||||
updateState(ZONE_OPENED_CHANNEL_UID, booleanToContactState(zone.getZoneState().isOpened()));
|
ZoneState zoneState = zone.getZoneState();
|
||||||
updateState(ZONE_TAMPERED_CHANNEL_UID, booleanToSwitchState(zone.getZoneState().isTampered()));
|
if (zoneState != null) {
|
||||||
updateState(ZONE_LOW_BATTERY_CHANNEL_UID, booleanToSwitchState(zone.getZoneState().hasLowBattery()));
|
updateState(ZONE_OPENED_CHANNEL_UID, booleanToContactState(zoneState.isOpened()));
|
||||||
|
updateState(ZONE_TAMPERED_CHANNEL_UID, booleanToSwitchState(zoneState.isTampered()));
|
||||||
|
updateState(ZONE_LOW_BATTERY_CHANNEL_UID, booleanToSwitchState(zoneState.hasLowBattery()));
|
||||||
|
|
||||||
|
updateState(ZONE_SUPERVISION_TROUBLE_UID, booleanToSwitchState(zoneState.isSupervisionTrouble()));
|
||||||
|
updateState(ZONE_IN_TX_DELAY_UID, booleanToSwitchState(zoneState.isInTxDelay()));
|
||||||
|
updateState(ZONE_SHUTDOWN_UID, booleanToSwitchState(zoneState.isShutdown()));
|
||||||
|
updateState(ZONE_BYPASSED_UID, booleanToSwitchState(zoneState.isBypassed()));
|
||||||
|
updateState(ZONE_HAS_ACTIVATED_INTELLIZONE_DELAY_UID,
|
||||||
|
booleanToSwitchState(zoneState.isHasActivatedIntellizoneDelay()));
|
||||||
|
updateState(ZONE_HAS_ACTIVATED_ENTRY_DELAY_UID,
|
||||||
|
booleanToSwitchState(zoneState.isHasActivatedEntryDelay()));
|
||||||
|
updateState(ZONE_PRESENTLY_IN_ALARM_UID, booleanToSwitchState(zoneState.isPresentlyInAlarm()));
|
||||||
|
updateState(ZONE_GENERATED_ALARM_UID, booleanToSwitchState(zoneState.isGeneratedAlarm()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,4 +96,52 @@ public class ParadoxZoneHandler extends EntityBaseHandler {
|
|||||||
private OnOffType booleanToSwitchState(boolean value) {
|
private OnOffType booleanToSwitchState(boolean value) {
|
||||||
return value ? OnOffType.ON : OnOffType.OFF;
|
return value ? OnOffType.ON : OnOffType.OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) {
|
||||||
|
if (command instanceof StringType) {
|
||||||
|
Zone zone = getZone();
|
||||||
|
if (zone != null) {
|
||||||
|
zone.handleCommand(command.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.handleCommand(channelUID, command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Zone getZone() {
|
||||||
|
ParadoxIP150BridgeHandler bridgeHandler = getBridgeHandler();
|
||||||
|
if (bridgeHandler == null) {
|
||||||
|
logger.debug("Paradox bridge handler is null. Skipping update.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParadoxPanel panel = bridgeHandler.getPanel();
|
||||||
|
List<Zone> zones = panel.getZones();
|
||||||
|
if (zones == null) {
|
||||||
|
logger.debug(
|
||||||
|
"Zones collection of Paradox Panel object is null. Probably not yet initialized. Skipping update.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = calculateEntityIndex();
|
||||||
|
if (zones.size() <= index) {
|
||||||
|
logger.debug("Attempted to access a zone out of bounds of current zone list. Index: {}, List: {}", index,
|
||||||
|
zones);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Zone zone = zones.get(index);
|
||||||
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParadoxIP150BridgeHandler getBridgeHandler() {
|
||||||
|
Bridge bridge = getBridge();
|
||||||
|
if (bridge == null) {
|
||||||
|
logger.debug("Paradox bridge is null. Skipping update.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ParadoxIP150BridgeHandler) bridge.getHandler();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.paradoxalarm.internal.model;
|
package org.openhab.binding.paradoxalarm.internal.model;
|
||||||
|
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.PartitionCommandRequest;
|
import org.openhab.binding.paradoxalarm.internal.communication.IRequest;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.RequestType;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.partition.PartitionCommand;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.CommandPayload;
|
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderMessageType;
|
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.ParadoxIPPacket;
|
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.PartitionCommand;
|
|
||||||
import org.openhab.binding.paradoxalarm.internal.handlers.Commandable;
|
import org.openhab.binding.paradoxalarm.internal.handlers.Commandable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -51,16 +47,13 @@ public class Partition extends Entity implements Commandable {
|
|||||||
@Override
|
@Override
|
||||||
public void handleCommand(String command) {
|
public void handleCommand(String command) {
|
||||||
PartitionCommand partitionCommand = PartitionCommand.parse(command);
|
PartitionCommand partitionCommand = PartitionCommand.parse(command);
|
||||||
if (partitionCommand == PartitionCommand.UNKNOWN) {
|
if (partitionCommand == null) {
|
||||||
logger.debug("Command UNKNOWN will be ignored.");
|
logger.debug("Command {} is parsed to null. Skipping it", command);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Submitting command={} for partition=[{}]", partitionCommand, this);
|
logger.debug("Submitting command={} for partition=[{}]", partitionCommand, this);
|
||||||
CommandPayload payload = new CommandPayload(getId(), partitionCommand);
|
IRequest request = partitionCommand.getRequest(getId());
|
||||||
ParadoxIPPacket packet = new ParadoxIPPacket(payload.getBytes())
|
|
||||||
.setMessageType(HeaderMessageType.SERIAL_PASSTHRU_REQUEST);
|
|
||||||
PartitionCommandRequest request = new PartitionCommandRequest(RequestType.PARTITION_COMMAND, packet, null);
|
|
||||||
getPanel().getCommunicator().submitRequest(request);
|
getPanel().getCommunicator().submitRequest(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.paradoxalarm.internal.model;
|
package org.openhab.binding.paradoxalarm.internal.model;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.IRequest;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.zone.ZoneCommand;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.handlers.Commandable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -22,23 +27,36 @@ import org.slf4j.LoggerFactory;
|
|||||||
*
|
*
|
||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
public class Zone extends Entity {
|
@NonNullByDefault
|
||||||
|
public class Zone extends Entity implements Commandable {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(Zone.class);
|
private final Logger logger = LoggerFactory.getLogger(Zone.class);
|
||||||
|
|
||||||
private ZoneState zoneState;
|
private @Nullable ZoneState zoneState;
|
||||||
|
|
||||||
public Zone(ParadoxPanel panel, int id, String label) {
|
public Zone(ParadoxPanel panel, int id, @Nullable String label) {
|
||||||
super(panel, id, label);
|
super(panel, id, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZoneState getZoneState() {
|
public @Nullable ZoneState getZoneState() {
|
||||||
return zoneState;
|
return zoneState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setZoneState(ZoneState zoneState) {
|
public void setZoneState(ZoneState zoneState) {
|
||||||
this.zoneState = zoneState;
|
this.zoneState = zoneState;
|
||||||
logger.debug("Zone {} state updated to:\tOpened: {}, Tampered: {}, LowBattery: {}", getLabel(),
|
logger.debug("Zone {} state updated to: {}", getLabel(), zoneState);
|
||||||
zoneState.isOpened(), zoneState.isTampered(), zoneState.hasLowBattery());
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(@Nullable String command) {
|
||||||
|
ZoneCommand zoneCommand = ZoneCommand.parse(command);
|
||||||
|
if (zoneCommand == null) {
|
||||||
|
logger.debug("Command {} is parsed to null. Skipping it", command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Submitting command={} for partition=[{}]", zoneCommand, this);
|
||||||
|
IRequest request = zoneCommand.getRequest(getId());
|
||||||
|
getPanel().getCommunicator().submitRequest(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,21 @@ package org.openhab.binding.paradoxalarm.internal.model;
|
|||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
public class ZoneState {
|
public class ZoneState {
|
||||||
|
// Regular states
|
||||||
private boolean isOpened;
|
private boolean isOpened;
|
||||||
private boolean isTampered;
|
private boolean isTampered;
|
||||||
private boolean hasLowBattery;
|
private boolean hasLowBattery;
|
||||||
|
|
||||||
|
// Special flag states
|
||||||
|
private boolean supervisionTrouble;
|
||||||
|
private boolean inTxDelay;
|
||||||
|
private boolean shuttedDown;
|
||||||
|
private boolean bypassed;
|
||||||
|
private boolean hasActivatedIntellizoneDelay;
|
||||||
|
private boolean hasActivatedEntryDelay;
|
||||||
|
private boolean presentlyInAlarm;
|
||||||
|
private boolean generatedAlarm;
|
||||||
|
|
||||||
public ZoneState(boolean isOpened, boolean isTampered, boolean hasLowBattery) {
|
public ZoneState(boolean isOpened, boolean isTampered, boolean hasLowBattery) {
|
||||||
this.isOpened = isOpened;
|
this.isOpened = isOpened;
|
||||||
this.isTampered = isTampered;
|
this.isTampered = isTampered;
|
||||||
@ -51,4 +62,77 @@ public class ZoneState {
|
|||||||
public void setHasLowBattery(boolean hasLowBattery) {
|
public void setHasLowBattery(boolean hasLowBattery) {
|
||||||
this.hasLowBattery = hasLowBattery;
|
this.hasLowBattery = hasLowBattery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSupervisionTrouble(boolean supervisionTrouble) {
|
||||||
|
this.supervisionTrouble = supervisionTrouble;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSupervisionTrouble() {
|
||||||
|
return supervisionTrouble;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInTxDelay() {
|
||||||
|
return inTxDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInTxDelay(boolean inTxDelay) {
|
||||||
|
this.inTxDelay = inTxDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShutdown() {
|
||||||
|
return shuttedDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShuttedDown(boolean shuttedDown) {
|
||||||
|
this.shuttedDown = shuttedDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBypassed() {
|
||||||
|
return bypassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBypassed(boolean bypassed) {
|
||||||
|
this.bypassed = bypassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasActivatedIntellizoneDelay() {
|
||||||
|
return hasActivatedIntellizoneDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasActivatedIntellizoneDelay(boolean hasActivatedIntellizoneDelay) {
|
||||||
|
this.hasActivatedIntellizoneDelay = hasActivatedIntellizoneDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasActivatedEntryDelay() {
|
||||||
|
return hasActivatedEntryDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasActivatedEntryDelay(boolean hasActivatedEntryDelay) {
|
||||||
|
this.hasActivatedEntryDelay = hasActivatedEntryDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPresentlyInAlarm() {
|
||||||
|
return presentlyInAlarm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPresentlyInAlarm(boolean presentlyInAlarm) {
|
||||||
|
this.presentlyInAlarm = presentlyInAlarm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGeneratedAlarm() {
|
||||||
|
return generatedAlarm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeneratedAlarm(boolean generatedAlarm) {
|
||||||
|
this.generatedAlarm = generatedAlarm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ZoneState [isOpened=" + isOpened + ", isTampered=" + isTampered + ", hasLowBattery=" + hasLowBattery
|
||||||
|
+ ", supervisionTrouble=" + supervisionTrouble + ", inTxDelay=" + inTxDelay + ", shuttedDown="
|
||||||
|
+ shuttedDown + ", bypassed=" + bypassed + ", hasActivatedIntellizoneDelay="
|
||||||
|
+ hasActivatedIntellizoneDelay + ", hasActivatedEntryDelay=" + hasActivatedEntryDelay
|
||||||
|
+ ", presentlyInAlarm=" + presentlyInAlarm + ", generatedAlarm=" + generatedAlarm + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ public class ZoneStateFlags {
|
|||||||
private byte[] zonesOpened;
|
private byte[] zonesOpened;
|
||||||
private byte[] zonesTampered;
|
private byte[] zonesTampered;
|
||||||
private byte[] zonesLowBattery;
|
private byte[] zonesLowBattery;
|
||||||
|
private byte[] zoneSpecialFlags;
|
||||||
|
|
||||||
public byte[] getZonesOpened() {
|
public byte[] getZonesOpened() {
|
||||||
return zonesOpened;
|
return zonesOpened;
|
||||||
@ -45,4 +46,12 @@ public class ZoneStateFlags {
|
|||||||
public void setZonesLowBattery(byte[] zonesLowBattery) {
|
public void setZonesLowBattery(byte[] zonesLowBattery) {
|
||||||
this.zonesLowBattery = zonesLowBattery;
|
this.zonesLowBattery = zonesLowBattery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getZoneSpecialFlags() {
|
||||||
|
return zoneSpecialFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZoneSpecialFlags(byte[] zoneSpecialFlags) {
|
||||||
|
this.zoneSpecialFlags = zoneSpecialFlags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,10 +64,11 @@ public class EvoParser extends AbstractParser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ZoneState calculateZoneState(int id, ZoneStateFlags zoneStateFlags) {
|
public ZoneState calculateZoneState(int id, ZoneStateFlags zoneStateFlags) {
|
||||||
|
|
||||||
int index = (id - 1) / 8;
|
int index = (id - 1) / 8;
|
||||||
int bitNumber = id % 8 - 1;
|
int bitNumber = id % 8 - 1;
|
||||||
|
|
||||||
|
// Every zone state is represented by a bit set/unset in the big byte array retrieved from the memory of the
|
||||||
|
// panel
|
||||||
byte[] zonesOpened = zoneStateFlags.getZonesOpened();
|
byte[] zonesOpened = zoneStateFlags.getZonesOpened();
|
||||||
boolean isOpened = ParadoxUtil.isBitSet(zonesOpened[index], bitNumber);
|
boolean isOpened = ParadoxUtil.isBitSet(zonesOpened[index], bitNumber);
|
||||||
|
|
||||||
@ -77,6 +78,38 @@ public class EvoParser extends AbstractParser {
|
|||||||
byte[] zonesLowBattery = zoneStateFlags.getZonesLowBattery();
|
byte[] zonesLowBattery = zoneStateFlags.getZonesLowBattery();
|
||||||
boolean hasLowBattery = ParadoxUtil.isBitSet(zonesLowBattery[index], bitNumber);
|
boolean hasLowBattery = ParadoxUtil.isBitSet(zonesLowBattery[index], bitNumber);
|
||||||
|
|
||||||
return new ZoneState(isOpened, isTampered, hasLowBattery);
|
ZoneState zoneState = new ZoneState(isOpened, isTampered, hasLowBattery);
|
||||||
|
|
||||||
|
calculateSpecialFlags(zoneStateFlags, id, zoneState);
|
||||||
|
|
||||||
|
return zoneState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateSpecialFlags(ZoneStateFlags zoneStateFlags, int index, ZoneState zoneState) {
|
||||||
|
// Each byte is filled with 8 special zone flags.
|
||||||
|
// Each bit of the byte represents a specific flag.
|
||||||
|
// Zone Flags:
|
||||||
|
// 0 = Zone supervision trouble
|
||||||
|
// 1 = Zone in TX delay
|
||||||
|
// 2 = Zone shutted down
|
||||||
|
// 3 = Zone bypassed
|
||||||
|
// 4 = Zone activated intellizone delay
|
||||||
|
// 5 = Zone activated entry delay
|
||||||
|
// 6 = Zone presently in alarm
|
||||||
|
// 7 = Zone generated an alarm
|
||||||
|
|
||||||
|
// The index of the actual zones and partitions enumerates from 1-N. In the arrays we need to index it from 0.
|
||||||
|
int specialFlagsIndex = index - 1;
|
||||||
|
byte[] zoneSpecialFlags = zoneStateFlags.getZoneSpecialFlags();
|
||||||
|
byte currentZoneFlags = zoneSpecialFlags[specialFlagsIndex];
|
||||||
|
|
||||||
|
zoneState.setSupervisionTrouble(ParadoxUtil.isBitSet(currentZoneFlags, 0));
|
||||||
|
zoneState.setInTxDelay(ParadoxUtil.isBitSet(currentZoneFlags, 1));
|
||||||
|
zoneState.setShuttedDown(ParadoxUtil.isBitSet(currentZoneFlags, 2));
|
||||||
|
zoneState.setBypassed(ParadoxUtil.isBitSet(currentZoneFlags, 3));
|
||||||
|
zoneState.setHasActivatedIntellizoneDelay(ParadoxUtil.isBitSet(currentZoneFlags, 4));
|
||||||
|
zoneState.setHasActivatedEntryDelay(ParadoxUtil.isBitSet(currentZoneFlags, 5));
|
||||||
|
zoneState.setPresentlyInAlarm(ParadoxUtil.isBitSet(currentZoneFlags, 6));
|
||||||
|
zoneState.setGeneratedAlarm(ParadoxUtil.isBitSet(currentZoneFlags, 7));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
public class ParadoxUtil {
|
public class ParadoxUtil {
|
||||||
|
|
||||||
private static final String SPACE_DELIMITER = " ";
|
private static final String SPACE_DELIMITER = " ";
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ParadoxUtil.class);
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(ParadoxUtil.class);
|
||||||
|
|
||||||
public static byte calculateChecksum(byte[] payload) {
|
public static byte calculateChecksum(byte[] payload) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
@ -50,28 +51,28 @@ public class ParadoxUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void printPacket(String description, byte[] array) {
|
public static void printPacket(String description, byte[] array) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (LOGGER.isTraceEnabled()) {
|
||||||
logger.trace("Packet payload size: {}", array[1]);
|
LOGGER.trace("Packet payload size: {}", array[1]);
|
||||||
printByteArray(description, array, array[1] + 16);
|
printByteArray(description, array, array[1] + 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printByteArray(String description, byte[] array) {
|
public static void printByteArray(String description, byte[] array) {
|
||||||
if (array == null) {
|
if (array == null) {
|
||||||
logger.trace("Array is null");
|
LOGGER.trace("Array is null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printByteArray(description, array, array.length);
|
printByteArray(description, array, array.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printByteArray(String description, byte[] array, int length) {
|
public static void printByteArray(String description, byte[] array, int length) {
|
||||||
if (!logger.isTraceEnabled()) {
|
if (!LOGGER.isTraceEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = byteArrayToString(array, length);
|
String result = byteArrayToString(array, length);
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
logger.trace("{}", description + SPACE_DELIMITER + result);
|
LOGGER.trace("{}", description + SPACE_DELIMITER + result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +132,7 @@ public class ParadoxUtil {
|
|||||||
byte[] byteArray = outputStream.toByteArray();
|
byte[] byteArray = outputStream.toByteArray();
|
||||||
return byteArray;
|
return byteArray;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.warn("Exception merging arrays:", e);
|
LOGGER.warn("Exception merging arrays:", e);
|
||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
# add-on
|
|
||||||
|
|
||||||
addon.paradoxalarm.name = ParadoxAlarm Binding
|
|
||||||
addon.paradoxalarm.description = This is the binding for ParadoxAlarm.
|
|
||||||
|
|
||||||
# thing types
|
# thing types
|
||||||
|
|
||||||
thing-type.paradoxalarm.ip150.label = Paradox IP150 Module Connector
|
thing-type.paradoxalarm.ip150.label = Paradox IP150 Module Connector
|
||||||
@ -57,6 +52,9 @@ channel-type.paradoxalarm.bootloaderVersion.label = Boot Loader Version
|
|||||||
channel-type.paradoxalarm.bootloaderVersion.description = Boot loader version
|
channel-type.paradoxalarm.bootloaderVersion.description = Boot loader version
|
||||||
channel-type.paradoxalarm.bypassReady.label = Partition Is Bypass Ready
|
channel-type.paradoxalarm.bypassReady.label = Partition Is Bypass Ready
|
||||||
channel-type.paradoxalarm.bypassReady.description = Partition is Bypass Ready
|
channel-type.paradoxalarm.bypassReady.description = Partition is Bypass Ready
|
||||||
|
channel-type.paradoxalarm.bypassed.label = Bypassed
|
||||||
|
channel-type.paradoxalarm.command.label = Communicator Command
|
||||||
|
channel-type.paradoxalarm.command.description = Send Command
|
||||||
channel-type.paradoxalarm.command.label = Partition Command
|
channel-type.paradoxalarm.command.label = Partition Command
|
||||||
channel-type.paradoxalarm.command.description = Send command
|
channel-type.paradoxalarm.command.description = Send command
|
||||||
channel-type.paradoxalarm.command.state.option.ARM = Arm
|
channel-type.paradoxalarm.command.state.option.ARM = Arm
|
||||||
@ -65,20 +63,26 @@ channel-type.paradoxalarm.command.state.option.INSTANT_ARM = Instant Arm
|
|||||||
channel-type.paradoxalarm.command.state.option.FORCE_ARM = Force Arm
|
channel-type.paradoxalarm.command.state.option.FORCE_ARM = Force Arm
|
||||||
channel-type.paradoxalarm.command.state.option.DISARM = Disarm
|
channel-type.paradoxalarm.command.state.option.DISARM = Disarm
|
||||||
channel-type.paradoxalarm.command.state.option.BEEP = Keyboard Beep
|
channel-type.paradoxalarm.command.state.option.BEEP = Keyboard Beep
|
||||||
channel-type.paradoxalarm.command.label = Communicator Command
|
channel-type.paradoxalarm.command.label = Zone Command
|
||||||
channel-type.paradoxalarm.command.description = Send Command
|
channel-type.paradoxalarm.command.description = Send command for a zone
|
||||||
|
channel-type.paradoxalarm.command.state.option.BYPASS = Bypass
|
||||||
|
channel-type.paradoxalarm.command.state.option.CLEAR_BYPASS = Clear Bypass
|
||||||
channel-type.paradoxalarm.communicationState.label = Bridge Communication State
|
channel-type.paradoxalarm.communicationState.label = Bridge Communication State
|
||||||
channel-type.paradoxalarm.communicationState.description = Status of connection to Paradox system
|
channel-type.paradoxalarm.communicationState.description = Status of connection to Paradox system
|
||||||
channel-type.paradoxalarm.forceReady.label = Partition Is Force Ready
|
channel-type.paradoxalarm.forceReady.label = Partition Is Force Ready
|
||||||
channel-type.paradoxalarm.forceReady.description = Partition is Force Ready
|
channel-type.paradoxalarm.forceReady.description = Partition is Force Ready
|
||||||
|
channel-type.paradoxalarm.generatedAlarm.label = Generated an Alarm
|
||||||
channel-type.paradoxalarm.hardwareVersion.label = Hardware Version
|
channel-type.paradoxalarm.hardwareVersion.label = Hardware Version
|
||||||
channel-type.paradoxalarm.hardwareVersion.description = Panel hardware version
|
channel-type.paradoxalarm.hardwareVersion.description = Panel hardware version
|
||||||
|
channel-type.paradoxalarm.hasActivatedEntryDelay.label = Has Activated Entry Delay
|
||||||
|
channel-type.paradoxalarm.hasActivatedIntellizoneDelay.label = Has Activated Intellizone Delay
|
||||||
channel-type.paradoxalarm.inEntryDelay.label = Partition In Entry Delay
|
channel-type.paradoxalarm.inEntryDelay.label = Partition In Entry Delay
|
||||||
channel-type.paradoxalarm.inEntryDelay.description = Partition in Entry Delay
|
channel-type.paradoxalarm.inEntryDelay.description = Partition in Entry Delay
|
||||||
channel-type.paradoxalarm.inExitDelay.label = Partition In Exit Delay
|
channel-type.paradoxalarm.inExitDelay.label = Partition In Exit Delay
|
||||||
channel-type.paradoxalarm.inExitDelay.description = Partition in Exit Delay
|
channel-type.paradoxalarm.inExitDelay.description = Partition in Exit Delay
|
||||||
channel-type.paradoxalarm.inTrouble.label = Partition In Trouble
|
channel-type.paradoxalarm.inTrouble.label = Partition In Trouble
|
||||||
channel-type.paradoxalarm.inTrouble.description = Partition in Trouble
|
channel-type.paradoxalarm.inTrouble.description = Partition in Trouble
|
||||||
|
channel-type.paradoxalarm.inTxDelay.label = In TX Delay
|
||||||
channel-type.paradoxalarm.inhibitReady.label = Partition Is Inhibit Ready
|
channel-type.paradoxalarm.inhibitReady.label = Partition Is Inhibit Ready
|
||||||
channel-type.paradoxalarm.inhibitReady.description = Partition is Inhibit Ready
|
channel-type.paradoxalarm.inhibitReady.description = Partition is Inhibit Ready
|
||||||
channel-type.paradoxalarm.openedState.label = Zone State
|
channel-type.paradoxalarm.openedState.label = Zone State
|
||||||
@ -91,14 +95,17 @@ channel-type.paradoxalarm.panelType.label = Panel Type
|
|||||||
channel-type.paradoxalarm.panelType.description = Panel type (Evo, SP, etc)
|
channel-type.paradoxalarm.panelType.description = Panel type (Evo, SP, etc)
|
||||||
channel-type.paradoxalarm.partitionLabel.label = Partition Label
|
channel-type.paradoxalarm.partitionLabel.label = Partition Label
|
||||||
channel-type.paradoxalarm.partitionLabel.description = Label of partition
|
channel-type.paradoxalarm.partitionLabel.description = Label of partition
|
||||||
|
channel-type.paradoxalarm.presentlyInAlarm.label = Currently in Alarm
|
||||||
channel-type.paradoxalarm.readyToArm.label = Partition Ready To Arm
|
channel-type.paradoxalarm.readyToArm.label = Partition Ready To Arm
|
||||||
channel-type.paradoxalarm.readyToArm.description = Partition ready to arm
|
channel-type.paradoxalarm.readyToArm.description = Partition ready to arm
|
||||||
channel-type.paradoxalarm.serialNumber.label = Serial Number
|
channel-type.paradoxalarm.serialNumber.label = Serial Number
|
||||||
channel-type.paradoxalarm.serialNumber.description = Panel serial number
|
channel-type.paradoxalarm.serialNumber.description = Panel serial number
|
||||||
|
channel-type.paradoxalarm.shutdown.label = Shutdown
|
||||||
channel-type.paradoxalarm.state.label = Partition State
|
channel-type.paradoxalarm.state.label = Partition State
|
||||||
channel-type.paradoxalarm.state.description = State of partition
|
channel-type.paradoxalarm.state.description = State of partition
|
||||||
channel-type.paradoxalarm.stayInstantReady.label = Partition Is Stay Instant Ready
|
channel-type.paradoxalarm.stayInstantReady.label = Partition Is Stay Instant Ready
|
||||||
channel-type.paradoxalarm.stayInstantReady.description = Partition is Stay Instant Ready
|
channel-type.paradoxalarm.stayInstantReady.description = Partition is Stay Instant Ready
|
||||||
|
channel-type.paradoxalarm.supervisionTrouble.label = Supervision Trouble
|
||||||
channel-type.paradoxalarm.tamperedState.label = Tampered
|
channel-type.paradoxalarm.tamperedState.label = Tampered
|
||||||
channel-type.paradoxalarm.tamperedState.description = State of zone
|
channel-type.paradoxalarm.tamperedState.description = State of zone
|
||||||
channel-type.paradoxalarm.voltage.label = Voltage
|
channel-type.paradoxalarm.voltage.label = Voltage
|
||||||
@ -115,3 +122,8 @@ channel-type.paradoxalarm.zoneInTamperTrouble.label = Partition Has Zone In Tamp
|
|||||||
channel-type.paradoxalarm.zoneInTamperTrouble.description = Partition has in Tamper Trouble
|
channel-type.paradoxalarm.zoneInTamperTrouble.description = Partition has in Tamper Trouble
|
||||||
channel-type.paradoxalarm.zoneLabel.label = Zone Label
|
channel-type.paradoxalarm.zoneLabel.label = Zone Label
|
||||||
channel-type.paradoxalarm.zoneLabel.description = Label of zone
|
channel-type.paradoxalarm.zoneLabel.description = Label of zone
|
||||||
|
|
||||||
|
# add-on
|
||||||
|
|
||||||
|
addon.paradoxalarm.name = ParadoxAlarm Binding
|
||||||
|
addon.paradoxalarm.description = This is the binding for ParadoxAlarm.
|
||||||
|
@ -16,6 +16,16 @@
|
|||||||
<channel id="opened" typeId="openedState"/>
|
<channel id="opened" typeId="openedState"/>
|
||||||
<channel id="tampered" typeId="tamperedState"/>
|
<channel id="tampered" typeId="tamperedState"/>
|
||||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||||
|
<channel id="command" typeId="command"/>
|
||||||
|
|
||||||
|
<channel id="supervisionTrouble" typeId="supervisionTrouble"/>
|
||||||
|
<channel id="inTxDelay" typeId="inTxDelay"/>
|
||||||
|
<channel id="shutdown" typeId="shutdown"/>
|
||||||
|
<channel id="bypassed" typeId="bypassed"/>
|
||||||
|
<channel id="hasActivatedIntellizoneDelay" typeId="hasActivatedIntellizoneDelay"/>
|
||||||
|
<channel id="hasActivatedEntryDelay" typeId="hasActivatedEntryDelay"/>
|
||||||
|
<channel id="presentlyInAlarm" typeId="presentlyInAlarm"/>
|
||||||
|
<channel id="generatedAlarm" typeId="generatedAlarm"/>
|
||||||
</channels>
|
</channels>
|
||||||
|
|
||||||
<config-description>
|
<config-description>
|
||||||
@ -44,4 +54,55 @@
|
|||||||
<description>State of zone</description>
|
<description>State of zone</description>
|
||||||
<state readOnly="true" pattern="%s"/>
|
<state readOnly="true" pattern="%s"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
<channel-type id="command">
|
||||||
|
<item-type>String</item-type>
|
||||||
|
<label>Zone Command</label>
|
||||||
|
<description>Send command for a zone</description>
|
||||||
|
<state>
|
||||||
|
<options>
|
||||||
|
<option value="BYPASS">Bypass</option>
|
||||||
|
<option value="CLEAR_BYPASS">Clear Bypass</option>
|
||||||
|
</options>
|
||||||
|
</state>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="supervisionTrouble">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Supervision Trouble</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="inTxDelay">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>In TX Delay</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="shutdown">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Shutdown</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="bypassed">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Bypassed</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="hasActivatedIntellizoneDelay">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Has Activated Intellizone Delay</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="hasActivatedEntryDelay">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Has Activated Entry Delay</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="presentlyInAlarm">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Currently in Alarm</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
|
<channel-type id="generatedAlarm">
|
||||||
|
<item-type>Switch</item-type>
|
||||||
|
<label>Generated an Alarm</label>
|
||||||
|
<state readOnly="true" pattern="%s"/>
|
||||||
|
</channel-type>
|
||||||
</thing:thing-descriptions>
|
</thing:thing-descriptions>
|
||||||
|
@ -12,11 +12,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.paradoxalarm.internal;
|
package org.openhab.binding.paradoxalarm.internal;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.CommandPayload;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.partition.PartitionCommand;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.PartitionCommand;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.partition.PartitionCommandPayload;
|
||||||
import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,19 +26,20 @@ import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
|||||||
*
|
*
|
||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class TestCreateCommandPayload {
|
public class TestCreateCommandPayload {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreatePayload() {
|
public void testCreatePayload() {
|
||||||
for (PartitionCommand command : PartitionCommand.values()) {
|
for (PartitionCommand command : PartitionCommand.values()) {
|
||||||
for (int partitionNumber = 1; partitionNumber <= 8; partitionNumber++) {
|
for (int partitionNumber = 1; partitionNumber <= 8; partitionNumber++) {
|
||||||
CommandPayload payload = new CommandPayload(partitionNumber, command);
|
PartitionCommandPayload payload = new PartitionCommandPayload(partitionNumber, command);
|
||||||
assertNibble(partitionNumber, command, payload);
|
assertNibble(partitionNumber, command, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertNibble(int partitionNumber, PartitionCommand command, CommandPayload payload) {
|
private void assertNibble(int partitionNumber, PartitionCommand command, PartitionCommandPayload payload) {
|
||||||
byte[] bytes = payload.getBytes();
|
byte[] bytes = payload.getBytes();
|
||||||
int payloadIndexOfByteToCheck = 6 + (partitionNumber - 1) / 2;
|
int payloadIndexOfByteToCheck = 6 + (partitionNumber - 1) / 2;
|
||||||
byte byteValue = bytes[payloadIndexOfByteToCheck];
|
byte byteValue = bytes[payloadIndexOfByteToCheck];
|
||||||
|
@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.crypto.EncryptionHandler;
|
import org.openhab.binding.paradoxalarm.internal.communication.crypto.EncryptionHandler;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderCommand;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderCommand;
|
||||||
@ -27,6 +28,7 @@ import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
|||||||
*
|
*
|
||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class TestEncryptionHandler {
|
public class TestEncryptionHandler {
|
||||||
|
|
||||||
private static final String INPUT_STRING = "My test string for encryption.";
|
private static final String INPUT_STRING = "My test string for encryption.";
|
||||||
|
@ -12,17 +12,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.paradoxalarm.internal;
|
package org.openhab.binding.paradoxalarm.internal;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.CommandPayload;
|
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.EpromRequestPayload;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.EpromRequestPayload;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderCommand;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.HeaderCommand;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.IPayload;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.IPayload;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.ParadoxIPPacket;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.ParadoxIPPacket;
|
||||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.PartitionCommand;
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.partition.PartitionCommand;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.partition.PartitionCommandPayload;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.zone.ZoneCommand;
|
||||||
|
import org.openhab.binding.paradoxalarm.internal.communication.messages.zone.ZoneCommandPayload;
|
||||||
import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxException;
|
import org.openhab.binding.paradoxalarm.internal.exceptions.ParadoxException;
|
||||||
import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -33,6 +36,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
*
|
*
|
||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class TestGetBytes {
|
public class TestGetBytes {
|
||||||
|
|
||||||
private static final int PARTITION_NUMBER = 1;
|
private static final int PARTITION_NUMBER = 1;
|
||||||
@ -61,18 +65,18 @@ public class TestGetBytes {
|
|||||||
assertTrue(Arrays.equals(packetBytes, EXPECTED1));
|
assertTrue(Arrays.equals(packetBytes, EXPECTED1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final byte[] EXPECTED_COMMAND_PAYLOAD = { 0x40, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
private static final byte[] EXPECTED_PARTITION_COMMAND_PAYLOAD = { 0x40, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00 };
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCommandPayload() {
|
public void testPartitionCommandPayload() {
|
||||||
CommandPayload payload = new CommandPayload(PARTITION_NUMBER, PartitionCommand.ARM);
|
PartitionCommandPayload payload = new PartitionCommandPayload(PARTITION_NUMBER, PartitionCommand.ARM);
|
||||||
final byte[] packetBytes = payload.getBytes();
|
final byte[] packetBytes = payload.getBytes();
|
||||||
|
|
||||||
ParadoxUtil.printByteArray("Expected =", EXPECTED_COMMAND_PAYLOAD);
|
ParadoxUtil.printByteArray("Expected =", EXPECTED_PARTITION_COMMAND_PAYLOAD);
|
||||||
ParadoxUtil.printByteArray("Result =", packetBytes);
|
ParadoxUtil.printByteArray("Result =", packetBytes);
|
||||||
|
|
||||||
assertTrue(Arrays.equals(packetBytes, EXPECTED_COMMAND_PAYLOAD));
|
assertTrue(Arrays.equals(packetBytes, EXPECTED_PARTITION_COMMAND_PAYLOAD));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final byte[] EXPECTED_MEMORY_PAYLOAD = { (byte) 0xAA, 0x0A, 0x00, 0x03, 0x08, (byte) 0xF0, 0x00,
|
private static final byte[] EXPECTED_MEMORY_PAYLOAD = { (byte) 0xAA, 0x0A, 0x00, 0x03, 0x08, (byte) 0xF0, 0x00,
|
||||||
@ -88,4 +92,39 @@ public class TestGetBytes {
|
|||||||
ParadoxUtil.printByteArray("Expected =", EXPECTED_MEMORY_PAYLOAD);
|
ParadoxUtil.printByteArray("Expected =", EXPECTED_MEMORY_PAYLOAD);
|
||||||
ParadoxUtil.printByteArray("Result =", bytes);
|
ParadoxUtil.printByteArray("Result =", bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final byte[] EXPECTED_ZONE_5_BYPASS_COMMAND = { (byte) 0xd0, 0x1f, 0x08, 0x08, 0x00, 0x00, 0x10,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f };
|
||||||
|
private static final byte[] EXPECTED_ZONE_5_CLEAR_BYPASS_COMMAND = { (byte) 0xd0, 0x1f, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07 };
|
||||||
|
private static final byte[] EXPECTED_ZONE_20_BYPASS_COMMAND = { (byte) 0xd0, 0x1f, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x07 };
|
||||||
|
private static final byte[] EXPECTED_ZONE_23_BYPASS_COMMAND = { (byte) 0xd0, 0x1f, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f };
|
||||||
|
private static final byte[] EXPECTED_ZONE_23_CLEAR_BYPASS_COMMAND = { (byte) 0xd0, 0x1f, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37 };
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testZoneCommandPayload() {
|
||||||
|
testZoneCommandPayload(5, ZoneCommand.BYPASS, EXPECTED_ZONE_5_BYPASS_COMMAND);
|
||||||
|
testZoneCommandPayload(5, ZoneCommand.CLEAR_BYPASS, EXPECTED_ZONE_5_CLEAR_BYPASS_COMMAND);
|
||||||
|
testZoneCommandPayload(20, ZoneCommand.BYPASS, EXPECTED_ZONE_20_BYPASS_COMMAND);
|
||||||
|
testZoneCommandPayload(23, ZoneCommand.BYPASS, EXPECTED_ZONE_23_BYPASS_COMMAND);
|
||||||
|
testZoneCommandPayload(23, ZoneCommand.CLEAR_BYPASS, EXPECTED_ZONE_23_CLEAR_BYPASS_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testZoneCommandPayload(int zoneNumber, ZoneCommand command, byte[] expected) {
|
||||||
|
ZoneCommandPayload payload = new ZoneCommandPayload(zoneNumber, command);
|
||||||
|
final byte[] packetBytes = payload.getBytes();
|
||||||
|
|
||||||
|
ParadoxUtil.printByteArray("Expected " + zoneNumber + " =", expected);
|
||||||
|
ParadoxUtil.printByteArray("Result " + zoneNumber + " =", packetBytes);
|
||||||
|
|
||||||
|
assertTrue(Arrays.equals(packetBytes, expected));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.paradoxalarm.internal;
|
package org.openhab.binding.paradoxalarm.internal;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
|||||||
*
|
*
|
||||||
* @author Konstantin Polihronov - Initial contribution
|
* @author Konstantin Polihronov - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class TestParadoxUtil {
|
public class TestParadoxUtil {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -30,19 +32,19 @@ public class TestParadoxUtil {
|
|||||||
final int rate = 16;
|
final int rate = 16;
|
||||||
byte[] extendedArray = ParadoxUtil.extendArray(arrayToExtend, rate);
|
byte[] extendedArray = ParadoxUtil.extendArray(arrayToExtend, rate);
|
||||||
|
|
||||||
final byte[] EXPECTED_RESULT = { 0x0A, 0x50, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x59, (byte) 0xEE, (byte) 0xEE,
|
final byte[] expectedResult = { 0x0A, 0x50, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x59, (byte) 0xEE, (byte) 0xEE,
|
||||||
(byte) 0xEE, (byte) 0xEE, (byte) 0xEE, (byte) 0xEE, (byte) 0xEE };
|
(byte) 0xEE, (byte) 0xEE, (byte) 0xEE, (byte) 0xEE, (byte) 0xEE };
|
||||||
assertArrayEquals(EXPECTED_RESULT, extendedArray); //
|
assertArrayEquals(expectedResult, extendedArray); //
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMergeArrays() {
|
public void testMergeArrays() {
|
||||||
final byte[] ARR1 = { 0x01, 0x02, 0x03 };
|
final byte[] arr1 = { 0x01, 0x02, 0x03 };
|
||||||
final byte[] ARR2 = { 0x04, 0x05, 0x06 };
|
final byte[] arr2 = { 0x04, 0x05, 0x06 };
|
||||||
final byte[] ARR3 = { 0x07, 0x08, 0x09 };
|
final byte[] arr3 = { 0x07, 0x08, 0x09 };
|
||||||
byte[] mergedArrays = ParadoxUtil.mergeByteArrays(ARR1, ARR2, ARR3);
|
byte[] mergedArrays = ParadoxUtil.mergeByteArrays(arr1, arr2, arr3);
|
||||||
|
|
||||||
final byte[] EXPECTED_RESULT = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
final byte[] expectedResult = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
||||||
assertArrayEquals(EXPECTED_RESULT, mergedArrays);
|
assertArrayEquals(expectedResult, mergedArrays);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user