2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.powermax.internal.state;
15 import java.nio.charset.StandardCharsets;
16 import java.util.Arrays;
17 import java.util.Calendar;
18 import java.util.GregorianCalendar;
19 import java.util.stream.IntStream;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants;
24 import org.openhab.binding.powermax.internal.message.PowermaxSendType;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * A class to store all the settings of the alarm system
31 * @author Laurent Garnier - Initial contribution
34 public class PowermaxPanelSettings {
36 /** Number of PGM and X10 devices managed by the system */
37 private static final int NB_PGM_X10_DEVICES = 16;
39 private final Logger logger = LoggerFactory.getLogger(PowermaxPanelSettings.class);
41 /** Raw buffers for settings */
42 private Byte[][] rawSettings;
44 private PowermaxPanelType panelType;
45 private String[] phoneNumbers;
47 private boolean silentPanic;
48 private boolean quickArm;
49 private boolean bypassEnabled;
50 private boolean partitionsEnabled;
51 private String @Nullable [] pinCodes;
52 private @Nullable String panelEprom;
53 private @Nullable String panelSoftware;
54 private @Nullable String panelSerial;
55 private PowermaxZoneSettings[] zoneSettings;
56 private PowermaxX10Settings[] x10Settings;
57 private boolean @Nullable [] keypad1wEnrolled;
58 private boolean @Nullable [] keypad2wEnrolled;
59 private boolean @Nullable [] sirensEnrolled;
64 * @param defaultPanelType the default panel type to consider
66 public PowermaxPanelSettings(PowermaxPanelType defaultPanelType) {
67 rawSettings = new Byte[0x100][];
68 panelType = defaultPanelType;
69 phoneNumbers = new String[4];
71 int zoneCnt = panelType.getWireless() + panelType.getWired();
72 zoneSettings = new PowermaxZoneSettings[zoneCnt];
73 x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES];
77 * @return the panel type
79 public PowermaxPanelType getPanelType() {
84 * @return the length of time the bell or siren sounds (in minutes)
86 public int getBellTime() {
91 * @return true if panic alarms are silent; false if audible
93 public boolean isSilentPanic() {
98 * @return true if bypassing zones is enabled; false if not
100 public boolean isBypassEnabled() {
101 return bypassEnabled;
105 * @return true if partitions usage is enabled; false if not
107 public boolean isPartitionsEnabled() {
108 return partitionsEnabled;
112 * @return the panel EEPROM version
114 public @Nullable String getPanelEprom() {
119 * @return the panel software version
121 public @Nullable String getPanelSoftware() {
122 return panelSoftware;
126 * @return the panel serial ID
128 public @Nullable String getPanelSerial() {
133 * @return the number of zones
135 public int getNbZones() {
136 return zoneSettings.length;
140 * @return an integer stream for iterating over the range of zone numbers
142 public IntStream getZoneRange() {
143 return IntStream.rangeClosed(1, getNbZones());
147 * Get the settings relative to a zone
149 * @param zone the zone index (from 1 to NumberOfZones)
151 * @return the settings of the zone
153 public @Nullable PowermaxZoneSettings getZoneSettings(int zone) {
154 return ((zone < 1) || (zone > zoneSettings.length)) ? null : zoneSettings[zone - 1];
158 * Get a zone's display name
160 * @param zone the zone index (from 1 to NumberOfZones)
162 * @return the name of the zone
164 public @Nullable String getZoneName(int zone) {
165 PowermaxZoneSettings zoneSettings = getZoneSettings(zone);
166 return (zoneSettings == null) ? null : zoneSettings.getName();
170 * Get a friendly display name for a zone, user, or device
171 * (any possible source for an event)
173 * @param zoneOrUser the zone, user, or device code
175 * @return the display name
177 public String getZoneOrUserName(int zoneOrUser) {
178 String zoneName = getZoneName(zoneOrUser);
180 if (zoneOrUser >= 1 && zoneOrUser <= zoneSettings.length && zoneName != null) {
181 return String.format("%s[%d]", zoneName, zoneOrUser);
183 return PowermaxMessageConstants.getZoneOrUser(zoneOrUser);
188 * @return the number of PGM and X10 devices managed by the system
190 public int getNbPGMX10Devices() {
191 return NB_PGM_X10_DEVICES;
195 * Get the settings relative to the PGM
197 * @return the settings of the PGM
199 public PowermaxX10Settings getPGMSettings() {
200 return x10Settings[0];
204 * Get the settings relative to a X10 device
206 * @param idx the index (from 1 to 15)
208 * @return the settings of the X10 device
210 public @Nullable PowermaxX10Settings getX10Settings(int idx) {
211 return ((idx < 1) || (idx >= x10Settings.length)) ? null : x10Settings[idx];
215 * @param idx the keypad index (first is 1)
217 * @return true if the 1 way keypad is enrolled; false if not
219 public boolean isKeypad1wEnrolled(int idx) {
220 boolean @Nullable [] localKeypad1wEnrolled = keypad1wEnrolled;
221 return ((localKeypad1wEnrolled == null) || (idx < 1) || (idx >= localKeypad1wEnrolled.length)) ? false
222 : localKeypad1wEnrolled[idx - 1];
226 * @param idx the keypad index (first is 1)
228 * @return true if the 2 way keypad is enrolled; false if not
230 public boolean isKeypad2wEnrolled(int idx) {
231 boolean @Nullable [] localKeypad2wEnrolled = keypad2wEnrolled;
232 return ((localKeypad2wEnrolled == null) || (idx < 1) || (idx >= localKeypad2wEnrolled.length)) ? false
233 : localKeypad2wEnrolled[idx - 1];
237 * @param idx the siren index (first is 1)
239 * @return true if the siren is enrolled; false if not
241 public boolean isSirenEnrolled(int idx) {
242 boolean @Nullable [] localSirensEnrolled = sirensEnrolled;
243 return ((localSirensEnrolled == null) || (idx < 1) || (idx >= localSirensEnrolled.length)) ? false
244 : localSirensEnrolled[idx - 1];
248 * @return the PIN code of the first user of an empty string if unknown (standard mode)
250 public String getFirstPinCode() {
251 String @Nullable [] localPinCodes = pinCodes;
252 return (localPinCodes == null || localPinCodes.length == 0) ? "" : localPinCodes[0];
255 public void updateRawSettings(byte[] data) {
256 if (data.length < 3) {
260 int end = data.length - 3;
261 int index = data[0] & 0x000000FF;
262 int page = data[1] & 0x000000FF;
263 int pageMin = page + (index + start) / 0x100;
264 int indexPageMin = (index + start) % 0x100;
265 int pageMax = page + (index + end) / 0x100;
266 int indexPageMax = (index + end) % 0x100;
268 for (int i = pageMin; i <= pageMax; i++) {
272 start = indexPageMin;
277 if (rawSettings[i] == null) {
278 rawSettings[i] = new Byte[0x100];
280 for (int j = start; j <= end; j++) {
281 rawSettings[i][j] = data[index++];
286 private byte @Nullable [] readSettings(PowermaxSendType msgType, int start, int end) {
287 byte[] message = msgType.getMessage();
288 int page = message[2] & 0x000000FF;
289 int index = message[1] & 0x000000FF;
290 return readSettings(page, index + start, index + end);
293 private byte @Nullable [] readSettings(int page, int start, int end) {
294 int pageMin = page + start / 0x100;
295 int indexPageMin = start % 0x100;
296 int pageMax = page + end / 0x100;
297 int indexPageMax = end % 0x100;
299 boolean missingData = false;
300 for (int i = pageMin; i <= pageMax; i++) {
304 start2 = indexPageMin;
309 index += end2 - start2 + 1;
310 for (int j = start2; j <= end2; j++) {
311 if ((rawSettings[i] == null) || (rawSettings[i][j] == null)) {
321 logger.debug("readSettings({}, {}, {}): missing data", page, start, end);
324 byte[] result = new byte[index];
326 for (int i = pageMin; i <= pageMax; i++) {
330 start2 = indexPageMin;
335 for (int j = start2; j <= end2; j++) {
336 result[index++] = rawSettings[i][j];
342 private @Nullable String readSettingsAsString(PowermaxSendType msgType, int start, int end) {
343 byte[] message = msgType.getMessage();
344 int page = message[2] & 0x000000FF;
345 int index = message[1] & 0x000000FF;
346 String result = null;
347 byte[] data = readSettings(page, index + start, index + end);
348 if ((data != null) && ((data[0] & 0x000000FF) != 0x000000FF)) {
350 for (int i = 0; i < data.length; i++) {
351 boolean endStr = false;
352 switch (data[i] & 0x000000FF) {
366 if ((data[i] & 0x000000FF) >= 0x20) {
367 result += new String(data, i, 1, StandardCharsets.US_ASCII);
369 logger.debug("Unhandled character code {}", data[i]);
377 result = result.trim();
383 * Process and store all the panel settings from the raw buffers
385 * @param PowerlinkMode true if in Powerlink mode or false if in standard mode
386 * @param defaultPanelType the default panel type to consider if not found in the raw buffers
387 * @param timeSet the time in milliseconds used to set time and date; 0 if no sync time requested
389 * @return true if no problem encountered to get all the settings; false if not
391 public boolean process(boolean PowerlinkMode, PowermaxPanelType defaultPanelType, long timeSet) {
392 logger.debug("Process settings Powerlink {}", PowerlinkMode);
394 boolean result = true;
398 // Identify panel type
399 panelType = defaultPanelType;
401 data = readSettings(PowermaxSendType.DL_SERIAL, 7, 7);
404 panelType = PowermaxPanelType.fromCode(data[0]);
405 } catch (IllegalArgumentException e) {
406 logger.debug("Powermax alarm binding: unknwon panel type for code {}", data[0] & 0x000000FF);
407 panelType = defaultPanelType;
410 logger.debug("Cannot get panel type");
415 int zoneCnt = panelType.getWireless() + panelType.getWired();
416 int customCnt = panelType.getCustomZones();
417 int userCnt = panelType.getUserCodes();
418 int partitionCnt = panelType.getPartitions();
419 int sirenCnt = panelType.getSirens();
420 int keypad1wCnt = panelType.getKeypads1w();
421 int keypad2wCnt = panelType.getKeypads2w();
423 phoneNumbers = new String[4];
427 bypassEnabled = false;
428 partitionsEnabled = false;
429 String[] localPinCodes = new String[userCnt];
431 panelSoftware = null;
433 zoneSettings = new PowermaxZoneSettings[zoneCnt];
434 x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES];
435 boolean[] localKeypad1wEnrolled = new boolean[keypad1wCnt];
436 boolean[] localKeypad2wEnrolled = new boolean[keypad2wCnt];
437 boolean[] localSirensEnrolled = new boolean[sirenCnt];
440 // Check time and date
441 data = readSettings(PowermaxSendType.DL_TIME, 0, 5);
443 GregorianCalendar cal = new GregorianCalendar();
444 cal.set(Calendar.MILLISECOND, 0);
445 cal.set(Calendar.SECOND, data[0] & 0x000000FF);
446 cal.set(Calendar.MINUTE, data[1] & 0x000000FF);
447 cal.set(Calendar.HOUR_OF_DAY, data[2] & 0x000000FF);
448 cal.set(Calendar.DAY_OF_MONTH, data[3] & 0x000000FF);
449 cal.set(Calendar.MONTH, (data[4] & 0x000000FF) - 1);
450 cal.set(Calendar.YEAR, (data[5] & 0x000000FF) + 2000);
451 long timeRead = cal.getTimeInMillis();
452 logger.debug("Powermax alarm binding: time {}",
453 String.format("%02d/%02d/%04d %02d:%02d:%02d", cal.get(Calendar.DAY_OF_MONTH),
454 cal.get(Calendar.MONTH) + 1, cal.get(Calendar.YEAR), cal.get(Calendar.HOUR_OF_DAY),
455 cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)));
457 // Check if time sync was OK
459 long delta = (timeRead - timeSet) / 1000;
461 logger.debug("Powermax alarm binding: time sync OK (delta {} s)", delta);
463 logger.info("Powermax alarm binding: time sync failed ! (delta {} s)", delta);
467 logger.debug("Cannot get time and date settings");
471 // Process zone names
473 for (int i = 0; i < (26 + customCnt); i++) {
474 String str = readSettingsAsString(PowermaxSendType.DL_ZONESTR, i * 16, (i + 1) * 16 - 1);
477 PowermaxZoneName zoneName = PowermaxZoneName.fromId(i);
478 zoneName.setName(str);
479 } catch (IllegalArgumentException e) {
480 logger.debug("Zone id out of bounds {}", i);
487 logger.debug("Cannot get all zone names");
491 // Process communication settings
493 for (int i = 0; i < phoneNumbers.length; i++) {
494 data = readSettings(PowermaxSendType.DL_PHONENRS, 8 * i, 8 * i + 7);
496 for (int j = 0; j < 8; j++) {
497 if ((data[j] & 0x000000FF) != 0x000000FF) {
499 phoneNumbers[i] = "";
501 if (phoneNumbers[i] != null) {
502 phoneNumbers[i] += String.format("%02X", data[j] & 0x000000FF);
511 logger.debug("Cannot get all communication settings");
515 // Process alarm settings
516 data = readSettings(PowermaxSendType.DL_COMMDEF, 0, 0x1B);
518 bellTime = data[3] & 0x000000FF;
519 silentPanic = (data[0x19] & 0x00000010) == 0x00000010;
520 quickArm = (data[0x1A] & 0x00000008) == 0x00000008;
521 bypassEnabled = (data[0x1B] & 0x000000C0) != 0;
523 logger.debug("Cannot get alarm settings");
527 // Process user PIN codes
529 panelType.isPowerMaster() ? PowermaxSendType.DL_MR_PINCODES : PowermaxSendType.DL_PINCODES, 0,
532 for (int i = 0; i < userCnt; i++) {
533 localPinCodes[i] = String.format("%02X%02X", data[i * 2] & 0x000000FF,
534 data[i * 2 + 1] & 0x000000FF);
537 logger.debug("Cannot get PIN codes");
541 // Process EEPROM version
542 panelEprom = readSettingsAsString(PowermaxSendType.DL_PANELFW, 0, 15);
543 if (panelEprom == null) {
544 logger.debug("Cannot get EEPROM version");
548 // Process software version
549 panelSoftware = readSettingsAsString(PowermaxSendType.DL_PANELFW, 16, 31);
550 if (panelSoftware == null) {
551 logger.debug("Cannot get software version");
557 data = readSettings(PowermaxSendType.DL_SERIAL, 0, 5);
559 for (int i = 0; i <= 5; i++) {
560 if ((data[i] & 0x000000FF) != 0x000000FF) {
561 panelSerial += String.format("%02X", data[i] & 0x000000FF);
567 logger.debug("Cannot get serial ID");
571 // Check if partitions are enabled (only on panels that support partitions)
572 byte[] partitions = null;
573 if (partitionCnt > 1) {
574 partitions = readSettings(PowermaxSendType.DL_PARTITIONS, 0, 0x10 + zoneCnt);
575 if (partitions != null) {
576 partitionsEnabled = (partitions[0] & 0x000000FF) == 1;
578 logger.debug("Cannot get partitions information");
581 if (!partitionsEnabled) {
586 // Process zone settings
587 data = readSettings(PowermaxSendType.DL_ZONES, 0, zoneCnt * 4 - 1);
588 byte[] zoneNr = null;
589 byte[] dataMr = null;
590 if (panelType.isPowerMaster()) {
591 zoneNr = readSettings(PowermaxSendType.DL_MR_ZONENAMES, 0, zoneCnt - 1);
592 dataMr = readSettings(PowermaxSendType.DL_MR_ZONES, 0, zoneCnt * 10 - 2);
594 zoneNr = readSettings(PowermaxSendType.DL_ZONENAMES, 0, zoneCnt - 1);
596 if ((data != null) && (zoneNr != null)) {
597 byte[] zero3 = new byte[] { 0, 0, 0 };
598 byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
600 for (int i = 0; i < zoneCnt; i++) {
603 PowermaxZoneName zone = PowermaxZoneName.fromId(zoneNr[i] & 0x0000001F);
604 zoneName = zone.getName();
605 } catch (IllegalArgumentException e) {
606 logger.debug("Zone id out of bounds {}", zoneNr[i] & 0x0000001F);
610 boolean zoneEnrolled;
612 String sensorTypeStr;
613 if (panelType.isPowerMaster()) {
615 if (dataMr != null) {
616 zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(dataMr, i * 10 + 4, i * 10 + 9), zero5);
617 byte sensorTypeCode = dataMr[i * 10 + 5];
619 PowermasterSensorType sensorType = PowermasterSensorType.fromCode(sensorTypeCode);
620 sensorTypeStr = sensorType.getLabel();
621 } catch (IllegalArgumentException e) {
622 sensorTypeStr = null;
625 zoneEnrolled = false;
626 sensorTypeStr = null;
629 zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
630 zoneInfo = data[i * 4 + 3];
631 byte sensorTypeCode = data[i * 4 + 2];
633 PowermaxSensorType sensorType = PowermaxSensorType
634 .fromCode((byte) (sensorTypeCode & 0x0000000F));
635 sensorTypeStr = sensorType.getLabel();
636 } catch (IllegalArgumentException e) {
637 sensorTypeStr = null;
641 byte zoneType = (byte) (zoneInfo & 0x0000000F);
642 byte zoneChime = (byte) ((zoneInfo >> 4) & 0x00000003);
644 boolean[] part = new boolean[partitionCnt];
645 if (partitionCnt > 1) {
646 for (int j = 0; j < partitionCnt; j++) {
647 part[j] = (partitions != null) ? ((partitions[0x11 + i] & (1 << j)) != 0) : true;
653 zoneSettings[i] = new PowermaxZoneSettings(zoneName, zoneType, zoneChime, sensorTypeStr, part);
657 logger.debug("Cannot get zone settings");
661 data = readSettings(PowermaxSendType.DL_PGMX10, 0, 148);
662 zoneNr = readSettings(PowermaxSendType.DL_X10NAMES, 0, NB_PGM_X10_DEVICES - 2);
663 if ((data != null) && (zoneNr != null)) {
664 for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
665 boolean enabled = false;
666 String zoneName = null;
667 for (int j = 0; j <= 8; j++) {
668 if (data[5 + i + j * 0x10] != 0) {
675 PowermaxZoneName zone = PowermaxZoneName.fromId(zoneNr[i - 1] & 0x0000001F);
676 zoneName = zone.getName();
677 } catch (IllegalArgumentException e) {
678 logger.debug("Zone id out of bounds {}", zoneNr[i - 1] & 0x0000001F);
682 x10Settings[i] = new PowermaxX10Settings(zoneName, enabled);
685 logger.debug("Cannot get PGM / X10 settings");
689 if (panelType.isPowerMaster()) {
690 // Process 2 way keypad settings
691 data = readSettings(PowermaxSendType.DL_MR_KEYPADS, 0, keypad2wCnt * 10 - 1);
693 byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
695 for (int i = 0; i < keypad2wCnt; i++) {
696 localKeypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9),
700 logger.debug("Cannot get 2 way keypad settings");
703 // Process siren settings
704 data = readSettings(PowermaxSendType.DL_MR_SIRENS, 0, sirenCnt * 10 - 1);
706 byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
708 for (int i = 0; i < sirenCnt; i++) {
709 localSirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9),
713 logger.debug("Cannot get siren settings");
717 // Process 1 way keypad settings
718 data = readSettings(PowermaxSendType.DL_1WKEYPAD, 0, keypad1wCnt * 4 - 1);
720 byte[] zero2 = new byte[] { 0, 0 };
722 for (int i = 0; i < keypad1wCnt; i++) {
723 localKeypad1wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 2), zero2);
726 logger.debug("Cannot get 1 way keypad settings");
729 // Process 2 way keypad settings
730 data = readSettings(PowermaxSendType.DL_2WKEYPAD, 0, keypad2wCnt * 4 - 1);
732 byte[] zero3 = new byte[] { 0, 0, 0 };
734 for (int i = 0; i < keypad2wCnt; i++) {
735 localKeypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
738 logger.debug("Cannot get 2 way keypad settings");
741 // Process siren settings
742 data = readSettings(PowermaxSendType.DL_SIRENS, 0, sirenCnt * 4 - 1);
744 byte[] zero3 = new byte[] { 0, 0, 0 };
746 for (int i = 0; i < sirenCnt; i++) {
747 localSirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
750 logger.debug("Cannot get siren settings");
755 if (!partitionsEnabled) {
758 boolean[] part = new boolean[partitionCnt];
759 for (int j = 0; j < partitionCnt; j++) {
762 for (int i = 0; i < zoneCnt; i++) {
763 zoneSettings[i] = new PowermaxZoneSettings(null, (byte) 0xFF, (byte) 0xFF, null, part);
765 for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
766 x10Settings[i] = new PowermaxX10Settings(null, true);
770 pinCodes = localPinCodes;
771 keypad1wEnrolled = localKeypad1wEnrolled;
772 keypad2wEnrolled = localKeypad2wEnrolled;
773 sirensEnrolled = localSirensEnrolled;
779 * Update the name of a zone
781 * @param zoneIdx the zone index (first zone is index 1)
782 * @param zoneNameIdx the index in the table of zone names
784 public void updateZoneName(int zoneIdx, byte zoneNameIdx) {
785 PowermaxZoneSettings zone = getZoneSettings(zoneIdx);
789 PowermaxZoneName zoneName = PowermaxZoneName.fromId(zoneNameIdx & 0x0000001F);
790 name = zoneName.getName();
791 } catch (IllegalArgumentException e) {
792 logger.debug("Zone id out of bounds {}", zoneNameIdx & 0x0000001F);
800 * Update the type of a zone
802 * @param zoneIdx the zone index (first zone is index 1)
803 * @param zoneInfo the zone info as an internal code
805 public void updateZoneInfo(int zoneIdx, int zoneInfo) {
806 PowermaxZoneSettings zone = getZoneSettings(zoneIdx);
808 zone.setType((byte) (zoneInfo & 0x0000000F));
812 public String getInfo() {
813 String str = "\nPanel is of type " + panelType.getLabel();
815 int zoneCnt = panelType.getWireless() + panelType.getWired();
816 int partitionCnt = panelType.getPartitions();
817 int sirenCnt = panelType.getSirens();
818 int keypad1wCnt = panelType.getKeypads1w();
819 int keypad2wCnt = panelType.getKeypads2w();
820 // int customCnt = panelType.getCustomZones();
822 if (!partitionsEnabled) {
826 // for (int i = 0; i < (26 + customCnt); i++) {
829 // PowermaxZoneName zoneName = PowermaxZoneName.fromId(i);
830 // name = zoneName.getName();
831 // } catch (IllegalArgumentException e) {
832 // logger.debug("Zone id out of bounds {}", i);
835 // str += String.format("\nZone name %d; %s", i + 1, name);
837 for (int i = 0; i < phoneNumbers.length; i++) {
838 if (phoneNumbers[i] != null) {
839 str += String.format("\nPhone number %d: %s", i + 1, phoneNumbers[i]);
842 str += String.format("\nBell time: %d minutes", bellTime);
843 str += String.format("\nSilent panic: %s", silentPanic ? "enabled" : "disabled");
844 str += String.format("\nQuick arm: %s", quickArm ? "enabled" : "disabled");
845 str += String.format("\nZone bypass: %s", bypassEnabled ? "enabled" : "disabled");
846 str += String.format("\nEPROM: %s", (panelEprom != null) ? panelEprom : "Undefined");
847 str += String.format("\nSW: %s", (panelSoftware != null) ? panelSoftware : "Undefined");
848 str += String.format("\nSerial: %s", (panelSerial != null) ? panelSerial : "Undefined");
849 str += String.format("\nUse partitions: %s", partitionsEnabled ? "enabled" : "disabled");
850 str += String.format("\nNumber of partitions: %d", partitionCnt);
851 for (int i = 0; i < zoneCnt; i++) {
852 if (zoneSettings[i] != null) {
854 for (int j = 1; j <= partitionCnt; j++) {
855 if (zoneSettings[i].isInPartition(j)) {
859 str += String.format("\nZone %d %s: %s (chime = %s; sensor type = %s; partitions = %s)", i + 1,
860 zoneSettings[i].getName(), zoneSettings[i].getType(), zoneSettings[i].getChime(),
861 zoneSettings[i].getSensorType(), partStr);
864 for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
865 if (x10Settings[i] != null && x10Settings[i].isEnabled()) {
866 str += String.format("\n%s: %s enabled", (i == 0) ? "PGM" : ("X10 " + i),
867 (x10Settings[i].getName() != null) ? x10Settings[i].getName() : "");
870 for (int i = 1; i <= sirenCnt; i++) {
871 if (isSirenEnrolled(i)) {
872 str += String.format("\nSiren %d enrolled", i);
875 for (int i = 1; i <= keypad1wCnt; i++) {
876 if (isKeypad1wEnrolled(i)) {
877 str += String.format("\nKeypad 1w %d enrolled", i);
880 for (int i = 1; i <= keypad2wCnt; i++) {
881 if (isKeypad2wEnrolled(i)) {
882 str += String.format("\nKeypad 2w %d enrolled", i);
885 return "Powermax alarm binding:" + str;