mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[powermax] Introduce some new channels for better status reporting (#10624)
* [powermax] Introduce some new channels for better status reporting * New panel channels: - Ringing indicator (the siren is currently sounding) - Date/time the last message was received from the panel - List of all active alarms and alerts (similar to panel's Memory list, but items get removed from the list as conditions resolve) * New zone channels: - Alarmed indicator (zone is in alarm, or has an alarm in memory) - Tamper alarm indicator (same but for a tamper condition) - Inactive indicator - Tamper indicator (zone is actively tampered right now) - Last status message received for this zone, and when * Use descriptive names for zones in log messages. If you create a Thing for a zone, it will use that Thing's label in all reporting for that zone. If there's no Thing then it will attempt to use the zone label from the panel (e.g. "Basement"). * Clear all channels during startup to keep from displaying stale values loaded from persistence * Also includes some minor SAT fixes (checkstyle, spotbugs) Signed-off-by: Ron Isaacson <isaacson.ron@gmail.com> * Incorporate review feedback from lolodomo Signed-off-by: Ron Isaacson <isaacson.ron@gmail.com>
This commit is contained in:
parent
0a1f23f98d
commit
d4983c4991
@ -47,8 +47,11 @@ public class PowermaxBindingConstants {
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String MODE = "mode";
|
||||
public static final String LAST_MESSAGE_TIME = "last_message_time";
|
||||
public static final String ACTIVE_ALERTS = "active_alerts";
|
||||
public static final String TROUBLE = "trouble";
|
||||
public static final String ALERT_IN_MEMORY = "alert_in_memory";
|
||||
public static final String RINGING = "ringing";
|
||||
public static final String SYSTEM_STATUS = "system_status";
|
||||
public static final String READY = "ready";
|
||||
public static final String WITH_ZONES_BYPASSED = "with_zones_bypassed";
|
||||
@ -58,8 +61,14 @@ public class PowermaxBindingConstants {
|
||||
public static final String TRIPPED = "tripped";
|
||||
public static final String LAST_TRIP = "last_trip";
|
||||
public static final String BYPASSED = "bypassed";
|
||||
public static final String ALARMED = "alarmed";
|
||||
public static final String TAMPER_ALARM = "tamper_alarm";
|
||||
public static final String INACTIVE = "inactive";
|
||||
public static final String TAMPERED = "tampered";
|
||||
public static final String ARMED = "armed";
|
||||
public static final String LOCKED = "locked";
|
||||
public static final String ZONE_LAST_MESSAGE = "last_message";
|
||||
public static final String ZONE_LAST_MESSAGE_TIME = "last_message_time";
|
||||
public static final String LOW_BATTERY = "low_battery";
|
||||
public static final String PGM_STATUS = "pgm_status";
|
||||
public static final String X10_STATUS = "x10_status";
|
||||
|
@ -143,6 +143,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
|
||||
try {
|
||||
logger.trace("Powermax job...");
|
||||
updateMotionSensorState();
|
||||
updateRingingState();
|
||||
if (isConnected()) {
|
||||
checkKeepAlive();
|
||||
commManager.retryDownloadSetup(remainingDownloadAttempts);
|
||||
@ -254,6 +255,23 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn off the Ringing flag when the bell time expires
|
||||
*/
|
||||
private void updateRingingState() {
|
||||
if (currentState != null && Boolean.TRUE.equals(currentState.ringing.getValue())) {
|
||||
long now = System.currentTimeMillis();
|
||||
long bellTime = getPanelSettings().getBellTime() * ONE_MINUTE;
|
||||
|
||||
if ((currentState.ringingSince.getValue() + bellTime) < now) {
|
||||
PowermaxState updateState = commManager.createNewState();
|
||||
updateState.ringing.setValue(false);
|
||||
updateChannelsFromAlarmState(RINGING, updateState);
|
||||
currentState.merge(updateState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that we're actively communicating with the panel
|
||||
*/
|
||||
@ -266,8 +284,8 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
|
||||
commManager.sendRestoreMessage();
|
||||
currentState.lastKeepAlive.setValue(now);
|
||||
} else if (!Boolean.TRUE.equals(currentState.downloadMode.getValue())
|
||||
&& (currentState.lastMessageReceived.getValue() != null)
|
||||
&& ((now - currentState.lastMessageReceived.getValue()) > FIVE_MINUTES)) {
|
||||
&& (currentState.lastMessageTime.getValue() != null)
|
||||
&& ((now - currentState.lastMessageTime.getValue()) > FIVE_MINUTES)) {
|
||||
// In Standard mode: ping the panel every so often to detect disconnects
|
||||
commManager.sendMessage(PowermaxSendType.STATUS);
|
||||
}
|
||||
@ -281,6 +299,7 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
|
||||
openConnection();
|
||||
logger.debug("openConnection(): connected");
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateChannelsFromAlarmState(currentState);
|
||||
if (forceStandardMode) {
|
||||
currentState.powerlinkMode.setValue(false);
|
||||
updateChannelsFromAlarmState(MODE, currentState);
|
||||
@ -455,12 +474,12 @@ public class PowermaxBridgeHandler extends BaseBridgeHandler implements Powermax
|
||||
|
||||
boolean doProcessSettings = (updateState.powerlinkMode.getValue() != null);
|
||||
|
||||
for (int i = 1; i <= getPanelSettings().getNbZones(); i++) {
|
||||
getPanelSettings().getZoneRange().forEach(i -> {
|
||||
if (Boolean.TRUE.equals(updateState.getZone(i).armed.getValue())
|
||||
&& Boolean.TRUE.equals(currentState.getZone(i).bypassed.getValue())) {
|
||||
updateState.getZone(i).armed.setValue(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateState.keepOnlyDifferencesWith(currentState);
|
||||
updateChannelsFromAlarmState(updateState);
|
||||
|
@ -184,10 +184,10 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
|
||||
int num = getConfigAs(PowermaxZoneConfiguration.class).zoneNumber.intValue();
|
||||
|
||||
for (Value<?> value : state.getZone(num).getValues()) {
|
||||
String v_channel = value.getChannel();
|
||||
String vChannel = value.getChannel();
|
||||
|
||||
if (channel.equals(v_channel) && (value.getValue() != null)) {
|
||||
updateState(v_channel, value.getState());
|
||||
if (channel.equals(vChannel) && (value.getValue() != null)) {
|
||||
updateState(vChannel, value.getState());
|
||||
}
|
||||
}
|
||||
} else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) {
|
||||
@ -256,6 +256,9 @@ public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPa
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
logger.debug("Set handler status to ONLINE for thing {} (zone number {} paired)",
|
||||
getThing().getUID(), config.zoneNumber);
|
||||
|
||||
logger.debug("Using name '{}' for {}", getThing().getLabel(), getThing().getUID());
|
||||
zoneSettings.setName(getThing().getLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.powermax.internal.message;
|
||||
|
||||
/**
|
||||
* All defined alarm types
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public enum PowermaxAlarmType {
|
||||
|
||||
ALARM_TYPE_1(0x01, "Intruder"),
|
||||
ALARM_TYPE_2(0x02, "Intruder"),
|
||||
ALARM_TYPE_3(0x03, "Intruder"),
|
||||
ALARM_TYPE_4(0x04, "Intruder"),
|
||||
ALARM_TYPE_5(0x05, "Intruder"),
|
||||
ALARM_TYPE_6(0x06, "Tamper"),
|
||||
ALARM_TYPE_7(0x07, "Tamper"),
|
||||
ALARM_TYPE_8(0x08, "Tamper"),
|
||||
ALARM_TYPE_9(0x09, "Tamper"),
|
||||
ALARM_TYPE_10(0x0B, "Panic"),
|
||||
ALARM_TYPE_11(0x0C, "Panic"),
|
||||
ALARM_TYPE_12(0x20, "Fire"),
|
||||
ALARM_TYPE_13(0x23, "Emergency"),
|
||||
ALARM_TYPE_14(0x49, "Gas"),
|
||||
ALARM_TYPE_15(0x4D, "Flood");
|
||||
|
||||
private int code;
|
||||
private String label;
|
||||
|
||||
private PowermaxAlarmType(int code, String label) {
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the code identifying the alarm type
|
||||
*/
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the label associated to the alarm type
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ENUM value from its identifying code
|
||||
*
|
||||
* @param code the identifying code
|
||||
*
|
||||
* @return the corresponding ENUM value
|
||||
*
|
||||
* @throws IllegalArgumentException if no ENUM value corresponds to this code
|
||||
*/
|
||||
public static PowermaxAlarmType fromCode(int code) throws IllegalArgumentException {
|
||||
for (PowermaxAlarmType alarmType : PowermaxAlarmType.values()) {
|
||||
if (alarmType.getCode() == code) {
|
||||
return alarmType;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid code: " + code);
|
||||
}
|
||||
}
|
@ -259,7 +259,7 @@ public class PowermaxCommManager implements PowermaxMessageEventListener {
|
||||
updateState = createNewState();
|
||||
}
|
||||
|
||||
updateState.lastMessageReceived.setValue(System.currentTimeMillis());
|
||||
updateState.lastMessageTime.setValue(System.currentTimeMillis());
|
||||
|
||||
if (updateState.getUpdateSettings() != null) {
|
||||
panelSettings.updateRawSettings(updateState.getUpdateSettings());
|
||||
|
@ -61,8 +61,8 @@ public class PowermaxEventLogMessage extends PowermaxBaseMessage {
|
||||
String timestamp = String.format("%02d/%02d/%04d %02d:%02d:%02d", day, month, year, hour, minute, second);
|
||||
byte eventZone = message[10];
|
||||
byte logEvent = message[11];
|
||||
String logEventStr = PowermaxMessageConstants.getSystemEventString(logEvent & 0x000000FF);
|
||||
String logUserStr = PowermaxMessageConstants.getZoneOrUserString(eventZone & 0x000000FF);
|
||||
String logEventStr = PowermaxMessageConstants.getSystemEvent(logEvent & 0x000000FF).toString();
|
||||
String logUserStr = panelSettings.getZoneOrUserName(eventZone & 0x000000FF);
|
||||
|
||||
String eventStr;
|
||||
if (panelSettings.getPanelType().getPartitions() > 1) {
|
||||
|
@ -12,6 +12,10 @@
|
||||
*/
|
||||
package org.openhab.binding.powermax.internal.message;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Constants used in Powermax messages
|
||||
*
|
||||
@ -22,81 +26,377 @@ public class PowermaxMessageConstants {
|
||||
private PowermaxMessageConstants() {
|
||||
}
|
||||
|
||||
private static String getValue(String[] table, int index) {
|
||||
return (((index >= 0) && (index < table.length)) ? table[index] : "UNKNOWN");
|
||||
// System events
|
||||
|
||||
public static enum PowermaxSysEventType {
|
||||
NONE,
|
||||
ALARM,
|
||||
SILENT_ALARM,
|
||||
ALERT,
|
||||
PANIC,
|
||||
TROUBLE,
|
||||
RESTORE,
|
||||
GENERAL_RESTORE,
|
||||
CANCEL,
|
||||
RESET;
|
||||
}
|
||||
|
||||
public static class PowermaxSysEvent {
|
||||
private final String name;
|
||||
private final PowermaxSysEventType type;
|
||||
private final int restoreFor;
|
||||
|
||||
protected PowermaxSysEvent(String name, PowermaxSysEventType type, int restoreFor) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.restoreFor = restoreFor;
|
||||
}
|
||||
|
||||
protected static PowermaxSysEvent of(String name) {
|
||||
return new PowermaxSysEvent(name, PowermaxSysEventType.NONE, 0);
|
||||
}
|
||||
|
||||
protected static PowermaxSysEvent of(String name, PowermaxSysEventType type) {
|
||||
return new PowermaxSysEvent(name, type, 0);
|
||||
}
|
||||
|
||||
protected static PowermaxSysEvent of(String name, PowermaxSysEventType type, int restoreFor) {
|
||||
return new PowermaxSysEvent(name, type, restoreFor);
|
||||
}
|
||||
|
||||
public PowermaxSysEventType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public int getRestoreFor() {
|
||||
return this.restoreFor;
|
||||
}
|
||||
|
||||
public boolean isAlarm() {
|
||||
return (this.type == PowermaxSysEventType.ALARM);
|
||||
}
|
||||
|
||||
public boolean isSilentAlarm() {
|
||||
return (this.type == PowermaxSysEventType.SILENT_ALARM);
|
||||
}
|
||||
|
||||
public boolean isAlert() {
|
||||
return (this.type == PowermaxSysEventType.ALERT);
|
||||
}
|
||||
|
||||
public boolean isPanic() {
|
||||
return (this.type == PowermaxSysEventType.PANIC);
|
||||
}
|
||||
|
||||
public boolean isTrouble() {
|
||||
return (this.type == PowermaxSysEventType.TROUBLE);
|
||||
}
|
||||
|
||||
public boolean isRestore() {
|
||||
return (this.type == PowermaxSysEventType.RESTORE);
|
||||
}
|
||||
|
||||
public boolean isGeneralRestore() {
|
||||
return (this.type == PowermaxSysEventType.GENERAL_RESTORE);
|
||||
}
|
||||
|
||||
public boolean isCancel() {
|
||||
return (this.type == PowermaxSysEventType.CANCEL);
|
||||
}
|
||||
|
||||
public boolean isReset() {
|
||||
return (this.type == PowermaxSysEventType.RESET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// Important note: in all of the following lists, each entry line ends
|
||||
// with an empty "//" comment. This is to prevent the "spotless" code
|
||||
// formatter from trying to wrap these lines in a way that makes them
|
||||
// much less readable.
|
||||
|
||||
private static final PowermaxSysEvent UNKNOWN_SYSTEM_EVENT = PowermaxSysEvent.of("UNKNOWN");
|
||||
|
||||
private static final Map<Integer, PowermaxSysEvent> SYSTEM_EVENTS = Map.ofEntries( //
|
||||
entry(0x00, PowermaxSysEvent.of("None")), //
|
||||
entry(0x01, PowermaxSysEvent.of("Interior Alarm", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x02, PowermaxSysEvent.of("Perimeter Alarm", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x03, PowermaxSysEvent.of("Delay Alarm", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x04, PowermaxSysEvent.of("24h Silent Alarm", PowermaxSysEventType.SILENT_ALARM)), //
|
||||
entry(0x05, PowermaxSysEvent.of("24h Audible Alarm", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x06, PowermaxSysEvent.of("Tamper", PowermaxSysEventType.ALERT)), //
|
||||
entry(0x07, PowermaxSysEvent.of("Control Panel Tamper", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x08, PowermaxSysEvent.of("Tamper Alarm", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x09, PowermaxSysEvent.of("Tamper Alarm", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x0A, PowermaxSysEvent.of("Communication Loss", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x0B, PowermaxSysEvent.of("Panic From KeyKeyfob", PowermaxSysEventType.PANIC)), //
|
||||
entry(0x0C, PowermaxSysEvent.of("Panic From Control Panel", PowermaxSysEventType.PANIC)), //
|
||||
entry(0x0D, PowermaxSysEvent.of("Duress", PowermaxSysEventType.SILENT_ALARM)), //
|
||||
entry(0x0E, PowermaxSysEvent.of("Confirm Alarm", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x0F, PowermaxSysEvent.of("General Trouble", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x10, PowermaxSysEvent.of("General Trouble Restore", PowermaxSysEventType.RESTORE, 0x0F)), //
|
||||
entry(0x11, PowermaxSysEvent.of("Interior Restore")), //
|
||||
entry(0x12, PowermaxSysEvent.of("Perimeter Restore")), //
|
||||
entry(0x13, PowermaxSysEvent.of("Delay Restore")), //
|
||||
entry(0x14, PowermaxSysEvent.of("24h Silent Restore")), //
|
||||
entry(0x15, PowermaxSysEvent.of("24h Audible Restore")), //
|
||||
entry(0x16, PowermaxSysEvent.of("Tamper Restore", PowermaxSysEventType.RESTORE, 0x06)), //
|
||||
entry(0x17, PowermaxSysEvent.of("Control Panel Tamper Restore")), //
|
||||
entry(0x18, PowermaxSysEvent.of("Tamper Restore")), //
|
||||
entry(0x19, PowermaxSysEvent.of("Tamper Restore")), //
|
||||
entry(0x1A, PowermaxSysEvent.of("Communication Restore")), //
|
||||
entry(0x1B, PowermaxSysEvent.of("Cancel Alarm", PowermaxSysEventType.CANCEL)), //
|
||||
entry(0x1C, PowermaxSysEvent.of("General Restore", PowermaxSysEventType.GENERAL_RESTORE)), //
|
||||
entry(0x1D, PowermaxSysEvent.of("Trouble Restore")), //
|
||||
entry(0x1E, PowermaxSysEvent.of("Not used")), //
|
||||
entry(0x1F, PowermaxSysEvent.of("Recent Close")), //
|
||||
entry(0x20, PowermaxSysEvent.of("Fire", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x21, PowermaxSysEvent.of("Fire Restore")), //
|
||||
entry(0x22, PowermaxSysEvent.of("No Activity", PowermaxSysEventType.ALERT)), //
|
||||
entry(0x23, PowermaxSysEvent.of("Emergency", PowermaxSysEventType.ALERT)), //
|
||||
entry(0x24, PowermaxSysEvent.of("Not used")), //
|
||||
entry(0x25, PowermaxSysEvent.of("Disarm Latchkey", PowermaxSysEventType.ALERT)), //
|
||||
entry(0x26, PowermaxSysEvent.of("Panic Restore")), //
|
||||
entry(0x27, PowermaxSysEvent.of("Supervision (Inactive)", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x28, PowermaxSysEvent.of("Supervision Restore (Active)", PowermaxSysEventType.RESTORE, 0x27)), //
|
||||
entry(0x29, PowermaxSysEvent.of("Low Battery", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x2A, PowermaxSysEvent.of("Low Battery Restore", PowermaxSysEventType.RESTORE, 0x29)), //
|
||||
entry(0x2B, PowermaxSysEvent.of("AC Fail", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x2C, PowermaxSysEvent.of("AC Restore", PowermaxSysEventType.RESTORE, 0x2B)), //
|
||||
entry(0x2D, PowermaxSysEvent.of("Control Panel Low Battery", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x2E, PowermaxSysEvent.of("Control Panel Low Battery Restore", PowermaxSysEventType.RESTORE, 0x2D)), //
|
||||
entry(0x2F, PowermaxSysEvent.of("RF Jamming", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x30, PowermaxSysEvent.of("RF Jamming Restore", PowermaxSysEventType.RESTORE, 0x2F)), //
|
||||
entry(0x31, PowermaxSysEvent.of("Communications Failure", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x32, PowermaxSysEvent.of("Communications Restore", PowermaxSysEventType.RESTORE, 0x31)), //
|
||||
entry(0x33, PowermaxSysEvent.of("Telephone Line Failure", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x34, PowermaxSysEvent.of("Telephone Line Restore", PowermaxSysEventType.RESTORE, 0x33)), //
|
||||
entry(0x35, PowermaxSysEvent.of("Auto Test")), //
|
||||
entry(0x36, PowermaxSysEvent.of("Fuse Failure", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x37, PowermaxSysEvent.of("Fuse Restore", PowermaxSysEventType.RESTORE, 0x36)), //
|
||||
entry(0x38, PowermaxSysEvent.of("KeyKeyfob Low Battery", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x39, PowermaxSysEvent.of("KeyKeyfob Low Battery Restore", PowermaxSysEventType.RESTORE, 0x38)), //
|
||||
entry(0x3A, PowermaxSysEvent.of("Engineer Reset")), //
|
||||
entry(0x3B, PowermaxSysEvent.of("Battery Disconnect")), //
|
||||
entry(0x3C, PowermaxSysEvent.of("1-Way Keypad Low Battery", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x3D, PowermaxSysEvent.of("1-Way Keypad Low Battery Restore", PowermaxSysEventType.RESTORE, 0x3C)), //
|
||||
entry(0x3E, PowermaxSysEvent.of("1-Way Keypad Inactive", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x3F, PowermaxSysEvent.of("1-Way Keypad Restore Active", PowermaxSysEventType.RESTORE, 0x3E)), //
|
||||
entry(0x40, PowermaxSysEvent.of("Low Battery")), //
|
||||
entry(0x41, PowermaxSysEvent.of("Clean Me", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x42, PowermaxSysEvent.of("Fire Trouble", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x43, PowermaxSysEvent.of("Low Battery", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x44, PowermaxSysEvent.of("Battery Restore", PowermaxSysEventType.RESTORE, 0x43)), //
|
||||
entry(0x45, PowermaxSysEvent.of("AC Fail", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x46, PowermaxSysEvent.of("AC Restore", PowermaxSysEventType.RESTORE, 0x45)), //
|
||||
entry(0x47, PowermaxSysEvent.of("Supervision (Inactive)", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x48, PowermaxSysEvent.of("Supervision Restore (Active)", PowermaxSysEventType.RESTORE, 0x47)), //
|
||||
entry(0x49, PowermaxSysEvent.of("Gas Alert", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x4A, PowermaxSysEvent.of("Gas Alert Restore")), //
|
||||
entry(0x4B, PowermaxSysEvent.of("Gas Trouble", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x4C, PowermaxSysEvent.of("Gas Trouble Restore", PowermaxSysEventType.RESTORE, 0x4B)), //
|
||||
entry(0x4D, PowermaxSysEvent.of("Flood Alert", PowermaxSysEventType.ALARM)), //
|
||||
entry(0x4E, PowermaxSysEvent.of("Flood Alert Restore")), //
|
||||
entry(0x4F, PowermaxSysEvent.of("X-10 Trouble", PowermaxSysEventType.TROUBLE)), //
|
||||
entry(0x50, PowermaxSysEvent.of("X-10 Trouble Restore", PowermaxSysEventType.RESTORE, 0x4F)), //
|
||||
entry(0x51, PowermaxSysEvent.of("Arm Home")), //
|
||||
entry(0x52, PowermaxSysEvent.of("Arm Away")), //
|
||||
entry(0x53, PowermaxSysEvent.of("Quick Arm Home")), //
|
||||
entry(0x54, PowermaxSysEvent.of("Quick Arm Away")), //
|
||||
entry(0x55, PowermaxSysEvent.of("Disarm")), //
|
||||
entry(0x56, PowermaxSysEvent.of("Fail To Auto-Arm")), //
|
||||
entry(0x57, PowermaxSysEvent.of("Enter To Test Mode")), //
|
||||
entry(0x58, PowermaxSysEvent.of("Exit From Test Mode")), //
|
||||
entry(0x59, PowermaxSysEvent.of("Force Arm")), //
|
||||
entry(0x5A, PowermaxSysEvent.of("Auto Arm")), //
|
||||
entry(0x5B, PowermaxSysEvent.of("Instant Arm")), //
|
||||
entry(0x5C, PowermaxSysEvent.of("Bypass")), //
|
||||
entry(0x5D, PowermaxSysEvent.of("Fail To Arm")), //
|
||||
entry(0x5E, PowermaxSysEvent.of("Door Open")), //
|
||||
entry(0x5F, PowermaxSysEvent.of("Communication Established By Control Panel")), //
|
||||
entry(0x60, PowermaxSysEvent.of("System Reset", PowermaxSysEventType.RESET)), //
|
||||
entry(0x61, PowermaxSysEvent.of("Installer Programming")), //
|
||||
entry(0x62, PowermaxSysEvent.of("Wrong Password")), //
|
||||
entry(0x63, PowermaxSysEvent.of("Not Sys Event")), //
|
||||
entry(0x64, PowermaxSysEvent.of("Not Sys Event")), //
|
||||
entry(0x65, PowermaxSysEvent.of("Extreme Hot Alert")), //
|
||||
entry(0x66, PowermaxSysEvent.of("Extreme Hot Alert Restore")), //
|
||||
entry(0x67, PowermaxSysEvent.of("Freeze Alert")), //
|
||||
entry(0x68, PowermaxSysEvent.of("Freeze Alert Restore")), //
|
||||
entry(0x69, PowermaxSysEvent.of("Human Cold Alert")), //
|
||||
entry(0x6A, PowermaxSysEvent.of("Human Cold Alert Restore")), //
|
||||
entry(0x6B, PowermaxSysEvent.of("Human Hot Alert")), //
|
||||
entry(0x6C, PowermaxSysEvent.of("Human Hot Alert Restore")), //
|
||||
entry(0x6D, PowermaxSysEvent.of("Temperature Sensor Trouble")), //
|
||||
entry(0x6E, PowermaxSysEvent.of("Temperature Sensor Trouble Restore")), //
|
||||
entry(0x6F, PowermaxSysEvent.of("PIR Mask")), //
|
||||
entry(0x70, PowermaxSysEvent.of("PIR Mask Restore")), //
|
||||
entry(0x7B, PowermaxSysEvent.of("Alarmed")), //
|
||||
entry(0x7C, PowermaxSysEvent.of("Restore")), //
|
||||
entry(0x7D, PowermaxSysEvent.of("Alarmed")), //
|
||||
entry(0x7E, PowermaxSysEvent.of("Restore")), //
|
||||
entry(0x8E, PowermaxSysEvent.of("Exit Installer")), //
|
||||
entry(0x8F, PowermaxSysEvent.of("Enter Installer")) //
|
||||
);
|
||||
|
||||
/**
|
||||
* System event lookup
|
||||
*/
|
||||
public static String getSystemEventString(int code) {
|
||||
return getValue(SYSTEM_EVENT_TABLE, code);
|
||||
public static PowermaxSysEvent getSystemEvent(int code) {
|
||||
return SYSTEM_EVENTS.getOrDefault(code, UNKNOWN_SYSTEM_EVENT);
|
||||
}
|
||||
|
||||
private static final String[] SYSTEM_EVENT_TABLE = new String[] { "None", "Interior Alarm", "Perimeter Alarm",
|
||||
"Delay Alarm", "24h Silent Alarm", "24h Audible Alarm", "Tamper", "Control Panel Tamper", "Tamper Alarm",
|
||||
"Tamper Alarm", "Communication Loss", "Panic From Keyfob", "Panic From Control Panel", "Duress",
|
||||
"Confirm Alarm", "General Trouble", "General Trouble Restore", "Interior Restore", "Perimeter Restore",
|
||||
"Delay Restore", "24h Silent Restore", "24h Audible Restore", "Tamper Restore",
|
||||
"Control Panel Tamper Restore", "Tamper Restore", "Tamper Restore", "Communication Restore", "Cancel Alarm",
|
||||
"General Restore", "Trouble Restore", "Not used", "Recent Close", "Fire", "Fire Restore", "No Active",
|
||||
"Emergency", "No used", "Disarm Latchkey", "Panic Restore", "Supervision (Inactive)",
|
||||
"Supervision Restore (Active)", "Low Battery", "Low Battery Restore", "AC Fail", "AC Restore",
|
||||
"Control Panel Low Battery", "Control Panel Low Battery Restore", "RF Jamming", "RF Jamming Restore",
|
||||
"Communications Failure", "Communications Restore", "Telephone Line Failure", "Telephone Line Restore",
|
||||
"Auto Test", "Fuse Failure", "Fuse Restore", "Keyfob Low Battery", "Keyfob Low Battery Restore",
|
||||
"Engineer Reset", "Battery Disconnect", "1-Way Keypad Low Battery", "1-Way Keypad Low Battery Restore",
|
||||
"1-Way Keypad Inactive", "1-Way Keypad Restore Active", "Low Battery", "Clean Me", "Fire Trouble",
|
||||
"Low Battery", "Battery Restore", "AC Fail", "AC Restore", "Supervision (Inactive)",
|
||||
"Supervision Restore (Active)", "Gas Alert", "Gas Alert Restore", "Gas Trouble", "Gas Trouble Restore",
|
||||
"Flood Alert", "Flood Alert Restore", "X-10 Trouble", "X-10 Trouble Restore", "Arm Home", "Arm Away",
|
||||
"Quick Arm Home", "Quick Arm Away", "Disarm", "Fail To Auto-Arm", "Enter To Test Mode",
|
||||
"Exit From Test Mode", "Force Arm", "Auto Arm", "Instant Arm", "Bypass", "Fail To Arm", "Door Open",
|
||||
"Communication Established By Control Panel", "System Reset", "Installer Programming", "Wrong Password",
|
||||
"Not Sys Event", "Not Sys Event", "Extreme Hot Alert", "Extreme Hot Alert Restore", "Freeze Alert",
|
||||
"Freeze Alert Restore", "Human Cold Alert", "Human Cold Alert Restore", "Human Hot Alert",
|
||||
"Human Hot Alert Restore", "Temperature Sensor Trouble", "Temperature Sensor Trouble Restore",
|
||||
// new values partition models
|
||||
"PIR Mask", "PIR Mask Restore", "", "", "", "", "", "", "", "", "", "", "Alarmed", "Restore", "Alarmed",
|
||||
"Restore", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Exit Installer", "Enter Installer",
|
||||
"", "", "", "", "" };
|
||||
// Zone/User codes
|
||||
|
||||
private static final Map<Integer, String> ZONES_OR_USERS = Map.ofEntries( //
|
||||
entry(0x00, "System"), //
|
||||
entry(0x01, "Zone 1"), //
|
||||
entry(0x02, "Zone 2"), //
|
||||
entry(0x03, "Zone 3"), //
|
||||
entry(0x04, "Zone 4"), //
|
||||
entry(0x05, "Zone 5"), //
|
||||
entry(0x06, "Zone 6"), //
|
||||
entry(0x07, "Zone 7"), //
|
||||
entry(0x08, "Zone 8"), //
|
||||
entry(0x09, "Zone 9"), //
|
||||
entry(0x0A, "Zone 10"), //
|
||||
entry(0x0B, "Zone 11"), //
|
||||
entry(0x0C, "Zone 12"), //
|
||||
entry(0x0D, "Zone 13"), //
|
||||
entry(0x0E, "Zone 14"), //
|
||||
entry(0x0F, "Zone 15"), //
|
||||
entry(0x10, "Zone 16"), //
|
||||
entry(0x11, "Zone 17"), //
|
||||
entry(0x12, "Zone 18"), //
|
||||
entry(0x13, "Zone 19"), //
|
||||
entry(0x14, "Zone 20"), //
|
||||
entry(0x15, "Zone 21"), //
|
||||
entry(0x16, "Zone 22"), //
|
||||
entry(0x17, "Zone 23"), //
|
||||
entry(0x18, "Zone 24"), //
|
||||
entry(0x19, "Zone 25"), //
|
||||
entry(0x1A, "Zone 26"), //
|
||||
entry(0x1B, "Zone 27"), //
|
||||
entry(0x1C, "Zone 28"), //
|
||||
entry(0x1D, "Zone 29"), //
|
||||
entry(0x1E, "Zone 30"), //
|
||||
entry(0x1F, "Keyfob 1"), //
|
||||
entry(0x20, "Keyfob 2"), //
|
||||
entry(0x21, "Keyfob 3"), //
|
||||
entry(0x22, "Keyfob 4"), //
|
||||
entry(0x23, "Keyfob 5"), //
|
||||
entry(0x24, "Keyfob 6"), //
|
||||
entry(0x25, "Keyfob 7"), //
|
||||
entry(0x26, "Keyfob 8"), //
|
||||
entry(0x27, "User 1"), //
|
||||
entry(0x28, "User 2"), //
|
||||
entry(0x29, "User 3"), //
|
||||
entry(0x2A, "User 4"), //
|
||||
entry(0x2B, "User 5"), //
|
||||
entry(0x2C, "User 6"), //
|
||||
entry(0x2D, "User 7"), //
|
||||
entry(0x2E, "User 8"), //
|
||||
entry(0x2F, "Wireless Commander 1"), //
|
||||
entry(0x30, "Wireless Commander 2"), //
|
||||
entry(0x31, "Wireless Commander 3"), //
|
||||
entry(0x32, "Wireless Commander 4"), //
|
||||
entry(0x33, "Wireless Commander 5"), //
|
||||
entry(0x34, "Wireless Commander 6"), //
|
||||
entry(0x35, "Wireless Commander 7"), //
|
||||
entry(0x36, "Wireless Commander 8"), //
|
||||
entry(0x37, "Wireless Siren 1"), //
|
||||
entry(0x38, "Wireless Siren 2"), //
|
||||
entry(0x39, "Two-Way Wireless Keypad 1"), //
|
||||
entry(0x3A, "Two-Way Wireless Keypad 2"), //
|
||||
entry(0x3B, "Two-Way Wireless Keypad 3"), //
|
||||
entry(0x3C, "Two-Way Wireless Keypad 4"), //
|
||||
entry(0x3D, "X10 1"), //
|
||||
entry(0x3E, "X10 2"), //
|
||||
entry(0x3F, "X10 3"), //
|
||||
entry(0x40, "X10 4"), //
|
||||
entry(0x41, "X10 5"), //
|
||||
entry(0x42, "X10 6"), //
|
||||
entry(0x43, "X10 7"), //
|
||||
entry(0x44, "X10 8"), //
|
||||
entry(0x45, "X10 9"), //
|
||||
entry(0x46, "X10 10"), //
|
||||
entry(0x47, "X10 11"), //
|
||||
entry(0x48, "X10 12"), //
|
||||
entry(0x49, "X10 13"), //
|
||||
entry(0x4A, "X10 14"), //
|
||||
entry(0x4B, "X10 15"), //
|
||||
entry(0x4C, "PGM"), //
|
||||
entry(0x4D, "GSM"), //
|
||||
entry(0x4E, "Powerlink"), //
|
||||
entry(0x4F, "Proxy Tag 1"), //
|
||||
entry(0x50, "Proxy Tag 2"), //
|
||||
entry(0x51, "Proxy Tag 3"), //
|
||||
entry(0x52, "Proxy Tag 4"), //
|
||||
entry(0x53, "Proxy Tag 5"), //
|
||||
entry(0x54, "Proxy Tag 6"), //
|
||||
entry(0x55, "Proxy Tag 7"), //
|
||||
entry(0x56, "Proxy Tag 8") //
|
||||
);
|
||||
|
||||
/**
|
||||
* Zone/User lookup
|
||||
*/
|
||||
public static String getZoneOrUserString(int code) {
|
||||
return getValue(ZONE_OR_USER_TABLE, code);
|
||||
public static String getZoneOrUser(int code) {
|
||||
return ZONES_OR_USERS.getOrDefault(code, "UNKNOWN");
|
||||
}
|
||||
|
||||
private static final String[] ZONE_OR_USER_TABLE = new String[] { "System", "Zone 1", "Zone 2", "Zone 3", "Zone 4",
|
||||
"Zone 5", "Zone 6", "Zone 7", "Zone 8", "Zone 9", "Zone 10", "Zone 11", "Zone 12", "Zone 13", "Zone 14",
|
||||
"Zone 15", "Zone 16", "Zone 17", "Zone 18", "Zone 19", "Zone 20", "Zone 21", "Zone 22", "Zone 23",
|
||||
"Zone 24", "Zone 25", "Zone 26", "Zone 27", "Zone 28", "Zone 29", "Zone 30", "Fob 1", "Fob 2", "Fob 3",
|
||||
"Fob 4", "Fob 5", "Fob 6", "Fob 7", "Fob 8", "User 1", "User 2", "User 3", "User 4", "User 5", "User 6",
|
||||
"User 7", "User 8", "Pad 1", "Pad 2", "Pad 3", "Pad 4", "Pad 5", "Pad 6", "Pad 7", "Pad 8", "Siren 1",
|
||||
"Siren 2", "2Pad 1", "2Pad 2", "2Pad 3", "2Pad 4", "X10 1", "X10 2", "X10 3", "X10 4", "X10 5", "X10 6",
|
||||
"X10 7", "X10 8", "X10 9", "X10 10", "X10 11", "X10 12", "X10 13", "X10 14", "X10 15", "PGM", "GSM",
|
||||
"P-LINK", "PTag 1", "PTag 2", "PTag 3", "PTag 4", "PTag 5", "PTag 6", "PTag 7", "PTag 8" };
|
||||
// Zone events
|
||||
|
||||
private static final Map<Integer, String> ZONE_EVENTS = Map.ofEntries( //
|
||||
entry(0x00, "None"), //
|
||||
entry(0x01, "Tamper Alarm"), //
|
||||
entry(0x02, "Tamper Restore"), //
|
||||
entry(0x03, "Open"), //
|
||||
entry(0x04, "Closed"), //
|
||||
entry(0x05, "Violated (Motion)"), //
|
||||
entry(0x06, "Panic Alarm"), //
|
||||
entry(0x07, "RF Jamming"), //
|
||||
entry(0x08, "Tamper Open"), //
|
||||
entry(0x09, "Communication Failure"), //
|
||||
entry(0x0A, "Line Failure"), //
|
||||
entry(0x0B, "Fuse"), //
|
||||
entry(0x0C, "Not Active"), //
|
||||
entry(0x0D, "Low Battery"), //
|
||||
entry(0x0E, "AC Failure"), //
|
||||
entry(0x0F, "Fire Alarm"), //
|
||||
entry(0x10, "Emergency"), //
|
||||
entry(0x11, "Siren Tamper"), //
|
||||
entry(0x12, "Siren Tamper Restore"), //
|
||||
entry(0x13, "Siren Low Battery"), //
|
||||
entry(0x14, "Siren AC Fail") //
|
||||
);
|
||||
|
||||
/**
|
||||
* Zone event lookup
|
||||
* Zone Event lookup
|
||||
*/
|
||||
public static String getZoneEventString(int code) {
|
||||
return getValue(ZONE_EVENT_TABLE, code);
|
||||
public static String getZoneEvent(int code) {
|
||||
return ZONE_EVENTS.getOrDefault(code, "UNKNOWN");
|
||||
}
|
||||
|
||||
private static final String[] ZONE_EVENT_TABLE = new String[] { "None", "Tamper Alarm", "Tamper Restore", "Open",
|
||||
"Closed", "Violated (Motion)", "Panic Alarm", "RF Jamming", "Tamper Open", "Communication Failure",
|
||||
"Line Failure", "Fuse", "Not Active", "Low Battery", "AC Failure", "Fire Alarm", "Emergency",
|
||||
"Siren Tamper", "Siren Tamper Restore", "Siren Low Battery", "Siren AC Fail" };
|
||||
// Message types
|
||||
|
||||
private static final Map<Integer, String> ZONE_EVENT_TYPES = Map.ofEntries( //
|
||||
entry(0x00, "None"), //
|
||||
entry(0x01, "Alarm Message"), //
|
||||
entry(0x02, "Open/Battery Message"), //
|
||||
entry(0x03, "Inactive/Tamper Message"), //
|
||||
entry(0x04, "Zone Message"), //
|
||||
entry(0x06, "Enroll/Bypass Message") //
|
||||
);
|
||||
|
||||
/**
|
||||
* Message type lookup
|
||||
*/
|
||||
public static String getMessageTypeString(int code) {
|
||||
return getValue(MESSAGE_TYPE_TABLE, code);
|
||||
public static String getZoneEventType(int code) {
|
||||
return ZONE_EVENT_TYPES.getOrDefault(code, "UNKNOWN");
|
||||
}
|
||||
|
||||
private static final String[] MESSAGE_TYPE_TABLE = new String[] { "None", "Log Message", "Status Message",
|
||||
"Tamper Message", "Zone Message", "Unknown", "Enroll/Bypass Message" };
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.powermax.internal.message;
|
||||
|
||||
import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants.PowermaxSysEvent;
|
||||
import org.openhab.binding.powermax.internal.state.PowermaxState;
|
||||
|
||||
/**
|
||||
@ -48,33 +49,37 @@ public class PowermaxPanelMessage extends PowermaxBaseMessage {
|
||||
byte eventZone = message[2 + 2 * i];
|
||||
byte logEvent = message[3 + 2 * i];
|
||||
int eventType = logEvent & 0x0000007F;
|
||||
String logEventStr = PowermaxMessageConstants.getSystemEventString(eventType);
|
||||
String logUserStr = PowermaxMessageConstants.getZoneOrUserString(eventZone & 0x000000FF);
|
||||
updatedState.panelStatus.setValue(logEventStr + " (" + logUserStr + ")");
|
||||
PowermaxSysEvent sysEvent = PowermaxMessageConstants.getSystemEvent(eventType);
|
||||
String logEventStr = sysEvent.toString();
|
||||
String logUserStr = commManager.getPanelSettings().getZoneOrUserName(eventZone & 0x000000FF);
|
||||
|
||||
debug("Event " + i + " zone code", eventZone, logUserStr);
|
||||
debug("Event " + i + " event code", eventType, logEventStr);
|
||||
|
||||
String alarmStatus;
|
||||
try {
|
||||
PowermaxAlarmType alarmType = PowermaxAlarmType.fromCode(eventType);
|
||||
alarmStatus = alarmType.getLabel();
|
||||
} catch (IllegalArgumentException e) {
|
||||
alarmStatus = "None";
|
||||
if (sysEvent.isAlarm() || sysEvent.isSilentAlarm() || sysEvent.isAlert() || sysEvent.isPanic()
|
||||
|| sysEvent.isTrouble()) {
|
||||
updatedState.addActiveAlert(eventZone, eventType);
|
||||
}
|
||||
updatedState.alarmType.setValue(alarmStatus);
|
||||
|
||||
String troubleStatus;
|
||||
try {
|
||||
PowermaxTroubleType troubleType = PowermaxTroubleType.fromCode(eventType);
|
||||
troubleStatus = troubleType.getLabel();
|
||||
} catch (IllegalArgumentException e) {
|
||||
troubleStatus = "None";
|
||||
if (sysEvent.isAlarm() || (sysEvent.isPanic() && !commManager.getPanelSettings().isSilentPanic())) {
|
||||
updatedState.ringing.setValue(true);
|
||||
updatedState.ringingSince.setValue(System.currentTimeMillis());
|
||||
}
|
||||
updatedState.troubleType.setValue(troubleStatus);
|
||||
|
||||
if (eventType == 0x60) {
|
||||
// System reset
|
||||
if (sysEvent.isCancel() || sysEvent.isGeneralRestore() || sysEvent.isReset()) {
|
||||
updatedState.ringing.setValue(false);
|
||||
}
|
||||
|
||||
if (sysEvent.isRestore()) {
|
||||
updatedState.clearActiveAlert(eventZone, sysEvent.getRestoreFor());
|
||||
}
|
||||
|
||||
if (sysEvent.isGeneralRestore() || sysEvent.isReset()) {
|
||||
updatedState.clearAllActiveAlerts();
|
||||
}
|
||||
|
||||
if (sysEvent.isReset()) {
|
||||
updatedState.clearAllActiveAlerts();
|
||||
updatedState.downloadSetupRequired.setValue(true);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import java.util.List;
|
||||
|
||||
import org.openhab.binding.powermax.internal.state.PowermaxArmMode;
|
||||
import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings;
|
||||
import org.openhab.binding.powermax.internal.state.PowermaxSensorType;
|
||||
import org.openhab.binding.powermax.internal.state.PowermaxState;
|
||||
import org.openhab.binding.powermax.internal.state.PowermaxZoneSettings;
|
||||
|
||||
@ -33,12 +34,11 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
|
||||
}
|
||||
|
||||
private static boolean[] zoneBits(byte[] zoneBytes) {
|
||||
boolean[] zones = new boolean[32];
|
||||
char[] binary = new BigInteger(zoneBytes).toString(2).toCharArray();
|
||||
int len = binary.length - 1;
|
||||
boolean[] zones = new boolean[33];
|
||||
BigInteger bigint = new BigInteger(1, zoneBytes);
|
||||
|
||||
for (int i = len; i >= 0; i--) {
|
||||
zones[len - i + 1] = (binary[i] == '1');
|
||||
for (int i = 1; i <= 32; i++) {
|
||||
zones[i] = bigint.testBit(i - 1);
|
||||
}
|
||||
|
||||
return zones;
|
||||
@ -78,11 +78,43 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
|
||||
|
||||
byte[] message = getRawData();
|
||||
byte eventType = message[3];
|
||||
String eventTypeStr = PowermaxMessageConstants.getMessageTypeString(eventType & 0x000000FF);
|
||||
String eventTypeStr = PowermaxMessageConstants.getZoneEventType(eventType & 0x000000FF);
|
||||
|
||||
debug("Event type", eventType, eventTypeStr);
|
||||
|
||||
if (eventType == 0x02) {
|
||||
// Each event type except 0x04 contains two sets of zone bitmasks.
|
||||
// Each set is four bytes (32 bits) where each bit indicates the state
|
||||
// of the corresponding zone (1 = set, 0 = unset).
|
||||
|
||||
if (eventType == 0x01) {
|
||||
// These bits are set when a zone causes an alarm
|
||||
//
|
||||
// Set 1: Alarm caused by zone being open/tripped
|
||||
// Set 2: Alarm caused by a tamper
|
||||
//
|
||||
// Note: active alarms are cleared when the Memory flag is turned off
|
||||
// (the panel won't send a follow-up event with these bits set to zero)
|
||||
|
||||
byte[] alarmStatusBytes = zoneBytes(message[4], message[5], message[6], message[7]);
|
||||
byte[] tamperStatusBytes = zoneBytes(message[8], message[9], message[10], message[11]);
|
||||
|
||||
boolean[] alarmStatus = zoneBits(alarmStatusBytes);
|
||||
boolean[] tamperStatus = zoneBits(tamperStatusBytes);
|
||||
|
||||
String alarmStatusStr = zoneList(alarmStatusBytes);
|
||||
String tamperStatusStr = zoneList(tamperStatusBytes);
|
||||
|
||||
panelSettings.getZoneRange().forEach(i -> {
|
||||
updatedState.getZone(i).alarmed.setValue(alarmStatus[i]);
|
||||
updatedState.getZone(i).tamperAlarm.setValue(tamperStatus[i]);
|
||||
});
|
||||
|
||||
debug("Alarm status", alarmStatusBytes, alarmStatusStr);
|
||||
debug("Tamper alarm status", tamperStatusBytes, tamperStatusStr);
|
||||
} else if (eventType == 0x02) {
|
||||
// Set 1: List of zones that are open/tripped
|
||||
// Set 2: List of zones that have a low-battery condition
|
||||
|
||||
byte[] zoneStatusBytes = zoneBytes(message[4], message[5], message[6], message[7]);
|
||||
byte[] batteryStatusBytes = zoneBytes(message[8], message[9], message[10], message[11]);
|
||||
|
||||
@ -92,32 +124,62 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
|
||||
String zoneStatusStr = zoneList(zoneStatusBytes);
|
||||
String batteryStatusStr = zoneList(batteryStatusBytes);
|
||||
|
||||
for (int i = 1; i <= panelSettings.getNbZones(); i++) {
|
||||
panelSettings.getZoneRange().forEach(i -> {
|
||||
updatedState.getZone(i).tripped.setValue(zoneStatus[i]);
|
||||
updatedState.getZone(i).lowBattery.setValue(batteryStatus[i]);
|
||||
}
|
||||
});
|
||||
|
||||
debug("Zone status", zoneStatusBytes, zoneStatusStr);
|
||||
debug("Battery status", batteryStatusBytes, batteryStatusStr);
|
||||
} else if (eventType == 0x03) {
|
||||
// Set 1: Inactivity / loss of supervision
|
||||
// Set 2: Zone has an active tamper condition
|
||||
|
||||
byte[] inactiveStatusBytes = zoneBytes(message[4], message[5], message[6], message[7]);
|
||||
byte[] tamperStatusBytes = zoneBytes(message[8], message[9], message[10], message[11]);
|
||||
|
||||
boolean[] inactiveStatus = zoneBits(inactiveStatusBytes);
|
||||
boolean[] tamperStatus = zoneBits(tamperStatusBytes);
|
||||
|
||||
String inactiveStatusStr = zoneList(inactiveStatusBytes);
|
||||
String tamperStatusStr = zoneList(tamperStatusBytes);
|
||||
|
||||
panelSettings.getZoneRange().forEach(i -> {
|
||||
updatedState.getZone(i).inactive.setValue(inactiveStatus[i]);
|
||||
updatedState.getZone(i).tampered.setValue(tamperStatus[i]);
|
||||
});
|
||||
|
||||
debug("Inactive status", inactiveStatusBytes, inactiveStatusStr);
|
||||
debug("Tamper status", tamperStatusBytes, tamperStatusStr);
|
||||
} else if (eventType == 0x04) {
|
||||
// System & zone status message (not like the other event types)
|
||||
|
||||
byte sysStatus = message[4];
|
||||
byte sysFlags = message[5];
|
||||
byte eventZone = message[6];
|
||||
byte zoneEType = message[7];
|
||||
int eventZone = message[6] & 0x000000FF;
|
||||
int zoneEType = message[7] & 0x000000FF;
|
||||
int x10Status = (message[10] & 0x000000FF) | ((message[11] << 8) & 0x0000FF00);
|
||||
|
||||
String eventZoneStr = PowermaxMessageConstants.getZoneOrUserString(eventZone & 0x000000FF);
|
||||
String zoneETypeStr = PowermaxMessageConstants.getZoneEventString(zoneEType & 0x000000FF);
|
||||
String eventZoneStr = panelSettings.getZoneOrUserName(eventZone);
|
||||
String zoneETypeStr = PowermaxMessageConstants.getZoneEvent(zoneEType);
|
||||
|
||||
if (zoneEType != 0x00 && eventZone > 0 && eventZone <= panelSettings.getNbZones()) {
|
||||
updatedState.getZone(eventZone).lastMessage.setValue(zoneETypeStr);
|
||||
updatedState.getZone(eventZone).lastMessageTime.setValue(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
if (zoneEType == 0x03) {
|
||||
// Open
|
||||
updatedState.getZone(eventZone).tripped.setValue(true);
|
||||
updatedState.getZone(eventZone).lastTripped.setValue(System.currentTimeMillis());
|
||||
} else if (zoneEType == 0x04) {
|
||||
// Closed
|
||||
updatedState.getZone(eventZone).tripped.setValue(false);
|
||||
} else if (zoneEType == 0x05) {
|
||||
// Violated (Motion)
|
||||
PowermaxZoneSettings zone = panelSettings.getZoneSettings(eventZone);
|
||||
if ((zone != null) && zone.getSensorType().equalsIgnoreCase("unknown")) {
|
||||
zone.setSensorType("Motion");
|
||||
zone.setSensorType(PowermaxSensorType.MOTION_SENSOR_1.getLabel());
|
||||
}
|
||||
updatedState.getZone(eventZone).tripped.setValue(true);
|
||||
updatedState.getZone(eventZone).lastTripped.setValue(System.currentTimeMillis());
|
||||
@ -141,6 +203,12 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
|
||||
updatedState.alertInMemory.setValue(true);
|
||||
} else {
|
||||
updatedState.alertInMemory.setValue(false);
|
||||
|
||||
// When the memory flag is cleared, also clear all zone alarms and tamper alarms
|
||||
panelSettings.getZoneRange().forEach(i -> {
|
||||
updatedState.getZone(i).alarmed.setValue(false);
|
||||
updatedState.getZone(i).tamperAlarm.setValue(false);
|
||||
});
|
||||
}
|
||||
if (((sysFlags >> 2) & 0x1) == 1) {
|
||||
sysStatusStr = sysStatusStr + "Trouble, ";
|
||||
@ -153,9 +221,9 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
|
||||
updatedState.bypass.setValue(true);
|
||||
} else {
|
||||
updatedState.bypass.setValue(false);
|
||||
for (int i = 1; i <= panelSettings.getNbZones(); i++) {
|
||||
panelSettings.getZoneRange().forEach(i -> {
|
||||
updatedState.getZone(i).bypassed.setValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (((sysFlags >> 4) & 0x1) == 1) {
|
||||
sysStatusStr = sysStatusStr + "Last 10 seconds, ";
|
||||
@ -165,7 +233,7 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
|
||||
if (eventZone == 0xFF) {
|
||||
sysStatusStr = sysStatusStr + " from Panel, ";
|
||||
} else if (eventZone > 0) {
|
||||
sysStatusStr = sysStatusStr + String.format(" in Zone %d, ", eventZone);
|
||||
sysStatusStr = sysStatusStr + String.format(" in %s, ", eventZoneStr);
|
||||
} else {
|
||||
sysStatusStr = sysStatusStr + ", ";
|
||||
}
|
||||
@ -196,33 +264,42 @@ public class PowermaxStatusMessage extends PowermaxBaseMessage {
|
||||
debug("Zone event type", zoneEType, zoneETypeStr);
|
||||
debug("X10 status", x10Status);
|
||||
|
||||
for (int i = 1; i <= panelSettings.getNbZones(); i++) {
|
||||
panelSettings.getZoneRange().forEach(i -> {
|
||||
PowermaxZoneSettings zone = panelSettings.getZoneSettings(i);
|
||||
if (zone != null) {
|
||||
// mode: armed or not: 4=armed home; 5=armed away
|
||||
// mode: armed or not
|
||||
int mode = sysStatus & 0x0000000F;
|
||||
// Zone is shown as armed if
|
||||
// the sensor type always triggers an alarm
|
||||
// or the system is armed away (mode = 5)
|
||||
// or the system is armed home (mode = 4) and the zone is not interior(-follow)
|
||||
boolean armed = (!zone.getType().equalsIgnoreCase("Non-Alarm") && (zone.isAlwaysInAlarm()
|
||||
|| (mode == 0x5) || ((mode == 0x4) && !zone.getType().equalsIgnoreCase("Interior-Follow")
|
||||
&& !zone.getType().equalsIgnoreCase("Interior"))));
|
||||
// or the system is armed away
|
||||
// or the system is armed home and the zone is not interior(-follow)
|
||||
boolean armed = (!zone.getType().equalsIgnoreCase("Non-Alarm")
|
||||
&& (zone.isAlwaysInAlarm() || (mode == PowermaxArmMode.ARMED_AWAY.getCode())
|
||||
|| ((mode == PowermaxArmMode.ARMED_HOME.getCode())
|
||||
&& !zone.getType().equalsIgnoreCase("Interior-Follow")
|
||||
&& !zone.getType().equalsIgnoreCase("Interior"))));
|
||||
updatedState.getZone(i).armed.setValue(armed);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (eventType == 0x06) {
|
||||
// Set 1: List of zones that are enrolled (we don't currently use this)
|
||||
// Set 2: List of zones that are bypassed
|
||||
|
||||
byte[] zoneBypassBytes = zoneBytes(message[8], message[9], message[10], message[11]);
|
||||
boolean[] zoneBypass = zoneBits(zoneBypassBytes);
|
||||
String zoneBypassStr = zoneList(zoneBypassBytes);
|
||||
|
||||
for (int i = 1; i <= panelSettings.getNbZones(); i++) {
|
||||
panelSettings.getZoneRange().forEach(i -> {
|
||||
updatedState.getZone(i).bypassed.setValue(zoneBypass[i]);
|
||||
}
|
||||
});
|
||||
|
||||
debug("Zone bypass", zoneBypassBytes, zoneBypassStr);
|
||||
}
|
||||
|
||||
// Note: in response to a STATUS request, the panel will also send
|
||||
// messages with eventType = 0x05, 0x07, 0x08, and 0x09 but these
|
||||
// haven't been decoded yet
|
||||
|
||||
return updatedState;
|
||||
}
|
||||
}
|
||||
|
@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.powermax.internal.message;
|
||||
|
||||
/**
|
||||
* All defined trouble types
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public enum PowermaxTroubleType {
|
||||
|
||||
TROUBLE_TYPE_1(0x0A, "Communication"),
|
||||
TROUBLE_TYPE_2(0x0F, "General"),
|
||||
TROUBLE_TYPE_3(0x29, "Battery"),
|
||||
TROUBLE_TYPE_4(0x2B, "Power"),
|
||||
TROUBLE_TYPE_5(0x2D, "Battery"),
|
||||
TROUBLE_TYPE_6(0x2F, "Jamming"),
|
||||
TROUBLE_TYPE_7(0x31, "Communication"),
|
||||
TROUBLE_TYPE_8(0x33, "Telephone"),
|
||||
TROUBLE_TYPE_9(0x36, "Power"),
|
||||
TROUBLE_TYPE_10(0x38, "Battery"),
|
||||
TROUBLE_TYPE_11(0x3B, "Battery"),
|
||||
TROUBLE_TYPE_12(0x3C, "Battery"),
|
||||
TROUBLE_TYPE_13(0x40, "Battery"),
|
||||
TROUBLE_TYPE_14(0x43, "Battery");
|
||||
|
||||
private int code;
|
||||
private String label;
|
||||
|
||||
private PowermaxTroubleType(int code, String label) {
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the code identifying the trouble type
|
||||
*/
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the label associated to the trouble type
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ENUM value from its identifying code
|
||||
*
|
||||
* @param code the identifying code
|
||||
*
|
||||
* @return the corresponding ENUM value
|
||||
*
|
||||
* @throws IllegalArgumentException if no ENUM value corresponds to this code
|
||||
*/
|
||||
public static PowermaxTroubleType fromCode(int code) throws IllegalArgumentException {
|
||||
for (PowermaxTroubleType troubleType : PowermaxTroubleType.values()) {
|
||||
if (troubleType.getCode() == code) {
|
||||
return troubleType;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid code: " + code);
|
||||
}
|
||||
}
|
@ -16,7 +16,9 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants;
|
||||
import org.openhab.binding.powermax.internal.message.PowermaxSendType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -75,6 +77,20 @@ public class PowermaxPanelSettings {
|
||||
return panelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the length of time the bell or siren sounds (in minutes)
|
||||
*/
|
||||
public int getBellTime() {
|
||||
return bellTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if panic alarms are silent; false if audible
|
||||
*/
|
||||
public boolean isSilentPanic() {
|
||||
return silentPanic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if bypassing zones is enabled; false if not
|
||||
*/
|
||||
@ -117,6 +133,13 @@ public class PowermaxPanelSettings {
|
||||
return zoneSettings.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an integer stream for iterating over the range of zone numbers
|
||||
*/
|
||||
public IntStream getZoneRange() {
|
||||
return IntStream.rangeClosed(1, getNbZones());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings relative to a zone
|
||||
*
|
||||
@ -128,6 +151,36 @@ public class PowermaxPanelSettings {
|
||||
return ((zone < 1) || (zone > zoneSettings.length)) ? null : zoneSettings[zone - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a zone's display name
|
||||
*
|
||||
* @param zone the zone index (from 1 to NumberOfZones)
|
||||
*
|
||||
* @return the name of the zone
|
||||
*/
|
||||
public String getZoneName(int zone) {
|
||||
PowermaxZoneSettings zoneSettings = getZoneSettings(zone);
|
||||
return (zoneSettings == null) ? null : zoneSettings.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a friendly display name for a zone, user, or device
|
||||
* (any possible source for an event)
|
||||
*
|
||||
* @param zoneOrUser the zone, user, or device code
|
||||
*
|
||||
* @return the display name
|
||||
*/
|
||||
public String getZoneOrUserName(int zoneOrUser) {
|
||||
String zoneName = getZoneName(zoneOrUser);
|
||||
|
||||
if (zoneOrUser >= 1 && zoneOrUser <= zoneSettings.length && zoneName != null) {
|
||||
return String.format("%s[%d]", zoneName, zoneOrUser);
|
||||
} else {
|
||||
return PowermaxMessageConstants.getZoneOrUser(zoneOrUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of PGM and X10 devices managed by the system
|
||||
*/
|
||||
|
@ -14,9 +14,13 @@ package org.openhab.binding.powermax.internal.state;
|
||||
|
||||
import static org.openhab.binding.powermax.internal.PowermaxBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
@ -43,14 +47,13 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
public BooleanValue alarmActive = new BooleanValue(this, ALARM_ACTIVE);
|
||||
public BooleanValue trouble = new BooleanValue(this, TROUBLE);
|
||||
public BooleanValue alertInMemory = new BooleanValue(this, ALERT_IN_MEMORY);
|
||||
public BooleanValue ringing = new BooleanValue(this, RINGING);
|
||||
public DateTimeValue ringingSince = new DateTimeValue(this, "_ringing_since");
|
||||
public StringValue statusStr = new StringValue(this, SYSTEM_STATUS);
|
||||
public StringValue armMode = new StringValue(this, "_arm_mode");
|
||||
public BooleanValue downloadSetupRequired = new BooleanValue(this, "_download_setup_required");
|
||||
public DateTimeValue lastKeepAlive = new DateTimeValue(this, "_last_keepalive");
|
||||
public DateTimeValue lastMessageReceived = new DateTimeValue(this, "_last_message_received");
|
||||
public StringValue panelStatus = new StringValue(this, "_panel_status");
|
||||
public StringValue alarmType = new StringValue(this, "_alarm_type");
|
||||
public StringValue troubleType = new StringValue(this, "_trouble_type");
|
||||
public DateTimeValue lastMessageTime = new DateTimeValue(this, LAST_MESSAGE_TIME);
|
||||
|
||||
public DynamicValue<Boolean> isArmed = new DynamicValue<>(this, SYSTEM_ARMED, () -> {
|
||||
return isArmed();
|
||||
@ -70,24 +73,52 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
return new StringType(getShortArmMode());
|
||||
});
|
||||
|
||||
public DynamicValue<String> activeAlerts = new DynamicValue<>(this, ACTIVE_ALERTS, () -> {
|
||||
return getActiveAlerts();
|
||||
}, () -> {
|
||||
return new StringType(getActiveAlerts());
|
||||
});
|
||||
|
||||
public DynamicValue<Boolean> pgmStatus = new DynamicValue<>(this, PGM_STATUS, () -> {
|
||||
return getPGMX10DeviceStatus(0);
|
||||
}, () -> {
|
||||
return getPGMX10DeviceStatus(0) ? OnOffType.ON : OnOffType.OFF;
|
||||
});
|
||||
|
||||
private PowermaxPanelSettings panelSettings;
|
||||
private PowermaxZoneState[] zones;
|
||||
private Boolean[] pgmX10DevicesStatus;
|
||||
private byte[] updateSettings;
|
||||
private String[] eventLog;
|
||||
private Map<Integer, Byte> updatedZoneNames;
|
||||
private Map<Integer, Integer> updatedZoneInfos;
|
||||
private List<PowermaxActiveAlert> activeAlertList;
|
||||
private List<PowermaxActiveAlert> activeAlertQueue;
|
||||
|
||||
private enum PowermaxAlertAction {
|
||||
ADD,
|
||||
CLEAR,
|
||||
CLEAR_ALL
|
||||
}
|
||||
|
||||
private class PowermaxActiveAlert {
|
||||
public final @Nullable PowermaxAlertAction action;
|
||||
public final int zone;
|
||||
public final int code;
|
||||
|
||||
public PowermaxActiveAlert(@Nullable PowermaxAlertAction action, int zone, int code) {
|
||||
this.action = action;
|
||||
this.zone = zone;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor (default values)
|
||||
*/
|
||||
public PowermaxState(PowermaxPanelSettings panelSettings, TimeZoneProvider timeZoneProvider) {
|
||||
super(timeZoneProvider);
|
||||
this.panelSettings = panelSettings;
|
||||
|
||||
zones = new PowermaxZoneState[panelSettings.getNbZones()];
|
||||
for (int i = 0; i < panelSettings.getNbZones(); i++) {
|
||||
@ -96,6 +127,15 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
pgmX10DevicesStatus = new Boolean[panelSettings.getNbPGMX10Devices()];
|
||||
updatedZoneNames = new HashMap<>();
|
||||
updatedZoneInfos = new HashMap<>();
|
||||
activeAlertList = new ArrayList<>();
|
||||
activeAlertQueue = new ArrayList<>();
|
||||
|
||||
// Most fields will get populated by the initial download, but we set
|
||||
// the ringing indicator in response to an alarm message. We have no
|
||||
// other way to know if the siren is ringing so we'll initialize it to
|
||||
// false.
|
||||
|
||||
this.ringing.setValue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,6 +254,76 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
this.updatedZoneInfos.put(zoneIdx, zoneInfo);
|
||||
}
|
||||
|
||||
// This is an attempt to add persistence to an otherwise (mostly) stateless class.
|
||||
// All of the other values are either present or null, and it's easy to build a
|
||||
// delta state based only on which values are non-null. But these system events
|
||||
// are different because each event can be set by one message and cleared by a
|
||||
// later message. So to preserve the semantics of the state class, we'll keep a
|
||||
// queue of incoming changes, and apply them only when the delta state is resolved.
|
||||
|
||||
public boolean hasActiveAlertsQueued() {
|
||||
return !activeAlertQueue.isEmpty();
|
||||
}
|
||||
|
||||
public String getActiveAlerts() {
|
||||
if (activeAlertList.isEmpty()) {
|
||||
return "None";
|
||||
}
|
||||
|
||||
List<String> alerts = new ArrayList<>();
|
||||
|
||||
activeAlertList.forEach(e -> {
|
||||
String message = PowermaxMessageConstants.getSystemEvent(e.code).toString();
|
||||
String alert = e.zone == 0 ? message
|
||||
: String.format("%s (%s)", message, panelSettings.getZoneOrUserName(e.zone));
|
||||
|
||||
alerts.add(alert);
|
||||
});
|
||||
|
||||
return String.join(", ", alerts);
|
||||
}
|
||||
|
||||
public void addActiveAlert(int zoneIdx, int code) {
|
||||
PowermaxActiveAlert alert = new PowermaxActiveAlert(PowermaxAlertAction.ADD, zoneIdx, code);
|
||||
activeAlertQueue.add(alert);
|
||||
}
|
||||
|
||||
public void clearActiveAlert(int zoneIdx, int code) {
|
||||
PowermaxActiveAlert alert = new PowermaxActiveAlert(PowermaxAlertAction.CLEAR, zoneIdx, code);
|
||||
activeAlertQueue.add(alert);
|
||||
}
|
||||
|
||||
public void clearAllActiveAlerts() {
|
||||
PowermaxActiveAlert alert = new PowermaxActiveAlert(PowermaxAlertAction.CLEAR_ALL, 0, 0);
|
||||
activeAlertQueue.add(alert);
|
||||
}
|
||||
|
||||
public void resolveActiveAlerts(@Nullable PowermaxState previousState) {
|
||||
copyActiveAlertsFrom(previousState);
|
||||
|
||||
activeAlertQueue.forEach(alert -> {
|
||||
if (alert.action == PowermaxAlertAction.CLEAR_ALL) {
|
||||
activeAlertList.clear();
|
||||
} else {
|
||||
activeAlertList.removeIf(e -> e.zone == alert.zone && e.code == alert.code);
|
||||
|
||||
if (alert.action == PowermaxAlertAction.ADD) {
|
||||
activeAlertList.add(new PowermaxActiveAlert(null, alert.zone, alert.code));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void copyActiveAlertsFrom(@Nullable PowermaxState state) {
|
||||
activeAlertList = new ArrayList<>();
|
||||
|
||||
if (state != null) {
|
||||
state.activeAlertList.forEach(alert -> {
|
||||
activeAlertList.add(new PowermaxActiveAlert(null, alert.zone, alert.code));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the panel mode
|
||||
*
|
||||
@ -324,6 +434,10 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
thisValue.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasActiveAlertsQueued()) {
|
||||
resolveActiveAlerts(otherState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,6 +484,10 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
setEventLog(i, update.getEventLog(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (update.hasActiveAlertsQueued()) {
|
||||
copyActiveAlertsFrom(update);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -379,11 +497,11 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
for (Value<?> value : getValues()) {
|
||||
if ((value.getChannel() != null) && (value.getValue() != null)) {
|
||||
String channel = value.getChannel();
|
||||
String v_str = value.getValue().toString();
|
||||
String vStr = value.getValue().toString();
|
||||
String state = value.getState().toString();
|
||||
|
||||
str += "\n - " + channel + " = " + v_str;
|
||||
if (!v_str.equals(state)) {
|
||||
str += "\n - " + channel + " = " + vStr;
|
||||
if (!vStr.equals(state)) {
|
||||
str += " (" + state + ")";
|
||||
}
|
||||
}
|
||||
@ -400,11 +518,11 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
for (Value<?> value : zones[i - 1].getValues()) {
|
||||
if ((value.getChannel() != null) && (value.getValue() != null)) {
|
||||
String channel = value.getChannel();
|
||||
String v_str = value.getValue().toString();
|
||||
String vStr = value.getValue().toString();
|
||||
String state = value.getState().toString();
|
||||
|
||||
str += String.format("\n - sensor zone %d %s = %s", i, channel, v_str);
|
||||
if (!v_str.equals(state)) {
|
||||
str += String.format("\n - sensor zone %d %s = %s", i, channel, vStr);
|
||||
if (!vStr.equals(state)) {
|
||||
str += " (" + state + ")";
|
||||
}
|
||||
}
|
||||
@ -417,6 +535,9 @@ public class PowermaxState extends PowermaxStateContainer {
|
||||
}
|
||||
}
|
||||
|
||||
String alarms = getActiveAlerts();
|
||||
str += "\n - active alarms/alerts = " + (alarms == null ? "null" : alarms);
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ public abstract class PowermaxStateContainer {
|
||||
protected List<Value<?>> values;
|
||||
|
||||
public abstract class Value<T> {
|
||||
protected T value;
|
||||
protected @Nullable T value;
|
||||
protected final String channel;
|
||||
|
||||
public Value(PowermaxStateContainer parent, String channel) {
|
||||
|
@ -19,6 +19,8 @@ package org.openhab.binding.powermax.internal.state;
|
||||
*/
|
||||
public class PowermaxZoneSettings {
|
||||
|
||||
// Note: PowermaxStatusMessage contains hardcoded references to some of these strings
|
||||
|
||||
private static final String[] ZONE_TYPES = { "Non-Alarm", "Emergency", "Flood", "Gas", "Delay 1", "Delay 2",
|
||||
"Interior-Follow", "Perimeter", "Perimeter-Follow", "24 Hours Silent", "24 Hours Audible", "Fire",
|
||||
"Interior", "Home Delay", "Temperature", "Outdoor" };
|
||||
|
@ -28,7 +28,13 @@ public class PowermaxZoneState extends PowermaxStateContainer {
|
||||
public DateTimeValue lastTripped = new DateTimeValue(this, LAST_TRIP);
|
||||
public BooleanValue lowBattery = new BooleanValue(this, LOW_BATTERY);
|
||||
public BooleanValue bypassed = new BooleanValue(this, BYPASSED);
|
||||
public BooleanValue alarmed = new BooleanValue(this, ALARMED);
|
||||
public BooleanValue tamperAlarm = new BooleanValue(this, TAMPER_ALARM);
|
||||
public BooleanValue inactive = new BooleanValue(this, INACTIVE);
|
||||
public BooleanValue tampered = new BooleanValue(this, TAMPERED);
|
||||
public BooleanValue armed = new BooleanValue(this, ARMED);
|
||||
public StringValue lastMessage = new StringValue(this, ZONE_LAST_MESSAGE);
|
||||
public DateTimeValue lastMessageTime = new DateTimeValue(this, ZONE_LAST_MESSAGE_TIME);
|
||||
|
||||
public DynamicValue<Boolean> locked = new DynamicValue<>(this, LOCKED, () -> {
|
||||
return armed.getValue();
|
||||
|
@ -17,6 +17,20 @@
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="last_message_time" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Message Time</label>
|
||||
<description>Timestamp when the most recent message of any kind was received from the panel</description>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="active_alerts">
|
||||
<item-type>String</item-type>
|
||||
<label>Active Alarms and Alerts</label>
|
||||
<description>List of active alarms and alerts</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="trouble">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Trouble Detected</label>
|
||||
@ -31,6 +45,13 @@
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ringing">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Ringing</label>
|
||||
<description>Whether or not the alarm siren is currently ringing</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="system_status">
|
||||
<item-type>String</item-type>
|
||||
<label>System Status</label>
|
||||
@ -107,6 +128,36 @@
|
||||
<description>Whether or not the zone is bypassed</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="alarmed">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Zone Alarmed</label>
|
||||
<description>Whether or not the zone has an active alarm condition, or has had an active alarm since the memory was
|
||||
last cleared</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tamper_alarm">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Zone Tamper Alarm</label>
|
||||
<description>Whether or not the zone's sensor has an active tamper condition, or has had an active tamper condition
|
||||
since the memory was last cleared</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="inactive">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Zone Inactive</label>
|
||||
<description>Whether or not the zone's sensor is inactive (loss of supervision)</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tampered">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Zone Tampered</label>
|
||||
<description>Whether or not the zone's sensor is reporting a tamper condition</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="armed">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Zone Armed (Switch)</label>
|
||||
@ -121,6 +172,20 @@
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="zone_last_message">
|
||||
<item-type>String</item-type>
|
||||
<label>Zone Last Status Message</label>
|
||||
<description>The most recent status message reported by the zone</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="zone_last_message_time">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Zone Last Status Time</label>
|
||||
<description>Timestamp when Zone Last Status Message was received</description>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pgm_status" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>PGM Status</label>
|
||||
|
@ -17,8 +17,11 @@
|
||||
<channel id="with_zones_bypassed" typeId="with_zones_bypassed"/>
|
||||
<channel id="trouble" typeId="trouble"/>
|
||||
<channel id="alert_in_memory" typeId="alert_in_memory"/>
|
||||
<channel id="ringing" typeId="ringing"/>
|
||||
<channel id="pgm_status" typeId="pgm_status"/>
|
||||
<channel id="mode" typeId="mode"/>
|
||||
<channel id="last_message_time" typeId="last_message_time"/>
|
||||
<channel id="active_alerts" typeId="active_alerts"/>
|
||||
<channel id="event_log_1" typeId="event_log"/>
|
||||
<channel id="event_log_2" typeId="event_log"/>
|
||||
<channel id="event_log_3" typeId="event_log"/>
|
||||
|
@ -17,8 +17,11 @@
|
||||
<channel id="with_zones_bypassed" typeId="with_zones_bypassed"/>
|
||||
<channel id="trouble" typeId="trouble"/>
|
||||
<channel id="alert_in_memory" typeId="alert_in_memory"/>
|
||||
<channel id="ringing" typeId="ringing"/>
|
||||
<channel id="pgm_status" typeId="pgm_status"/>
|
||||
<channel id="mode" typeId="mode"/>
|
||||
<channel id="last_message_time" typeId="last_message_time"/>
|
||||
<channel id="active_alerts" typeId="active_alerts"/>
|
||||
<channel id="event_log_1" typeId="event_log"/>
|
||||
<channel id="event_log_2" typeId="event_log"/>
|
||||
<channel id="event_log_3" typeId="event_log"/>
|
||||
|
@ -21,6 +21,12 @@
|
||||
<channel id="last_trip" typeId="last_trip"/>
|
||||
<channel id="low_battery" typeId="system.low-battery"/>
|
||||
<channel id="bypassed" typeId="bypassed"/>
|
||||
<channel id="alarmed" typeId="alarmed"/>
|
||||
<channel id="tamper_alarm" typeId="tamper_alarm"/>
|
||||
<channel id="inactive" typeId="inactive"/>
|
||||
<channel id="tampered" typeId="tampered"/>
|
||||
<channel id="last_message" typeId="zone_last_message"/>
|
||||
<channel id="last_message_time" typeId="zone_last_message_time"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
|
Loading…
Reference in New Issue
Block a user