mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +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:
|
||||
|
||||
| Channel | Type | Description |
|
||||
|-----------------|---------|--------------------------------------------------------------------------------|
|
||||
| zoneLabel | String | Label of zone inside Paradox configuration |
|
||||
| openedState | Contact | Zone opened / closed |
|
||||
| tamperedState | Switch | Zone is tampered / not tampered |
|
||||
| Channel | Type | Description |
|
||||
|------------------------------------|---------|--------------------------------------------------------------------------------|
|
||||
| zoneLabel | String | Label of zone inside Paradox configuration |
|
||||
| openedState | Contact | Zone opened / closed |
|
||||
| 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
|
||||
|
||||
|
@ -87,7 +87,7 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
||||
if (payload != null && payload.length >= RAM_BLOCK_SIZE) {
|
||||
RamRequest request = (RamRequest) response.getRequest();
|
||||
int ramBlockNumber = request.getRamBlockNumber();
|
||||
memoryMap.updateElement(ramBlockNumber, payload);
|
||||
memoryMap.updateElement(ramBlockNumber - 1, payload);
|
||||
if (logger.isTraceEnabled()) {
|
||||
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[] 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;
|
||||
byte[] firstBlock = Arrays.copyOfRange(firstPage, 28, pageOffset);
|
||||
if (panelType != PanelType.EVO192) {
|
||||
@ -186,9 +200,11 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
||||
byte[] zonesOpened = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
||||
result.setZonesOpened(zonesOpened);
|
||||
}
|
||||
}
|
||||
|
||||
pageOffset = panelType == PanelType.EVO48 ? 46 : 52;
|
||||
firstBlock = Arrays.copyOfRange(firstPage, 40, pageOffset);
|
||||
private void createZoneTamperedFlags(ZoneStateFlags result, byte[] firstPage, byte[] secondPage) {
|
||||
int pageOffset = panelType == PanelType.EVO48 ? 46 : 52;
|
||||
byte[] firstBlock = Arrays.copyOfRange(firstPage, 40, pageOffset);
|
||||
if (panelType != PanelType.EVO192) {
|
||||
result.setZonesTampered(firstBlock);
|
||||
} else {
|
||||
@ -196,18 +212,51 @@ public class EvoCommunicator extends GenericCommunicator implements IParadoxComm
|
||||
byte[] zonesTampered = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
||||
result.setZonesTampered(zonesTampered);
|
||||
}
|
||||
}
|
||||
|
||||
pageOffset = panelType == PanelType.EVO48 ? 58 : 64;
|
||||
firstBlock = Arrays.copyOfRange(firstPage, 52, pageOffset);
|
||||
private void createZoneLowbatteryFlags(ZoneStateFlags result, byte[] firstPage, byte[] secondPage) {
|
||||
int pageOffset = panelType == PanelType.EVO48 ? 58 : 64;
|
||||
byte[] firstBlock = Arrays.copyOfRange(firstPage, 52, pageOffset);
|
||||
if (panelType != PanelType.EVO192) {
|
||||
result.setZonesTampered(firstBlock);
|
||||
result.setZonesLowBattery(firstBlock);
|
||||
} else {
|
||||
byte[] secondBlock = Arrays.copyOfRange(secondPage, 24, 36);
|
||||
byte[] zonesLowBattery = ParadoxUtil.mergeByteArrays(firstBlock, secondBlock);
|
||||
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() {
|
||||
|
@ -41,6 +41,6 @@ public class MemoryMap {
|
||||
}
|
||||
|
||||
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,
|
||||
RAM,
|
||||
EPROM,
|
||||
PARTITION_COMMAND
|
||||
PARTITION_COMMAND,
|
||||
ZONE_COMMAND
|
||||
}
|
||||
|
@ -103,36 +103,52 @@ public class Response implements IResponse {
|
||||
byte highNibble = ParadoxUtil.getHighNibble(receivedCommand);
|
||||
RequestType requestType = request.getType();
|
||||
|
||||
// For EPROM and RAM messages received command must be 0x5x
|
||||
if (requestType == RequestType.EPROM || requestType == RequestType.RAM) {
|
||||
if (highNibble == 0x5) {
|
||||
header = Arrays.copyOfRange(packetBytes, 0, 22);
|
||||
payload = Arrays.copyOfRange(packetBytes, 22, packetBytes.length - 1);
|
||||
return;
|
||||
}
|
||||
switch (requestType) {
|
||||
// For EPROM and RAM messages received command must be 0x5x
|
||||
case EPROM:
|
||||
case RAM:
|
||||
if (highNibble == 0x5) {
|
||||
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
|
||||
// 0x7
|
||||
} else if (requestType == RequestType.LOGON_SEQUENCE) {
|
||||
switch (highNibble) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x7:
|
||||
case LOGON_SEQUENCE:
|
||||
switch (highNibble) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
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);
|
||||
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;
|
||||
}
|
||||
} else if (requestType == RequestType.PARTITION_COMMAND) {
|
||||
if (highNibble == 0x4) {
|
||||
header = Arrays.copyOfRange(packetBytes, 0, 16);
|
||||
payload = Arrays.copyOfRange(packetBytes, 16, 16 + packetBytes[1]);
|
||||
logger.debug("Received valid response for partition command");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZONE_COMMAND:
|
||||
if (highNibble == 0xD) {
|
||||
header = Arrays.copyOfRange(packetBytes, 0, 16);
|
||||
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
|
||||
// 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;
|
||||
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 ROUNDS = 14;
|
||||
|
||||
private static final int[] lTable = new int[TABLE_SIZE];
|
||||
private static final int[] aTable = new int[TABLE_SIZE];
|
||||
private static final int[] L_TABLE = new int[TABLE_SIZE];
|
||||
private static final int[] A_TABLE = new int[TABLE_SIZE];
|
||||
|
||||
private static EncryptionHandler instance = new EncryptionHandler(new byte[] {});
|
||||
static {
|
||||
@ -62,7 +62,7 @@ public class EncryptionHandler {
|
||||
int a = 1;
|
||||
int d;
|
||||
for (int index = 0; index < 255; index++) {
|
||||
aTable[index] = a & 0xFF;
|
||||
A_TABLE[index] = a & 0xFF;
|
||||
/* Multiply by three */
|
||||
d = (a & 0x80) & 0xFF;
|
||||
a <<= 1;
|
||||
@ -70,13 +70,13 @@ public class EncryptionHandler {
|
||||
a ^= 0x1b;
|
||||
a &= 0xFF;
|
||||
}
|
||||
a ^= aTable[index];
|
||||
a ^= A_TABLE[index];
|
||||
a &= 0xFF;
|
||||
/* Set the log table value */
|
||||
lTable[aTable[index]] = index & 0xFF;
|
||||
L_TABLE[A_TABLE[index]] = index & 0xFF;
|
||||
}
|
||||
aTable[255] = aTable[0];
|
||||
lTable[0] = 0;
|
||||
A_TABLE[255] = A_TABLE[0];
|
||||
L_TABLE[0] = 0;
|
||||
}
|
||||
|
||||
private final int[] expandedKey = new int[KEY_LENGTH];
|
||||
@ -196,9 +196,7 @@ public class EncryptionHandler {
|
||||
if (i % 8 == 0) {
|
||||
int tmp = temp[0];
|
||||
|
||||
for (int j = 1; j < 4; j++) {
|
||||
temp[j - 1] = temp[j];
|
||||
}
|
||||
System.arraycopy(temp, 1, temp, 0, temp.length - 1);
|
||||
|
||||
temp[3] = tmp;
|
||||
temp[0] ^= EncryptionHandlerConstants.RCON[(i / 8 - 1)];
|
||||
@ -212,9 +210,9 @@ public class EncryptionHandler {
|
||||
}
|
||||
|
||||
private int gmul(int c, int b) {
|
||||
int s = lTable[c] + lTable[b];
|
||||
int s = L_TABLE[c] + L_TABLE[b];
|
||||
s %= 255;
|
||||
s = aTable[s];
|
||||
s = A_TABLE[s];
|
||||
if (b == 0 || c == 0) {
|
||||
s = 0;
|
||||
}
|
||||
@ -267,8 +265,7 @@ public class EncryptionHandler {
|
||||
int[] tmpArray = new int[] { 0, 0, 0, 0 };
|
||||
for (int i = 1; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
int[][][] shifts = EncryptionHandlerConstants.SHIFTS;
|
||||
int index = i * 4 + (j + shifts[0][i][d]) % 4;
|
||||
int index = i * 4 + (j + EncryptionHandlerConstants.SHIFTS[0][i][d]) % 4;
|
||||
tmpArray[j] = a[index];
|
||||
}
|
||||
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
|
||||
*/
|
||||
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.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.LoggerFactory;
|
||||
|
||||
@ -23,8 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum PartitionCommand {
|
||||
UNKNOWN(0),
|
||||
public enum PartitionCommand implements Command {
|
||||
ARM(2),
|
||||
STAY_ARM(3),
|
||||
INSTANT_ARM(4),
|
||||
@ -32,7 +38,7 @@ public enum PartitionCommand {
|
||||
DISARM(6),
|
||||
BEEP(8);
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PartitionCommand.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PartitionCommand.class);
|
||||
|
||||
private int command;
|
||||
|
||||
@ -44,12 +50,20 @@ public enum PartitionCommand {
|
||||
return command;
|
||||
}
|
||||
|
||||
public static PartitionCommand parse(String command) {
|
||||
public static @Nullable PartitionCommand parse(String command) {
|
||||
try {
|
||||
return PartitionCommand.valueOf(command);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Unable to parse command={}. Fallback to UNKNOWN.", command);
|
||||
return PartitionCommand.UNKNOWN;
|
||||
LOGGER.debug("Unable to parse command={}. Fallback to UNKNOWN.", command);
|
||||
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
|
||||
*/
|
||||
package org.openhab.binding.paradoxalarm.internal.communication.messages;
|
||||
package org.openhab.binding.paradoxalarm.internal.communication.messages.partition;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
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
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CommandPayload implements IPayload {
|
||||
public class PartitionCommandPayload implements IPayload {
|
||||
|
||||
private static final int BYTES_LENGTH = 15;
|
||||
|
||||
private final byte MESSAGE_START = 0x40;
|
||||
private final byte PAYLOAD_SIZE = 0x0f;
|
||||
private final byte[] EMPTY_FOUR_BYTES = { 0, 0, 0, 0 };
|
||||
private final byte CHECKSUM = 0;
|
||||
private static final byte MESSAGE_START = 0x40;
|
||||
private static final byte PAYLOAD_SIZE = 0x0f;
|
||||
private static final byte[] EMPTY_FOUR_BYTES = { 0, 0, 0, 0 };
|
||||
private static final byte CHECKSUM = 0;
|
||||
|
||||
private final int partitionNumber;
|
||||
private final PartitionCommand command;
|
||||
|
||||
public CommandPayload(int partitionNumber, PartitionCommand command) {
|
||||
public PartitionCommandPayload(int partitionNumber, PartitionCommand command) {
|
||||
this.partitionNumber = partitionNumber;
|
||||
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);
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUid)
|
||||
.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);
|
||||
|
||||
thingDiscovered(result);
|
||||
@ -105,7 +105,7 @@ public class ParadoxDiscoveryService extends AbstractDiscoveryService {
|
||||
ThingUID thingUID = new ThingUID(ZONE_THING_TYPE_UID, bridgeUid, thingId);
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUid)
|
||||
.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);
|
||||
|
||||
thingDiscovered(result);
|
||||
|
@ -67,7 +67,7 @@ public class ParadoxAlarmBindingConstants {
|
||||
public static final String PANEL_BOARD_VOLTAGE = "boardVoltage";
|
||||
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";
|
||||
@Deprecated // After implementation of channels for every possible state, the summarized additional states is no
|
||||
// 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_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_TAMPERED_CHANNEL_UID = "tampered";
|
||||
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
|
||||
public static final StringType STATE_OFFLINE = new StringType("Offline");
|
||||
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.unit.Units;
|
||||
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.
|
||||
@ -35,8 +33,6 @@ import org.slf4j.LoggerFactory;
|
||||
@NonNullByDefault
|
||||
public class ParadoxPanelHandler extends EntityBaseHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ParadoxPanelHandler.class);
|
||||
|
||||
public ParadoxPanelHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import static org.openhab.binding.paradoxalarm.internal.handlers.ParadoxAlarmBin
|
||||
import java.util.List;
|
||||
|
||||
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.Partition;
|
||||
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) {
|
||||
return value ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
|
||||
}
|
||||
@ -124,4 +104,24 @@ public class ParadoxPartitionHandler extends EntityBaseHandler {
|
||||
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.openhab.binding.paradoxalarm.internal.model.ParadoxPanel;
|
||||
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.OpenClosedType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -41,15 +45,21 @@ public class ParadoxZoneHandler extends EntityBaseHandler {
|
||||
|
||||
@Override
|
||||
protected void updateEntity() {
|
||||
int index = calculateEntityIndex();
|
||||
ParadoxIP150BridgeHandler bridge = (ParadoxIP150BridgeHandler) getBridge().getHandler();
|
||||
ParadoxPanel panel = bridge.getPanel();
|
||||
ParadoxIP150BridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler == null) {
|
||||
logger.debug("Paradox bridge handler is null. Skipping update.");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int index = calculateEntityIndex();
|
||||
if (zones.size() <= index) {
|
||||
logger.debug("Attempted to access zone out of bounds of current zone list. Index: {}, List: {}", index,
|
||||
zones);
|
||||
@ -59,9 +69,23 @@ public class ParadoxZoneHandler extends EntityBaseHandler {
|
||||
Zone zone = zones.get(index);
|
||||
if (zone != null) {
|
||||
updateState(ZONE_LABEL_CHANNEL_UID, new StringType(zone.getLabel()));
|
||||
updateState(ZONE_OPENED_CHANNEL_UID, booleanToContactState(zone.getZoneState().isOpened()));
|
||||
updateState(ZONE_TAMPERED_CHANNEL_UID, booleanToSwitchState(zone.getZoneState().isTampered()));
|
||||
updateState(ZONE_LOW_BATTERY_CHANNEL_UID, booleanToSwitchState(zone.getZoneState().hasLowBattery()));
|
||||
ZoneState zoneState = zone.getZoneState();
|
||||
if (zoneState != null) {
|
||||
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) {
|
||||
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;
|
||||
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.PartitionCommandRequest;
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.RequestType;
|
||||
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.communication.IRequest;
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.messages.partition.PartitionCommand;
|
||||
import org.openhab.binding.paradoxalarm.internal.handlers.Commandable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -51,16 +47,13 @@ public class Partition extends Entity implements Commandable {
|
||||
@Override
|
||||
public void handleCommand(String command) {
|
||||
PartitionCommand partitionCommand = PartitionCommand.parse(command);
|
||||
if (partitionCommand == PartitionCommand.UNKNOWN) {
|
||||
logger.debug("Command UNKNOWN will be ignored.");
|
||||
if (partitionCommand == null) {
|
||||
logger.debug("Command {} is parsed to null. Skipping it", command);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Submitting command={} for partition=[{}]", partitionCommand, this);
|
||||
CommandPayload payload = new CommandPayload(getId(), partitionCommand);
|
||||
ParadoxIPPacket packet = new ParadoxIPPacket(payload.getBytes())
|
||||
.setMessageType(HeaderMessageType.SERIAL_PASSTHRU_REQUEST);
|
||||
PartitionCommandRequest request = new PartitionCommandRequest(RequestType.PARTITION_COMMAND, packet, null);
|
||||
IRequest request = partitionCommand.getRequest(getId());
|
||||
getPanel().getCommunicator().submitRequest(request);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,11 @@
|
||||
*/
|
||||
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.LoggerFactory;
|
||||
|
||||
@ -22,23 +27,36 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* @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 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);
|
||||
}
|
||||
|
||||
public ZoneState getZoneState() {
|
||||
public @Nullable ZoneState getZoneState() {
|
||||
return zoneState;
|
||||
}
|
||||
|
||||
public void setZoneState(ZoneState zoneState) {
|
||||
this.zoneState = zoneState;
|
||||
logger.debug("Zone {} state updated to:\tOpened: {}, Tampered: {}, LowBattery: {}", getLabel(),
|
||||
zoneState.isOpened(), zoneState.isTampered(), zoneState.hasLowBattery());
|
||||
logger.debug("Zone {} state updated to: {}", getLabel(), zoneState);
|
||||
}
|
||||
|
||||
@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
|
||||
*/
|
||||
public class ZoneState {
|
||||
// Regular states
|
||||
private boolean isOpened;
|
||||
private boolean isTampered;
|
||||
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) {
|
||||
this.isOpened = isOpened;
|
||||
this.isTampered = isTampered;
|
||||
@ -51,4 +62,77 @@ public class ZoneState {
|
||||
public void setHasLowBattery(boolean 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[] zonesTampered;
|
||||
private byte[] zonesLowBattery;
|
||||
private byte[] zoneSpecialFlags;
|
||||
|
||||
public byte[] getZonesOpened() {
|
||||
return zonesOpened;
|
||||
@ -45,4 +46,12 @@ public class ZoneStateFlags {
|
||||
public void setZonesLowBattery(byte[] 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
|
||||
public ZoneState calculateZoneState(int id, ZoneStateFlags zoneStateFlags) {
|
||||
|
||||
int index = (id - 1) / 8;
|
||||
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();
|
||||
boolean isOpened = ParadoxUtil.isBitSet(zonesOpened[index], bitNumber);
|
||||
|
||||
@ -77,6 +78,38 @@ public class EvoParser extends AbstractParser {
|
||||
byte[] zonesLowBattery = zoneStateFlags.getZonesLowBattery();
|
||||
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 {
|
||||
|
||||
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) {
|
||||
int result = 0;
|
||||
@ -50,28 +51,28 @@ public class ParadoxUtil {
|
||||
}
|
||||
|
||||
public static void printPacket(String description, byte[] array) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Packet payload size: {}", array[1]);
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Packet payload size: {}", array[1]);
|
||||
printByteArray(description, array, array[1] + 16);
|
||||
}
|
||||
}
|
||||
|
||||
public static void printByteArray(String description, byte[] array) {
|
||||
if (array == null) {
|
||||
logger.trace("Array is null");
|
||||
LOGGER.trace("Array is null");
|
||||
return;
|
||||
}
|
||||
printByteArray(description, array, array.length);
|
||||
}
|
||||
|
||||
public static void printByteArray(String description, byte[] array, int length) {
|
||||
if (!logger.isTraceEnabled()) {
|
||||
if (!LOGGER.isTraceEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String result = byteArrayToString(array, length);
|
||||
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();
|
||||
return byteArray;
|
||||
} catch (IOException e) {
|
||||
logger.warn("Exception merging arrays:", e);
|
||||
LOGGER.warn("Exception merging arrays:", e);
|
||||
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-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.bypassReady.label = 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.description = Send command
|
||||
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.DISARM = Disarm
|
||||
channel-type.paradoxalarm.command.state.option.BEEP = Keyboard Beep
|
||||
channel-type.paradoxalarm.command.label = Communicator Command
|
||||
channel-type.paradoxalarm.command.description = Send Command
|
||||
channel-type.paradoxalarm.command.label = Zone 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.description = Status of connection to Paradox system
|
||||
channel-type.paradoxalarm.forceReady.label = 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.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.description = Partition in Entry Delay
|
||||
channel-type.paradoxalarm.inExitDelay.label = 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.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.description = Partition is Inhibit Ready
|
||||
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.partitionLabel.label = Partition Label
|
||||
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.description = Partition ready to arm
|
||||
channel-type.paradoxalarm.serialNumber.label = 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.description = State of partition
|
||||
channel-type.paradoxalarm.stayInstantReady.label = 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.description = State of zone
|
||||
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.zoneLabel.label = Zone Label
|
||||
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="tampered" typeId="tamperedState"/>
|
||||
<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>
|
||||
|
||||
<config-description>
|
||||
@ -44,4 +54,55 @@
|
||||
<description>State of zone</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</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>
|
||||
|
@ -12,11 +12,12 @@
|
||||
*/
|
||||
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.openhab.binding.paradoxalarm.internal.communication.messages.CommandPayload;
|
||||
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.util.ParadoxUtil;
|
||||
|
||||
/**
|
||||
@ -25,19 +26,20 @@ import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TestCreateCommandPayload {
|
||||
|
||||
@Test
|
||||
public void testCreatePayload() {
|
||||
for (PartitionCommand command : PartitionCommand.values()) {
|
||||
for (int partitionNumber = 1; partitionNumber <= 8; partitionNumber++) {
|
||||
CommandPayload payload = new CommandPayload(partitionNumber, command);
|
||||
PartitionCommandPayload payload = new PartitionCommandPayload(partitionNumber, command);
|
||||
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();
|
||||
int payloadIndexOfByteToCheck = 6 + (partitionNumber - 1) / 2;
|
||||
byte byteValue = bytes[payloadIndexOfByteToCheck];
|
||||
|
@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.paradoxalarm.internal.communication.crypto.EncryptionHandler;
|
||||
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
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TestEncryptionHandler {
|
||||
|
||||
private static final String INPUT_STRING = "My test string for encryption.";
|
||||
|
@ -12,17 +12,20 @@
|
||||
*/
|
||||
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 org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
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.HeaderCommand;
|
||||
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.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.util.ParadoxUtil;
|
||||
import org.slf4j.Logger;
|
||||
@ -33,6 +36,7 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TestGetBytes {
|
||||
|
||||
private static final int PARTITION_NUMBER = 1;
|
||||
@ -61,18 +65,18 @@ public class TestGetBytes {
|
||||
assertTrue(Arrays.equals(packetBytes, EXPECTED1));
|
||||
}
|
||||
|
||||
private static final byte[] EXPECTED_COMMAND_PAYLOAD = { 0x40, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 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 };
|
||||
|
||||
@Test
|
||||
public void testCommandPayload() {
|
||||
CommandPayload payload = new CommandPayload(PARTITION_NUMBER, PartitionCommand.ARM);
|
||||
public void testPartitionCommandPayload() {
|
||||
PartitionCommandPayload payload = new PartitionCommandPayload(PARTITION_NUMBER, PartitionCommand.ARM);
|
||||
final byte[] packetBytes = payload.getBytes();
|
||||
|
||||
ParadoxUtil.printByteArray("Expected =", EXPECTED_COMMAND_PAYLOAD);
|
||||
ParadoxUtil.printByteArray("Expected =", EXPECTED_PARTITION_COMMAND_PAYLOAD);
|
||||
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,
|
||||
@ -88,4 +92,39 @@ public class TestGetBytes {
|
||||
ParadoxUtil.printByteArray("Expected =", EXPECTED_MEMORY_PAYLOAD);
|
||||
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;
|
||||
|
||||
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.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
||||
|
||||
@ -22,6 +23,7 @@ import org.openhab.binding.paradoxalarm.internal.util.ParadoxUtil;
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TestParadoxUtil {
|
||||
|
||||
@Test
|
||||
@ -30,19 +32,19 @@ public class TestParadoxUtil {
|
||||
final int rate = 16;
|
||||
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 };
|
||||
assertArrayEquals(EXPECTED_RESULT, extendedArray); //
|
||||
assertArrayEquals(expectedResult, extendedArray); //
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeArrays() {
|
||||
final byte[] ARR1 = { 0x01, 0x02, 0x03 };
|
||||
final byte[] ARR2 = { 0x04, 0x05, 0x06 };
|
||||
final byte[] ARR3 = { 0x07, 0x08, 0x09 };
|
||||
byte[] mergedArrays = ParadoxUtil.mergeByteArrays(ARR1, ARR2, ARR3);
|
||||
final byte[] arr1 = { 0x01, 0x02, 0x03 };
|
||||
final byte[] arr2 = { 0x04, 0x05, 0x06 };
|
||||
final byte[] arr3 = { 0x07, 0x08, 0x09 };
|
||||
byte[] mergedArrays = ParadoxUtil.mergeByteArrays(arr1, arr2, arr3);
|
||||
|
||||
final byte[] EXPECTED_RESULT = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
||||
assertArrayEquals(EXPECTED_RESULT, mergedArrays);
|
||||
final byte[] expectedResult = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
||||
assertArrayEquals(expectedResult, mergedArrays);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user