]> git.basschouten.com Git - openhab-addons.git/blob
07fc054ecc15649565b3fcef1a83ca21afdde3a1
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.powermax.internal.state;
14
15 import java.io.UnsupportedEncodingException;
16 import java.util.Arrays;
17 import java.util.Calendar;
18 import java.util.GregorianCalendar;
19
20 import org.openhab.binding.powermax.internal.message.PowermaxSendType;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * A class to store all the settings of the alarm system
26  *
27  * @author Laurent Garnier - Initial contribution
28  */
29 public class PowermaxPanelSettings {
30
31     private final Logger logger = LoggerFactory.getLogger(PowermaxPanelSettings.class);
32
33     /** Number of PGM and X10 devices managed by the system */
34     private static final int NB_PGM_X10_DEVICES = 16;
35
36     /** Raw buffers for settings */
37     private Byte[][] rawSettings;
38
39     private PowermaxPanelType panelType;
40     private String[] phoneNumbers;
41     private int bellTime;
42     private boolean silentPanic;
43     private boolean quickArm;
44     private boolean bypassEnabled;
45     private boolean partitionsEnabled;
46     private String[] pinCodes;
47     private String panelEprom;
48     private String panelSoftware;
49     private String panelSerial;
50     private PowermaxZoneSettings[] zoneSettings;
51     private PowermaxX10Settings[] x10Settings;
52     private boolean[] keypad1wEnrolled;
53     private boolean[] keypad2wEnrolled;
54     private boolean[] sirensEnrolled;
55
56     /**
57      * Constructor
58      *
59      * @param defaultPanelType the default panel type to consider
60      */
61     public PowermaxPanelSettings(PowermaxPanelType defaultPanelType) {
62         rawSettings = new Byte[0x100][];
63         panelType = defaultPanelType;
64         phoneNumbers = new String[4];
65         bellTime = 4;
66         int zoneCnt = panelType.getWireless() + panelType.getWired();
67         zoneSettings = new PowermaxZoneSettings[zoneCnt];
68         x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES];
69     }
70
71     /**
72      * @return the panel type
73      */
74     public PowermaxPanelType getPanelType() {
75         return panelType;
76     }
77
78     /**
79      * @return true if bypassing zones is enabled; false if not
80      */
81     public boolean isBypassEnabled() {
82         return bypassEnabled;
83     }
84
85     /**
86      * @return true if partitions usage is enabled; false if not
87      */
88     public boolean isPartitionsEnabled() {
89         return partitionsEnabled;
90     }
91
92     /**
93      * @return the panel EEPROM version
94      */
95     public String getPanelEprom() {
96         return panelEprom;
97     }
98
99     /**
100      * @return the panel software version
101      */
102     public String getPanelSoftware() {
103         return panelSoftware;
104     }
105
106     /**
107      * @return the panel serial ID
108      */
109     public String getPanelSerial() {
110         return panelSerial;
111     }
112
113     /**
114      * @return the number of zones
115      */
116     public int getNbZones() {
117         return zoneSettings.length;
118     }
119
120     /**
121      * Get the settings relative to a zone
122      *
123      * @param zone the zone index (from 1 to NumberOfZones)
124      *
125      * @return the settings of the zone
126      */
127     public PowermaxZoneSettings getZoneSettings(int zone) {
128         return ((zone < 1) || (zone > zoneSettings.length)) ? null : zoneSettings[zone - 1];
129     }
130
131     /**
132      * @return the number of PGM and X10 devices managed by the system
133      */
134     public int getNbPGMX10Devices() {
135         return NB_PGM_X10_DEVICES;
136     }
137
138     /**
139      * Get the settings relative to the PGM
140      *
141      * @return the settings of the PGM
142      */
143     public PowermaxX10Settings getPGMSettings() {
144         return x10Settings[0];
145     }
146
147     /**
148      * Get the settings relative to a X10 device
149      *
150      * @param idx the index (from 1 to 15)
151      *
152      * @return the settings of the X10 device
153      */
154     public PowermaxX10Settings getX10Settings(int idx) {
155         return ((idx < 1) || (idx >= x10Settings.length)) ? null : x10Settings[idx];
156     }
157
158     /**
159      * @param idx the keypad index (first is 1)
160      *
161      * @return true if the 1 way keypad is enrolled; false if not
162      */
163     public boolean isKeypad1wEnrolled(int idx) {
164         return ((keypad1wEnrolled == null) || (idx < 1) || (idx >= keypad1wEnrolled.length)) ? false
165                 : keypad1wEnrolled[idx - 1];
166     }
167
168     /**
169      * @param idx the keypad index (first is 1)
170      *
171      * @return true if the 2 way keypad is enrolled; false if not
172      */
173     public boolean isKeypad2wEnrolled(int idx) {
174         return ((keypad2wEnrolled == null) || (idx < 1) || (idx >= keypad2wEnrolled.length)) ? false
175                 : keypad2wEnrolled[idx - 1];
176     }
177
178     /**
179      * @param idx the siren index (first is 1)
180      *
181      * @return true if the siren is enrolled; false if not
182      */
183     public boolean isSirenEnrolled(int idx) {
184         return ((sirensEnrolled == null) || (idx < 1) || (idx >= sirensEnrolled.length)) ? false
185                 : sirensEnrolled[idx - 1];
186     }
187
188     /**
189      * @return the PIN code of the first user of null if unknown (standard mode)
190      */
191     public String getFirstPinCode() {
192         return (pinCodes == null) ? null : pinCodes[0];
193     }
194
195     public void updateRawSettings(byte[] data) {
196         if ((data == null) || (data.length < 3)) {
197             return;
198         }
199         int start = 0;
200         int end = data.length - 3;
201         int index = data[0] & 0x000000FF;
202         int page = data[1] & 0x000000FF;
203         int pageMin = page + (index + start) / 0x100;
204         int indexPageMin = (index + start) % 0x100;
205         int pageMax = page + (index + end) / 0x100;
206         int indexPageMax = (index + end) % 0x100;
207         index = 2;
208         for (int i = pageMin; i <= pageMax; i++) {
209             start = 0;
210             end = 0xFF;
211             if (i == pageMin) {
212                 start = indexPageMin;
213             }
214             if (i == pageMax) {
215                 end = indexPageMax;
216             }
217             if (rawSettings[i] == null) {
218                 rawSettings[i] = new Byte[0x100];
219             }
220             for (int j = start; j <= end; j++) {
221                 rawSettings[i][j] = data[index++];
222             }
223         }
224     }
225
226     private byte[] readSettings(PowermaxSendType msgType, int start, int end) {
227         byte[] message = msgType.getMessage();
228         int page = message[2] & 0x000000FF;
229         int index = message[1] & 0x000000FF;
230         return readSettings(page, index + start, index + end);
231     }
232
233     private byte[] readSettings(int page, int start, int end) {
234         int pageMin = page + start / 0x100;
235         int indexPageMin = start % 0x100;
236         int pageMax = page + end / 0x100;
237         int indexPageMax = end % 0x100;
238         int index = 0;
239         boolean missingData = false;
240         for (int i = pageMin; i <= pageMax; i++) {
241             int start2 = 0;
242             int end2 = 0xFF;
243             if (i == pageMin) {
244                 start2 = indexPageMin;
245             }
246             if (i == pageMax) {
247                 end2 = indexPageMax;
248             }
249             index += end2 - start2 + 1;
250             for (int j = start2; j <= end2; j++) {
251                 if ((rawSettings[i] == null) || (rawSettings[i][j] == null)) {
252                     missingData = true;
253                     break;
254                 }
255             }
256             if (missingData) {
257                 break;
258             }
259         }
260         if (missingData) {
261             logger.debug("readSettings({}, {}, {}): missing data", page, start, end);
262             return null;
263         }
264         byte[] result = new byte[index];
265         index = 0;
266         for (int i = pageMin; i <= pageMax; i++) {
267             int start2 = 0;
268             int end2 = 0xFF;
269             if (i == pageMin) {
270                 start2 = indexPageMin;
271             }
272             if (i == pageMax) {
273                 end2 = indexPageMax;
274             }
275             for (int j = start2; j <= end2; j++) {
276                 result[index++] = rawSettings[i][j];
277             }
278         }
279         return result;
280     }
281
282     private String readSettingsAsString(PowermaxSendType msgType, int start, int end) {
283         byte[] message = msgType.getMessage();
284         int page = message[2] & 0x000000FF;
285         int index = message[1] & 0x000000FF;
286         String result = null;
287         byte[] data = readSettings(page, index + start, index + end);
288         if ((data != null) && ((data[0] & 0x000000FF) != 0x000000FF)) {
289             result = "";
290             for (int i = 0; i < data.length; i++) {
291                 boolean endStr = false;
292                 switch (data[i] & 0x000000FF) {
293                     case 0:
294                         endStr = true;
295                         break;
296                     case 1:
297                         result += "é";
298                         break;
299                     case 3:
300                         result += "è";
301                         break;
302                     case 5:
303                         result += "à";
304                         break;
305                     default:
306                         if ((data[i] & 0x000000FF) >= 0x20) {
307                             try {
308                                 result += new String(data, i, 1, "US-ASCII");
309                             } catch (UnsupportedEncodingException e) {
310                                 logger.debug("Unhandled character code {}", data[i]);
311                             }
312                         } else {
313                             logger.debug("Unhandled character code {}", data[i]);
314                         }
315                         break;
316                 }
317                 if (endStr) {
318                     break;
319                 }
320             }
321             result = result.trim();
322         }
323         return result;
324     }
325
326     /**
327      * Process and store all the panel settings from the raw buffers
328      *
329      * @param PowerlinkMode true if in Powerlink mode or false if in standard mode
330      * @param defaultPanelType the default panel type to consider if not found in the raw buffers
331      * @param timeSet the time in milliseconds used to set time and date; null if no sync time requested
332      *
333      * @return true if no problem encountered to get all the settings; false if not
334      */
335     @SuppressWarnings("null")
336     public boolean process(boolean PowerlinkMode, PowermaxPanelType defaultPanelType, Long timeSet) {
337         logger.debug("Process settings Powerlink {}", PowerlinkMode);
338
339         boolean result = true;
340         boolean result2;
341         byte[] data;
342
343         // Identify panel type
344         panelType = defaultPanelType;
345         if (PowerlinkMode) {
346             data = readSettings(PowermaxSendType.DL_SERIAL, 7, 7);
347             if (data != null) {
348                 try {
349                     panelType = PowermaxPanelType.fromCode(data[0]);
350                 } catch (IllegalArgumentException e) {
351                     logger.debug("Powermax alarm binding: unknwon panel type for code {}", data[0] & 0x000000FF);
352                     panelType = defaultPanelType;
353                 }
354             } else {
355                 logger.debug("Cannot get panel type");
356                 result = false;
357             }
358         }
359
360         int zoneCnt = panelType.getWireless() + panelType.getWired();
361         int customCnt = panelType.getCustomZones();
362         int userCnt = panelType.getUserCodes();
363         int partitionCnt = panelType.getPartitions();
364         int sirenCnt = panelType.getSirens();
365         int keypad1wCnt = panelType.getKeypads1w();
366         int keypad2wCnt = panelType.getKeypads2w();
367
368         phoneNumbers = new String[4];
369         bellTime = 4;
370         silentPanic = false;
371         quickArm = false;
372         bypassEnabled = false;
373         partitionsEnabled = false;
374         pinCodes = new String[userCnt];
375         panelEprom = null;
376         panelSoftware = null;
377         panelSerial = null;
378         zoneSettings = new PowermaxZoneSettings[zoneCnt];
379         x10Settings = new PowermaxX10Settings[NB_PGM_X10_DEVICES];
380         keypad1wEnrolled = new boolean[keypad1wCnt];
381         keypad2wEnrolled = new boolean[keypad2wCnt];
382         sirensEnrolled = new boolean[sirenCnt];
383
384         if (PowerlinkMode) {
385             // Check time and date
386             data = readSettings(PowermaxSendType.DL_TIME, 0, 5);
387             if (data != null) {
388                 GregorianCalendar cal = new GregorianCalendar();
389                 cal.set(Calendar.MILLISECOND, 0);
390                 cal.set(Calendar.SECOND, data[0] & 0x000000FF);
391                 cal.set(Calendar.MINUTE, data[1] & 0x000000FF);
392                 cal.set(Calendar.HOUR_OF_DAY, data[2] & 0x000000FF);
393                 cal.set(Calendar.DAY_OF_MONTH, data[3] & 0x000000FF);
394                 cal.set(Calendar.MONTH, (data[4] & 0x000000FF) - 1);
395                 cal.set(Calendar.YEAR, (data[5] & 0x000000FF) + 2000);
396                 long timeRead = cal.getTimeInMillis();
397                 logger.debug("Powermax alarm binding: time {}",
398                         String.format("%02d/%02d/%04d %02d:%02d:%02d", cal.get(Calendar.DAY_OF_MONTH),
399                                 cal.get(Calendar.MONTH) + 1, cal.get(Calendar.YEAR), cal.get(Calendar.HOUR_OF_DAY),
400                                 cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)));
401
402                 // Check if time sync was OK
403                 if (timeSet != null) {
404                     long delta = (timeRead - timeSet) / 1000;
405                     if (delta <= 5) {
406                         logger.debug("Powermax alarm binding: time sync OK (delta {} s)", delta);
407                     } else {
408                         logger.info("Powermax alarm binding: time sync failed ! (delta {} s)", delta);
409                     }
410                 }
411             } else {
412                 logger.debug("Cannot get time and date settings");
413                 result = false;
414             }
415
416             // Process zone names
417             result2 = true;
418             for (int i = 0; i < (26 + customCnt); i++) {
419                 String str = readSettingsAsString(PowermaxSendType.DL_ZONESTR, i * 16, (i + 1) * 16 - 1);
420                 if (str != null) {
421                     try {
422                         PowermaxZoneName zoneName = PowermaxZoneName.fromId(i);
423                         zoneName.setName(str);
424                     } catch (IllegalArgumentException e) {
425                         logger.debug("Zone id out of bounds {}", i);
426                     }
427                 } else {
428                     result2 = false;
429                 }
430             }
431             if (!result2) {
432                 logger.debug("Cannot get all zone names");
433                 result = false;
434             }
435
436             // Process communication settings
437             result2 = true;
438             for (int i = 0; i < phoneNumbers.length; i++) {
439                 data = readSettings(PowermaxSendType.DL_PHONENRS, 8 * i, 8 * i + 7);
440                 if (data != null) {
441                     for (int j = 0; j < 8; j++) {
442                         if ((data[j] & 0x000000FF) != 0x000000FF) {
443                             if (j == 0) {
444                                 phoneNumbers[i] = "";
445                             }
446                             if (phoneNumbers[i] != null) {
447                                 phoneNumbers[i] += String.format("%02X", data[j] & 0x000000FF);
448                             }
449                         }
450                     }
451                 } else {
452                     result2 = false;
453                 }
454             }
455             if (!result2) {
456                 logger.debug("Cannot get all communication settings");
457                 result = false;
458             }
459
460             // Process alarm settings
461             data = readSettings(PowermaxSendType.DL_COMMDEF, 0, 0x1B);
462             if (data != null) {
463                 bellTime = data[3] & 0x000000FF;
464                 silentPanic = (data[0x19] & 0x00000010) == 0x00000010;
465                 quickArm = (data[0x1A] & 0x00000008) == 0x00000008;
466                 bypassEnabled = (data[0x1B] & 0x000000C0) != 0;
467             } else {
468                 logger.debug("Cannot get alarm settings");
469                 result = false;
470             }
471
472             // Process user PIN codes
473             data = readSettings(
474                     panelType.isPowerMaster() ? PowermaxSendType.DL_MR_PINCODES : PowermaxSendType.DL_PINCODES, 0,
475                     2 * userCnt - 1);
476             if (data != null) {
477                 for (int i = 0; i < userCnt; i++) {
478                     pinCodes[i] = String.format("%02X%02X", data[i * 2] & 0x000000FF, data[i * 2 + 1] & 0x000000FF);
479                 }
480             } else {
481                 logger.debug("Cannot get PIN codes");
482                 result = false;
483             }
484
485             // Process EEPROM version
486             panelEprom = readSettingsAsString(PowermaxSendType.DL_PANELFW, 0, 15);
487             if (panelEprom == null) {
488                 logger.debug("Cannot get EEPROM version");
489                 result = false;
490             }
491
492             // Process software version
493             panelSoftware = readSettingsAsString(PowermaxSendType.DL_PANELFW, 16, 31);
494             if (panelSoftware == null) {
495                 logger.debug("Cannot get software version");
496                 result = false;
497             }
498
499             // Process serial ID
500             panelSerial = "";
501             data = readSettings(PowermaxSendType.DL_SERIAL, 0, 5);
502             if (data != null) {
503                 for (int i = 0; i <= 5; i++) {
504                     if ((data[i] & 0x000000FF) != 0x000000FF) {
505                         panelSerial += String.format("%02X", data[i] & 0x000000FF);
506                     } else {
507                         panelSerial += ".";
508                     }
509                 }
510             } else {
511                 logger.debug("Cannot get serial ID");
512                 result = false;
513             }
514
515             // Check if partitions are enabled
516             byte[] partitions = readSettings(PowermaxSendType.DL_PARTITIONS, 0, 0x10 + zoneCnt);
517             if (partitions != null) {
518                 partitionsEnabled = (partitions[0] & 0x000000FF) == 1;
519             } else {
520                 logger.debug("Cannot get partitions information");
521                 result = false;
522             }
523             if (!partitionsEnabled) {
524                 partitionCnt = 1;
525             }
526
527             // Process zone settings
528             data = readSettings(PowermaxSendType.DL_ZONES, 0, zoneCnt * 4 - 1);
529             byte[] zoneNr = null;
530             byte[] dataMr = null;
531             if (panelType.isPowerMaster()) {
532                 zoneNr = readSettings(PowermaxSendType.DL_MR_ZONENAMES, 0, zoneCnt - 1);
533                 dataMr = readSettings(PowermaxSendType.DL_MR_ZONES, 0, zoneCnt * 10 - 2);
534             } else {
535                 zoneNr = readSettings(PowermaxSendType.DL_ZONENAMES, 0, zoneCnt - 1);
536             }
537             if ((data != null) && (zoneNr != null)) {
538                 byte[] zero3 = new byte[] { 0, 0, 0 };
539                 byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
540
541                 for (int i = 0; i < zoneCnt; i++) {
542                     String zoneName;
543                     try {
544                         PowermaxZoneName zone = PowermaxZoneName.fromId(zoneNr[i] & 0x0000001F);
545                         zoneName = zone.getName();
546                     } catch (IllegalArgumentException e) {
547                         logger.debug("Zone id out of bounds {}", zoneNr[i] & 0x0000001F);
548                         zoneName = null;
549                     }
550
551                     boolean zoneEnrolled;
552                     byte zoneInfo;
553                     byte sensorTypeCode;
554                     String sensorTypeStr;
555                     if (panelType.isPowerMaster()) {
556                         zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(dataMr, i * 10 + 4, i * 10 + 9), zero5);
557                         zoneInfo = data[i];
558                         sensorTypeCode = dataMr[i * 10 + 5];
559                         try {
560                             PowermasterSensorType sensorType = PowermasterSensorType.fromCode(sensorTypeCode);
561                             sensorTypeStr = sensorType.getLabel();
562                         } catch (IllegalArgumentException e) {
563                             sensorTypeStr = null;
564                         }
565                     } else {
566                         zoneEnrolled = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
567                         zoneInfo = data[i * 4 + 3];
568                         sensorTypeCode = data[i * 4 + 2];
569                         try {
570                             PowermaxSensorType sensorType = PowermaxSensorType
571                                     .fromCode((byte) (sensorTypeCode & 0x0000000F));
572                             sensorTypeStr = sensorType.getLabel();
573                         } catch (IllegalArgumentException e) {
574                             sensorTypeStr = null;
575                         }
576                     }
577                     if (zoneEnrolled) {
578                         byte zoneType = (byte) (zoneInfo & 0x0000000F);
579                         byte zoneChime = (byte) ((zoneInfo >> 4) & 0x00000003);
580
581                         boolean[] part = new boolean[partitionCnt];
582                         if (partitionCnt > 1) {
583                             for (int j = 0; j < partitionCnt; j++) {
584                                 part[j] = (partitions != null) ? ((partitions[0x11 + i] & (1 << j)) != 0) : true;
585                             }
586                         } else {
587                             part[0] = true;
588                         }
589
590                         zoneSettings[i] = new PowermaxZoneSettings(zoneName, zoneType, zoneChime, sensorTypeStr, part);
591                     }
592                 }
593             } else {
594                 logger.debug("Cannot get zone settings");
595                 result = false;
596             }
597
598             data = readSettings(PowermaxSendType.DL_PGMX10, 0, 148);
599             zoneNr = readSettings(PowermaxSendType.DL_X10NAMES, 0, NB_PGM_X10_DEVICES - 2);
600             if ((data != null) && (zoneNr != null)) {
601                 for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
602                     boolean enabled = false;
603                     String zoneName = null;
604                     for (int j = 0; j <= 8; j++) {
605                         if (data[5 + i + j * 0x10] != 0) {
606                             enabled = true;
607                             break;
608                         }
609                     }
610                     if (i > 0) {
611                         try {
612                             PowermaxZoneName zone = PowermaxZoneName.fromId(zoneNr[i - 1] & 0x0000001F);
613                             zoneName = zone.getName();
614                         } catch (IllegalArgumentException e) {
615                             logger.debug("Zone id out of bounds {}", zoneNr[i - 1] & 0x0000001F);
616                             zoneName = null;
617                         }
618                     }
619                     x10Settings[i] = new PowermaxX10Settings(zoneName, enabled);
620                 }
621             } else {
622                 logger.debug("Cannot get PGM / X10 settings");
623                 result = false;
624             }
625
626             if (panelType.isPowerMaster()) {
627                 // Process 2 way keypad settings
628                 data = readSettings(PowermaxSendType.DL_MR_KEYPADS, 0, keypad2wCnt * 10 - 1);
629                 if (data != null) {
630                     byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
631
632                     for (int i = 0; i < keypad2wCnt; i++) {
633                         keypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9), zero5);
634                     }
635                 } else {
636                     logger.debug("Cannot get 2 way keypad settings");
637                     result = false;
638                 }
639                 // Process siren settings
640                 data = readSettings(PowermaxSendType.DL_MR_SIRENS, 0, sirenCnt * 10 - 1);
641                 if (data != null) {
642                     byte[] zero5 = new byte[] { 0, 0, 0, 0, 0 };
643
644                     for (int i = 0; i < sirenCnt; i++) {
645                         sirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 10 + 4, i * 10 + 9), zero5);
646                     }
647                 } else {
648                     logger.debug("Cannot get siren settings");
649                     result = false;
650                 }
651             } else {
652                 // Process 1 way keypad settings
653                 data = readSettings(PowermaxSendType.DL_1WKEYPAD, 0, keypad1wCnt * 4 - 1);
654                 if (data != null) {
655                     byte[] zero2 = new byte[] { 0, 0 };
656
657                     for (int i = 0; i < keypad1wCnt; i++) {
658                         keypad1wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 2), zero2);
659                     }
660                 } else {
661                     logger.debug("Cannot get 1 way keypad settings");
662                     result = false;
663                 }
664                 // Process 2 way keypad settings
665                 data = readSettings(PowermaxSendType.DL_2WKEYPAD, 0, keypad2wCnt * 4 - 1);
666                 if (data != null) {
667                     byte[] zero3 = new byte[] { 0, 0, 0 };
668
669                     for (int i = 0; i < keypad2wCnt; i++) {
670                         keypad2wEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
671                     }
672                 } else {
673                     logger.debug("Cannot get 2 way keypad settings");
674                     result = false;
675                 }
676                 // Process siren settings
677                 data = readSettings(PowermaxSendType.DL_SIRENS, 0, sirenCnt * 4 - 1);
678                 if (data != null) {
679                     byte[] zero3 = new byte[] { 0, 0, 0 };
680
681                     for (int i = 0; i < sirenCnt; i++) {
682                         sirensEnrolled[i] = !Arrays.equals(Arrays.copyOfRange(data, i * 4, i * 4 + 3), zero3);
683                     }
684                 } else {
685                     logger.debug("Cannot get siren settings");
686                     result = false;
687                 }
688             }
689         } else {
690             if (!partitionsEnabled) {
691                 partitionCnt = 1;
692             }
693             boolean[] part = new boolean[partitionCnt];
694             for (int j = 0; j < partitionCnt; j++) {
695                 part[j] = true;
696             }
697             for (int i = 0; i < zoneCnt; i++) {
698                 zoneSettings[i] = new PowermaxZoneSettings(null, (byte) 0xFF, (byte) 0xFF, null, part);
699             }
700             for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
701                 x10Settings[i] = new PowermaxX10Settings(null, true);
702             }
703         }
704
705         return result;
706     }
707
708     /**
709      * Update the name of a zone
710      *
711      * @param zoneIdx the zone index (first zone is index 1)
712      * @param zoneNameIdx the index in the table of zone names
713      */
714     public void updateZoneName(int zoneIdx, byte zoneNameIdx) {
715         PowermaxZoneSettings zone = getZoneSettings(zoneIdx);
716         if (zone != null) {
717             String name;
718             try {
719                 PowermaxZoneName zoneName = PowermaxZoneName.fromId(zoneNameIdx & 0x0000001F);
720                 name = zoneName.getName();
721             } catch (IllegalArgumentException e) {
722                 logger.debug("Zone id out of bounds {}", zoneNameIdx & 0x0000001F);
723                 name = null;
724             }
725             zone.setName(name);
726         }
727     }
728
729     /**
730      * Update the type of a zone
731      *
732      * @param zoneIdx the zone index (first zone is index 1)
733      * @param zoneInfo the zone info as an internal code
734      */
735     public void updateZoneInfo(int zoneIdx, int zoneInfo) {
736         PowermaxZoneSettings zone = getZoneSettings(zoneIdx);
737         if (zone != null) {
738             zone.setType((byte) (zoneInfo & 0x0000000F));
739         }
740     }
741
742     public String getInfo() {
743         String str = "\nPanel is of type " + panelType.getLabel();
744
745         int zoneCnt = panelType.getWireless() + panelType.getWired();
746         int partitionCnt = panelType.getPartitions();
747         int sirenCnt = panelType.getSirens();
748         int keypad1wCnt = panelType.getKeypads1w();
749         int keypad2wCnt = panelType.getKeypads2w();
750         // int customCnt = panelType.getCustomZones();
751
752         if (!partitionsEnabled) {
753             partitionCnt = 1;
754         }
755
756         // for (int i = 0; i < (26 + customCnt); i++) {
757         // String name;
758         // try {
759         // PowermaxZoneName zoneName = PowermaxZoneName.fromId(i);
760         // name = zoneName.getName();
761         // } catch (IllegalArgumentException e) {
762         // logger.debug("Zone id out of bounds {}", i);
763         // name = null;
764         // }
765         // str += String.format("\nZone name %d; %s", i + 1, name);
766         // }
767         for (int i = 0; i < phoneNumbers.length; i++) {
768             if (phoneNumbers[i] != null) {
769                 str += String.format("\nPhone number %d: %s", i + 1, phoneNumbers[i]);
770             }
771         }
772         str += String.format("\nBell time: %d minutes", bellTime);
773         str += String.format("\nSilent panic: %s", silentPanic ? "enabled" : "disabled");
774         str += String.format("\nQuick arm: %s", quickArm ? "enabled" : "disabled");
775         str += String.format("\nZone bypass: %s", bypassEnabled ? "enabled" : "disabled");
776         str += String.format("\nEPROM: %s", (panelEprom != null) ? panelEprom : "Undefined");
777         str += String.format("\nSW: %s", (panelSoftware != null) ? panelSoftware : "Undefined");
778         str += String.format("\nSerial: %s", (panelSerial != null) ? panelSerial : "Undefined");
779         str += String.format("\nUse partitions: %s", partitionsEnabled ? "enabled" : "disabled");
780         str += String.format("\nNumber of partitions: %d", partitionCnt);
781         for (int i = 0; i < zoneCnt; i++) {
782             if (zoneSettings[i] != null) {
783                 String partStr = "";
784                 for (int j = 1; j <= partitionCnt; j++) {
785                     if (zoneSettings[i].isInPartition(j)) {
786                         partStr += j + " ";
787                     }
788                 }
789                 str += String.format("\nZone %d %s: %s (chime = %s; sensor type = %s; partitions = %s)", i + 1,
790                         zoneSettings[i].getName(), zoneSettings[i].getType(), zoneSettings[i].getChime(),
791                         zoneSettings[i].getSensorType(), partStr);
792             }
793         }
794         for (int i = 0; i < NB_PGM_X10_DEVICES; i++) {
795             if (x10Settings[i] != null && x10Settings[i].isEnabled()) {
796                 str += String.format("\n%s: %s enabled", (i == 0) ? "PGM" : ("X10 " + i),
797                         (x10Settings[i].getName() != null) ? x10Settings[i].getName() : "");
798             }
799         }
800         for (int i = 1; i <= sirenCnt; i++) {
801             if (isSirenEnrolled(i)) {
802                 str += String.format("\nSiren %d enrolled", i);
803             }
804         }
805         for (int i = 1; i <= keypad1wCnt; i++) {
806             if (isKeypad1wEnrolled(i)) {
807                 str += String.format("\nKeypad 1w %d enrolled", i);
808             }
809         }
810         for (int i = 1; i <= keypad2wCnt; i++) {
811             if (isKeypad2wEnrolled(i)) {
812                 str += String.format("\nKeypad 2w %d enrolled", i);
813             }
814         }
815         return "Powermax alarm binding:" + str;
816     }
817 }