2 * Copyright (c) 2010-2021 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.io.UnsupportedEncodingException;
16 import java.util.Arrays;
17 import java.util.Calendar;
18 import java.util.GregorianCalendar;
19 import java.util.stream.IntStream;
21 import org.openhab.binding.powermax.internal.message.PowermaxMessageConstants;
22 import org.openhab.binding.powermax.internal.message.PowermaxSendType;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
27 * A class to store all the settings of the alarm system
29 * @author Laurent Garnier - Initial contribution
31 public class PowermaxPanelSettings {
33 private final Logger logger = LoggerFactory.getLogger(PowermaxPanelSettings.class);
35 /** Number of PGM and X10 devices managed by the system */
36 private static final int NB_PGM_X10_DEVICES = 16;
38 /** Raw buffers for settings */
39 private Byte[][] rawSettings;
41 private PowermaxPanelType panelType;
42 private String[] phoneNumbers;
44 private boolean silentPanic;
45 private boolean quickArm;
46 private boolean bypassEnabled;
47 private boolean partitionsEnabled;
48 private String[] pinCodes;
49 private String panelEprom;
50 private String panelSoftware;
51 private String panelSerial;
52 private PowermaxZoneSettings[] zoneSettings;
53 private PowermaxX10Settings[] x10Settings;
54 private boolean[] keypad1wEnrolled;
55 private boolean[] keypad2wEnrolled;
56 private boolean[] sirensEnrolled;
61 * @param defaultPanelType the default panel type to consider
63 public PowermaxPanelSettings(PowermaxPanelType defaultPanelType) {
64 rawSettings = new Byte[0x100][];
65 panelType = defaultPanelType;
66 phoneNumbers = new String[4];
68 int zoneCnt = panelType.getWireless() + panelType.getWired();
69 zoneSettings = new PowermaxZoneSettings[zoneCnt];
70 x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES];
74 * @return the panel type
76 public PowermaxPanelType getPanelType() {
81 * @return the length of time the bell or siren sounds (in minutes)
83 public int getBellTime() {
88 * @return true if panic alarms are silent; false if audible
90 public boolean isSilentPanic() {
95 * @return true if bypassing zones is enabled; false if not
97 public boolean isBypassEnabled() {
102 * @return true if partitions usage is enabled; false if not
104 public boolean isPartitionsEnabled() {
105 return partitionsEnabled;
109 * @return the panel EEPROM version
111 public String getPanelEprom() {
116 * @return the panel software version
118 public String getPanelSoftware() {
119 return panelSoftware;
123 * @return the panel serial ID
125 public String getPanelSerial() {
130 * @return the number of zones
132 public int getNbZones() {
133 return zoneSettings.length;
137 * @return an integer stream for iterating over the range of zone numbers
139 public IntStream getZoneRange() {
140 return IntStream.rangeClosed(1, getNbZones());
144 * Get the settings relative to a zone
146 * @param zone the zone index (from 1 to NumberOfZones)
148 * @return the settings of the zone
150 public PowermaxZoneSettings getZoneSettings(int zone) {
151 return ((zone < 1) || (zone > zoneSettings.length)) ? null : zoneSettings[zone - 1];
155 * Get a zone's display name
157 * @param zone the zone index (from 1 to NumberOfZones)
159 * @return the name of the zone
161 public String getZoneName(int zone) {
162 PowermaxZoneSettings zoneSettings = getZoneSettings(zone);
163 return (zoneSettings == null) ? null : zoneSettings.getName();
167 * Get a friendly display name for a zone, user, or device
168 * (any possible source for an event)
170 * @param zoneOrUser the zone, user, or device code
172 * @return the display name
174 public String getZoneOrUserName(int zoneOrUser) {
175 String zoneName = getZoneName(zoneOrUser);
177 if (zoneOrUser >= 1 && zoneOrUser <= zoneSettings.length && zoneName != null) {
178 return String.format("%s[%d]", zoneName, zoneOrUser);
180 return PowermaxMessageConstants.getZoneOrUser(zoneOrUser);
185 * @return the number of PGM and X10 devices managed by the system
187 public int getNbPGMX10Devices() {
188 return NB_PGM_X10_DEVICES;
192 * Get the settings relative to the PGM
194 * @return the settings of the PGM
196 public PowermaxX10Settings getPGMSettings() {
197 return x10Settings[0];
201 * Get the settings relative to a X10 device
203 * @param idx the index (from 1 to 15)
205 * @return the settings of the X10 device
207 public PowermaxX10Settings getX10Settings(int idx) {
208 return ((idx < 1) || (idx >= x10Settings.length)) ? null : x10Settings[idx];
212 * @param idx the keypad index (first is 1)
214 * @return true if the 1 way keypad is enrolled; false if not
216 public boolean isKeypad1wEnrolled(int idx) {
217 return ((keypad1wEnrolled == null) || (idx < 1) || (idx >= keypad1wEnrolled.length)) ? false
218 : keypad1wEnrolled[idx - 1];
222 * @param idx the keypad index (first is 1)
224 * @return true if the 2 way keypad is enrolled; false if not
226 public boolean isKeypad2wEnrolled(int idx) {
227 return ((keypad2wEnrolled == null) || (idx < 1) || (idx >= keypad2wEnrolled.length)) ? false
228 : keypad2wEnrolled[idx - 1];
232 * @param idx the siren index (first is 1)
234 * @return true if the siren is enrolled; false if not
236 public boolean isSirenEnrolled(int idx) {
237 return ((sirensEnrolled == null) || (idx < 1) || (idx >= sirensEnrolled.length)) ? false
238 : sirensEnrolled[idx - 1];
242 * @return the PIN code of the first user of null if unknown (standard mode)
244 public String getFirstPinCode() {
245 return (pinCodes == null) ? null : pinCodes[0];
248 public void updateRawSettings(byte[] data) {
249 if ((data == null) || (data.length < 3)) {
253 int end = data.length - 3;
254 int index = data[0] & 0x000000FF;
255 int page = data[1] & 0x000000FF;
256 int pageMin = page + (index + start) / 0x100;
257 int indexPageMin = (index + start) % 0x100;
258 int pageMax = page + (index + end) / 0x100;
259 int indexPageMax = (index + end) % 0x100;
261 for (int i = pageMin; i <= pageMax; i++) {
265 start = indexPageMin;
270 if (rawSettings[i] == null) {
271 rawSettings[i] = new Byte[0x100];
273 for (int j = start; j <= end; j++) {
274 rawSettings[i][j] = data[index++];
279 private byte[] readSettings(PowermaxSendType msgType, int start, int end) {
280 byte[] message = msgType.getMessage();
281 int page = message[2] & 0x000000FF;
282 int index = message[1] & 0x000000FF;
283 return readSettings(page, index + start, index + end);
286 private byte[] readSettings(int page, int start, int end) {
287 int pageMin = page + start / 0x100;
288 int indexPageMin = start % 0x100;
289 int pageMax = page + end / 0x100;
290 int indexPageMax = end % 0x100;
292 boolean missingData = false;
293 for (int i = pageMin; i <= pageMax; i++) {
297 start2 = indexPageMin;
302 index += end2 - start2 + 1;
303 for (int j = start2; j <= end2; j++) {
304 if ((rawSettings[i] == null) || (rawSettings[i][j] == null)) {
314 logger.debug("readSettings({}, {}, {}): missing data", page, start, end);
317 byte[] result = new byte[index];
319 for (int i = pageMin; i <= pageMax; i++) {
323 start2 = indexPageMin;
328 for (int j = start2; j <= end2; j++) {
329 result[index++] = rawSettings[i][j];
335 private String readSettingsAsString(PowermaxSendType msgType, int start, int end) {
336 byte[] message = msgType.getMessage();
337 int page = message[2] & 0x000000FF;
338 int index = message[1] & 0x000000FF;
339 String result = null;
340 byte[] data = readSettings(page, index + start, index + end);
341 if ((data != null) && ((data[0] & 0x000000FF) != 0x000000FF)) {
343 for (int i = 0; i < data.length; i++) {
344 boolean endStr = false;
345 switch (data[i] & 0x000000FF) {
359 if ((data[i] & 0x000000FF) >= 0x20) {
361 result += new String(data, i, 1, "US-ASCII");
362 } catch (UnsupportedEncodingException e) {
363 logger.debug("Unhandled character code {}", data[i]);
366 logger.debug("Unhandled character code {}", data[i]);
374 result = result.trim();
380 * Process and store all the panel settings from the raw buffers
382 * @param PowerlinkMode true if in Powerlink mode or false if in standard mode
383 * @param defaultPanelType the default panel type to consider if not found in the raw buffers
384 * @param timeSet the time in milliseconds used to set time and date; null if no sync time requested
386 * @return true if no problem encountered to get all the settings; false if not
388 @SuppressWarnings("null")
389 public boolean process(boolean PowerlinkMode, PowermaxPanelType defaultPanelType, Long timeSet) {
390 logger.debug("Process settings Powerlink {}", PowerlinkMode);
392 boolean result = true;
396 // Identify panel type
397 panelType = defaultPanelType;
399 data = readSettings(PowermaxSendType.DL_SERIAL, 7, 7);
402 panelType = PowermaxPanelType.fromCode(data[0]);
403 } catch (IllegalArgumentException e) {
404 logger.debug("Powermax alarm binding: unknwon panel type for code {}", data[0] & 0x000000FF);
405 panelType = defaultPanelType;
408 logger.debug("Cannot get panel type");
413 int zoneCnt = panelType.getWireless() + panelType.getWired();
414 int customCnt = panelType.getCustomZones();
415 int userCnt = panelType.getUserCodes();
416 int partitionCnt = panelType.getPartitions();
417 int sirenCnt = panelType.getSirens();
418 int keypad1wCnt = panelType.getKeypads1w();
419 int keypad2wCnt = panelType.getKeypads2w();
421 phoneNumbers = new String[4];
425 bypassEnabled = false;
426 partitionsEnabled = false;
427 pinCodes = new String[userCnt];
429 panelSoftware = null;
431 zoneSettings = new PowermaxZoneSettings[zoneCnt];
432 x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES];
433 keypad1wEnrolled = new boolean[keypad1wCnt];
434 keypad2wEnrolled = new boolean[keypad2wCnt];
435 sirensEnrolled = new boolean[sirenCnt];
438 // Check time and date
439 data = readSettings(PowermaxSendType.DL_TIME, 0, 5);
441 GregorianCalendar cal = new GregorianCalendar();
442 cal.set(Calendar.MILLISECOND, 0);
443 cal.set(Calendar.SECOND, data[0] & 0x000000FF);
444 cal.set(Calendar.MINUTE, data[1] & 0x000000FF);
445 cal.set(Calendar.HOUR_OF_DAY, data[2] & 0x000000FF);
446 cal.set(Calendar.DAY_OF_MONTH, data[3] & 0x000000FF);
447 cal.set(Calendar.MONTH, (data[4] & 0x000000FF) - 1);
448 cal.set(Calendar.YEAR, (data[5] & 0x000000FF) + 2000);
449 long timeRead = cal.getTimeInMillis();
450 logger.debug("Powermax alarm binding: time {}",
451 String.format("%02d/%02d/%04d %02d:%02d:%02d", cal.get(Calendar.DAY_OF_MONTH),
452 cal.get(Calendar.MONTH) + 1, cal.get(Calendar.YEAR), cal.get(Calendar.HOUR_OF_DAY),
453 cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)));
455 // Check if time sync was OK
456 if (timeSet != null) {
457 long delta = (timeRead - timeSet) / 1000;
459 logger.debug("Powermax alarm binding: time sync OK (delta {} s)", delta);
461 logger.info("Powermax alarm binding: time sync failed ! (delta {} s)", delta);
465 logger.debug("Cannot get time and date settings");
469 // Process zone names
471 for (int i = 0; i < (26 + customCnt); i++) {
472 String str = readSettingsAsString(PowermaxSendType.DL_ZONESTR, i * 16, (i + 1) * 16 - 1);
475 PowermaxZoneName zoneName = PowermaxZoneName.fromId(i);
476 zoneName.setName(str);
477 } catch (IllegalArgumentException e) {
478 logger.debug("Zone id out of bounds {}", i);
485 logger.debug("Cannot get all zone names");
489 // Process communication settings
491 for (int i = 0; i < phoneNumbers.length; i++) {
492 data = readSettings(PowermaxSendType.DL_PHONENRS, 8 * i, 8 * i + 7);
494 for (int j = 0; j < 8; j++) {
495 if ((data[j] & 0x000000FF) != 0x000000FF) {
497 phoneNumbers[i] = "";
499 if (phoneNumbers[i] != null) {
500 phoneNumbers[i] += String.format("%02X", data[j] & 0x000000FF);
509 logger.debug("Cannot get all communication settings");
513 // Process alarm settings
514 data = readSettings(PowermaxSendType.DL_COMMDEF, 0, 0x1B);
516 bellTime = data[3] & 0x000000FF;
517 silentPanic = (data[0x19] & 0x00000010) == 0x00000010;
518 quickArm = (data[0x1A] & 0x00000008) == 0x00000008;
519 bypassEnabled = (data[0x1B] & 0x000000C0) != 0;
521 logger.debug("Cannot get alarm settings");
525 // Process user PIN codes
527 panelType.isPowerMaster() ? PowermaxSendType.DL_MR_PINCODES : PowermaxSendType.DL_PINCODES, 0,
530 for (int i = 0; i < userCnt; i++) {
531 pinCodes[i] = String.format("%02X%02X", data[i * 2] & 0x000000FF, data[i * 2 + 1] & 0x000000FF);
534 logger.debug("Cannot get PIN codes");
538 // Process EEPROM version
539 panelEprom = readSettingsAsString(PowermaxSendType.DL_PANELFW, 0, 15);
540 if (panelEprom == null) {
541 logger.debug("Cannot get EEPROM version");
545 // Process software version
546 panelSoftware = readSettingsAsString(PowermaxSendType.DL_PANELFW, 16, 31);
547 if (panelSoftware == null) {
548 logger.debug("Cannot get software version");
554 data = readSettings(PowermaxSendType.DL_SERIAL, 0, 5);
556 for (int i = 0; i <= 5; i++) {
557 if ((data[i] & 0x000000FF) != 0x000000FF) {
558 panelSerial += String.format("%02X", data[i] & 0x000000FF);
564 logger.debug("Cannot get serial ID");
568 // Check if partitions are enabled (only on panels that support partitions)
569 byte[] partitions = null;
570 if (partitionCnt > 1) {
571 partitions = readSettings(PowermaxSendType.DL_PARTITIONS, 0, 0x10 + zoneCnt);
572 if (partitions != null) {
573 partitionsEnabled = (partitions[0] & 0x000000FF) == 1;
575 logger.debug("Cannot get partitions information");
578 if (!partitionsEnabled) {
583 // Process zone settings
584 data = readSettings(PowermaxSendType.DL_ZONES, 0, zoneCnt * 4 - 1);
585 byte[] zoneNr = null;
586 byte[] dataMr = null;
587 if (panelType.isPowerMaster()) {
588 zoneNr = readSettings(PowermaxSendType.DL_MR_ZONENAMES, 0, zoneCnt - 1);
589 dataMr = readSettings(PowermaxSendType.DL_MR_ZONES, 0, zoneCnt * 10 - 2);
591 zoneNr = readSettings(PowermaxSendType.DL_ZONENAMES, 0, zoneCnt - 1);
593 if ((data != null) && (zoneNr != null)) {
594 byte[] zero3 = new byte[] { 0, 0, 0 };
595 byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
597 for (int i = 0; i < zoneCnt; i++) {
600 PowermaxZoneName zone = PowermaxZoneName.fromId(zoneNr[i] & 0x0000001F);
601 zoneName = zone.getName();
602 } catch (IllegalArgumentException e) {
603 logger.debug("Zone id out of bounds {}", zoneNr[i] & 0x0000001F);
607 boolean zoneEnrolled;
610 String sensorTypeStr;
611 if (panelType.isPowerMaster()) {
612 zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(dataMr, i * 10 + 4, i * 10 + 9), zero5);
614 sensorTypeCode = dataMr[i * 10 + 5];
616 PowermasterSensorType sensorType = PowermasterSensorType.fromCode(sensorTypeCode);
617 sensorTypeStr = sensorType.getLabel();
618 } catch (IllegalArgumentException e) {
619 sensorTypeStr = null;
622 zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
623 zoneInfo = data[i * 4 + 3];
624 sensorTypeCode = data[i * 4 + 2];
626 PowermaxSensorType sensorType = PowermaxSensorType
627 .fromCode((byte) (sensorTypeCode & 0x0000000F));
628 sensorTypeStr = sensorType.getLabel();
629 } catch (IllegalArgumentException e) {
630 sensorTypeStr = null;
634 byte zoneType = (byte) (zoneInfo & 0x0000000F);
635 byte zoneChime = (byte) ((zoneInfo >> 4) & 0x00000003);
637 boolean[] part = new boolean[partitionCnt];
638 if (partitionCnt > 1) {
639 for (int j = 0; j < partitionCnt; j++) {
640 part[j] = (partitions != null) ? ((partitions[0x11 + i] & (1 << j)) != 0) : true;
646 zoneSettings[i] = new PowermaxZoneSettings(zoneName, zoneType, zoneChime, sensorTypeStr, part);
650 logger.debug("Cannot get zone settings");
654 data = readSettings(PowermaxSendType.DL_PGMX10, 0, 148);
655 zoneNr = readSettings(PowermaxSendType.DL_X10NAMES, 0, NB_PGM_X10_DEVICES - 2);
656 if ((data != null) && (zoneNr != null)) {
657 for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
658 boolean enabled = false;
659 String zoneName = null;
660 for (int j = 0; j <= 8; j++) {
661 if (data[5 + i + j * 0x10] != 0) {
668 PowermaxZoneName zone = PowermaxZoneName.fromId(zoneNr[i - 1] & 0x0000001F);
669 zoneName = zone.getName();
670 } catch (IllegalArgumentException e) {
671 logger.debug("Zone id out of bounds {}", zoneNr[i - 1] & 0x0000001F);
675 x10Settings[i] = new PowermaxX10Settings(zoneName, enabled);
678 logger.debug("Cannot get PGM / X10 settings");
682 if (panelType.isPowerMaster()) {
683 // Process 2 way keypad settings
684 data = readSettings(PowermaxSendType.DL_MR_KEYPADS, 0, keypad2wCnt * 10 - 1);
686 byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
688 for (int i = 0; i < keypad2wCnt; i++) {
689 keypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9), zero5);
692 logger.debug("Cannot get 2 way keypad settings");
695 // Process siren settings
696 data = readSettings(PowermaxSendType.DL_MR_SIRENS, 0, sirenCnt * 10 - 1);
698 byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
700 for (int i = 0; i < sirenCnt; i++) {
701 sirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9), zero5);
704 logger.debug("Cannot get siren settings");
708 // Process 1 way keypad settings
709 data = readSettings(PowermaxSendType.DL_1WKEYPAD, 0, keypad1wCnt * 4 - 1);
711 byte[] zero2 = new byte[] { 0, 0 };
713 for (int i = 0; i < keypad1wCnt; i++) {
714 keypad1wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 2), zero2);
717 logger.debug("Cannot get 1 way keypad settings");
720 // Process 2 way keypad settings
721 data = readSettings(PowermaxSendType.DL_2WKEYPAD, 0, keypad2wCnt * 4 - 1);
723 byte[] zero3 = new byte[] { 0, 0, 0 };
725 for (int i = 0; i < keypad2wCnt; i++) {
726 keypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
729 logger.debug("Cannot get 2 way keypad settings");
732 // Process siren settings
733 data = readSettings(PowermaxSendType.DL_SIRENS, 0, sirenCnt * 4 - 1);
735 byte[] zero3 = new byte[] { 0, 0, 0 };
737 for (int i = 0; i < sirenCnt; i++) {
738 sirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
741 logger.debug("Cannot get siren settings");
746 if (!partitionsEnabled) {
749 boolean[] part = new boolean[partitionCnt];
750 for (int j = 0; j < partitionCnt; j++) {
753 for (int i = 0; i < zoneCnt; i++) {
754 zoneSettings[i] = new PowermaxZoneSettings(null, (byte) 0xFF, (byte) 0xFF, null, part);
756 for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
757 x10Settings[i] = new PowermaxX10Settings(null, true);
765 * Update the name of a zone
767 * @param zoneIdx the zone index (first zone is index 1)
768 * @param zoneNameIdx the index in the table of zone names
770 public void updateZoneName(int zoneIdx, byte zoneNameIdx) {
771 PowermaxZoneSettings zone = getZoneSettings(zoneIdx);
775 PowermaxZoneName zoneName = PowermaxZoneName.fromId(zoneNameIdx & 0x0000001F);
776 name = zoneName.getName();
777 } catch (IllegalArgumentException e) {
778 logger.debug("Zone id out of bounds {}", zoneNameIdx & 0x0000001F);
786 * Update the type of a zone
788 * @param zoneIdx the zone index (first zone is index 1)
789 * @param zoneInfo the zone info as an internal code
791 public void updateZoneInfo(int zoneIdx, int zoneInfo) {
792 PowermaxZoneSettings zone = getZoneSettings(zoneIdx);
794 zone.setType((byte) (zoneInfo & 0x0000000F));
798 public String getInfo() {
799 String str = "\nPanel is of type " + panelType.getLabel();
801 int zoneCnt = panelType.getWireless() + panelType.getWired();
802 int partitionCnt = panelType.getPartitions();
803 int sirenCnt = panelType.getSirens();
804 int keypad1wCnt = panelType.getKeypads1w();
805 int keypad2wCnt = panelType.getKeypads2w();
806 // int customCnt = panelType.getCustomZones();
808 if (!partitionsEnabled) {
812 // for (int i = 0; i < (26 + customCnt); i++) {
815 // PowermaxZoneName zoneName = PowermaxZoneName.fromId(i);
816 // name = zoneName.getName();
817 // } catch (IllegalArgumentException e) {
818 // logger.debug("Zone id out of bounds {}", i);
821 // str += String.format("\nZone name %d; %s", i + 1, name);
823 for (int i = 0; i < phoneNumbers.length; i++) {
824 if (phoneNumbers[i] != null) {
825 str += String.format("\nPhone number %d: %s", i + 1, phoneNumbers[i]);
828 str += String.format("\nBell time: %d minutes", bellTime);
829 str += String.format("\nSilent panic: %s", silentPanic ? "enabled" : "disabled");
830 str += String.format("\nQuick arm: %s", quickArm ? "enabled" : "disabled");
831 str += String.format("\nZone bypass: %s", bypassEnabled ? "enabled" : "disabled");
832 str += String.format("\nEPROM: %s", (panelEprom != null) ? panelEprom : "Undefined");
833 str += String.format("\nSW: %s", (panelSoftware != null) ? panelSoftware : "Undefined");
834 str += String.format("\nSerial: %s", (panelSerial != null) ? panelSerial : "Undefined");
835 str += String.format("\nUse partitions: %s", partitionsEnabled ? "enabled" : "disabled");
836 str += String.format("\nNumber of partitions: %d", partitionCnt);
837 for (int i = 0; i < zoneCnt; i++) {
838 if (zoneSettings[i] != null) {
840 for (int j = 1; j <= partitionCnt; j++) {
841 if (zoneSettings[i].isInPartition(j)) {
845 str += String.format("\nZone %d %s: %s (chime = %s; sensor type = %s; partitions = %s)", i + 1,
846 zoneSettings[i].getName(), zoneSettings[i].getType(), zoneSettings[i].getChime(),
847 zoneSettings[i].getSensorType(), partStr);
850 for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
851 if (x10Settings[i] != null && x10Settings[i].isEnabled()) {
852 str += String.format("\n%s: %s enabled", (i == 0) ? "PGM" : ("X10 " + i),
853 (x10Settings[i].getName() != null) ? x10Settings[i].getName() : "");
856 for (int i = 1; i <= sirenCnt; i++) {
857 if (isSirenEnrolled(i)) {
858 str += String.format("\nSiren %d enrolled", i);
861 for (int i = 1; i <= keypad1wCnt; i++) {
862 if (isKeypad1wEnrolled(i)) {
863 str += String.format("\nKeypad 1w %d enrolled", i);
866 for (int i = 1; i <= keypad2wCnt; i++) {
867 if (isKeypad2wEnrolled(i)) {
868 str += String.format("\nKeypad 2w %d enrolled", i);
871 return "Powermax alarm binding:" + str;