]> git.basschouten.com Git - openhab-addons.git/blob
db93d232d3ff2ac9ce0dd9dc683ced440493107f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.dscalarm.internal.handler;
14
15 import static org.openhab.binding.dscalarm.internal.DSCAlarmBindingConstants.*;
16
17 import java.math.BigDecimal;
18 import java.text.SimpleDateFormat;
19 import java.util.Date;
20 import java.util.List;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23
24 import org.openhab.binding.dscalarm.internal.DSCAlarmCode;
25 import org.openhab.binding.dscalarm.internal.DSCAlarmEvent;
26 import org.openhab.binding.dscalarm.internal.DSCAlarmMessage;
27 import org.openhab.binding.dscalarm.internal.DSCAlarmMessage.DSCAlarmMessageInfoType;
28 import org.openhab.binding.dscalarm.internal.DSCAlarmMessage.DSCAlarmMessageType;
29 import org.openhab.binding.dscalarm.internal.config.DSCAlarmPartitionConfiguration;
30 import org.openhab.binding.dscalarm.internal.config.DSCAlarmZoneConfiguration;
31 import org.openhab.binding.dscalarm.internal.discovery.DSCAlarmDiscoveryService;
32 import org.openhab.core.config.core.Configuration;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.thing.Bridge;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.thing.ThingStatus;
39 import org.openhab.core.thing.binding.BaseBridgeHandler;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * Abstract class for a DSC Alarm Bridge Handler.
47  *
48  * @author Russell Stephens - Initial Contribution
49  */
50 public abstract class DSCAlarmBaseBridgeHandler extends BaseBridgeHandler {
51
52     private final Logger logger = LoggerFactory.getLogger(DSCAlarmBaseBridgeHandler.class);
53
54     /** The DSC Alarm bridge type. */
55     private DSCAlarmBridgeType dscAlarmBridgeType = null;
56
57     /** The DSC Alarm bridge type. */
58     private DSCAlarmProtocol dscAlarmProtocol = null;
59
60     /** The DSC Alarm Discovery Service. */
61     private DSCAlarmDiscoveryService dscAlarmDiscoveryService = null;
62
63     /** The Panel Thing handler for the bridge. */
64     private DSCAlarmBaseThingHandler panelThingHandler = null;
65
66     /** Connection status for the bridge. */
67     private boolean connected = false;
68
69     /** Determines if things have changed. */
70     private boolean thingsHaveChanged = false;
71
72     /** Determines if all things have been initialized. */
73     private boolean allThingsInitialized = false;
74
75     /** Thing count. */
76     private int thingCount = 0;
77
78     /** Password for bridge connection authentication. */
79     private String password = null;
80
81     /** User Code for some DSC Alarm commands. */
82     private String userCode = null;
83
84     // Polling variables
85     protected int pollPeriod = 0;
86     private long pollElapsedTime = 0;
87     private long pollStartTime = 0;
88     private long refreshInterval = 5000;
89
90     private ScheduledFuture<?> pollingTask;
91
92     /**
93      * Constructor.
94      *
95      * @param bridge
96      * @param dscAlarmBridgeType
97      */
98     DSCAlarmBaseBridgeHandler(Bridge bridge, DSCAlarmBridgeType dscAlarmBridgeType, DSCAlarmProtocol dscAlarmProtocol) {
99         super(bridge);
100         this.dscAlarmBridgeType = dscAlarmBridgeType;
101         this.dscAlarmProtocol = dscAlarmProtocol;
102     }
103
104     /**
105      * Returns the bridge type.
106      */
107     public DSCAlarmBridgeType getBridgeType() {
108         return dscAlarmBridgeType;
109     }
110
111     /**
112      * Sets the protocol.
113      *
114      * @param dscAlarmProtocol
115      */
116     public void setProtocol(DSCAlarmProtocol dscAlarmProtocol) {
117         this.dscAlarmProtocol = dscAlarmProtocol;
118     }
119
120     /**
121      * Returns the protocol.
122      */
123     public DSCAlarmProtocol getProtocol() {
124         return dscAlarmProtocol;
125     }
126
127     /**
128      * Sets the bridge type.
129      *
130      * @param dscAlarmBridgeType
131      */
132     public void setBridgeType(DSCAlarmBridgeType dscAlarmBridgeType) {
133         this.dscAlarmBridgeType = dscAlarmBridgeType;
134     }
135
136     @Override
137     public void initialize() {
138         if (this.pollPeriod > 15) {
139             this.pollPeriod = 15;
140         } else if (this.pollPeriod < 1) {
141             this.pollPeriod = 1;
142         }
143         updateStatus(ThingStatus.OFFLINE);
144         startPolling();
145     }
146
147     @Override
148     public void dispose() {
149         stopPolling();
150         closeConnection();
151         super.dispose();
152     }
153
154     /**
155      * Register the Discovery Service.
156      *
157      * @param discoveryService
158      */
159     public void registerDiscoveryService(DSCAlarmDiscoveryService discoveryService) {
160         if (discoveryService == null) {
161             throw new IllegalArgumentException("registerDiscoveryService(): Illegal Argument. Not allowed to be Null!");
162         } else {
163             this.dscAlarmDiscoveryService = discoveryService;
164             logger.trace("registerDiscoveryService(): Discovery Service Registered!");
165         }
166     }
167
168     /**
169      * Unregister the Discovery Service.
170      */
171     public void unregisterDiscoveryService() {
172         dscAlarmDiscoveryService = null;
173         logger.trace("unregisterDiscoveryService(): Discovery Service Unregistered!");
174     }
175
176     /**
177      * Connect The Bridge.
178      */
179     private void connect() {
180         openConnection();
181
182         if (isConnected()) {
183             if (dscAlarmBridgeType != DSCAlarmBridgeType.Envisalink) {
184                 onConnected();
185             }
186         }
187     }
188
189     /**
190      * Runs when connected.
191      */
192     public void onConnected() {
193         logger.debug("onConnected(): Bridge Connected!");
194
195         setBridgeStatus(true);
196
197         thingsHaveChanged = true;
198     }
199
200     /**
201      * Disconnect The Bridge.
202      */
203     private void disconnect() {
204         closeConnection();
205
206         if (!isConnected()) {
207             setBridgeStatus(false);
208         }
209     }
210
211     /**
212      * Returns Connected.
213      */
214     public boolean isConnected() {
215         return this.connected;
216     }
217
218     /**
219      * Sets Connected.
220      */
221     public void setConnected(boolean connected) {
222         this.connected = connected;
223     }
224
225     /**
226      * Set Bridge Status.
227      *
228      * @param isOnline
229      */
230     public void setBridgeStatus(boolean isOnline) {
231         logger.debug("setBridgeStatus(): Setting Bridge to {}", isOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
232
233         updateStatus(isOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
234
235         ChannelUID channelUID = new ChannelUID(getThing().getUID(), BRIDGE_RESET);
236         updateState(channelUID, isOnline ? OnOffType.ON : OnOffType.OFF);
237     }
238
239     /**
240      * Method for opening a connection to DSC Alarm.
241      */
242     abstract void openConnection();
243
244     /**
245      * Method for closing a connection to DSC Alarm.
246      */
247     abstract void closeConnection();
248
249     /**
250      * Method for writing to an open DSC Alarm connection.
251      *
252      * @param writeString
253      * @param doNotLog
254      */
255     public abstract void write(String writeString, boolean doNotLog);
256
257     /**
258      * Method for reading from an open DSC Alarm connection.
259      */
260     public abstract String read();
261
262     /**
263      * Get Bridge Password.
264      */
265     public String getPassword() {
266         return this.password;
267     }
268
269     /**
270      * Set Bridge Password.
271      *
272      * @param password
273      */
274     public void setPassword(String password) {
275         this.password = password;
276     }
277
278     /**
279      * Get Panel User Code.
280      */
281     public String getUserCode() {
282         return this.userCode;
283     }
284
285     /**
286      * Set Panel User Code.
287      *
288      * @param userCode
289      */
290     public void setUserCode(String userCode) {
291         this.userCode = userCode;
292     }
293
294     /**
295      * Method to start the polling task.
296      */
297     private void startPolling() {
298         logger.debug("Starting DSC Alarm Polling Task.");
299         if (pollingTask == null || pollingTask.isCancelled()) {
300             pollingTask = scheduler.scheduleWithFixedDelay(this::polling, 0, refreshInterval, TimeUnit.MILLISECONDS);
301         }
302     }
303
304     /**
305      * Method to stop the polling task.
306      */
307     private void stopPolling() {
308         logger.debug("Stopping DSC Alarm Polling Task.");
309         if (pollingTask != null && !pollingTask.isCancelled()) {
310             pollingTask.cancel(true);
311             pollingTask = null;
312         }
313     }
314
315     /**
316      * Method for polling the DSC Alarm System.
317      */
318     public synchronized void polling() {
319         logger.debug("DSC Alarm Polling Task - '{}' is {}", getThing().getUID(), getThing().getStatus());
320
321         if (isConnected()) {
322             if (pollStartTime == 0) {
323                 pollStartTime = System.currentTimeMillis();
324             }
325
326             pollElapsedTime = ((System.currentTimeMillis() - pollStartTime) / 1000) / 60;
327
328             // Send Poll command to the DSC Alarm if idle for 'pollPeriod'
329             // minutes
330             if (pollElapsedTime >= pollPeriod) {
331                 sendCommand(DSCAlarmCode.Poll);
332                 pollStartTime = 0;
333             }
334
335             checkThings();
336
337             if (thingsHaveChanged && allThingsInitialized) {
338                 this.setBridgeStatus(isConnected());
339                 thingsHaveChanged = false;
340                 // Get a status report from DSC Alarm.
341                 sendCommand(DSCAlarmCode.StatusReport);
342             }
343         } else {
344             logger.error("Not Connected to the DSC Alarm!");
345             connect();
346         }
347     }
348
349     /**
350      * Check if things have changed.
351      */
352     public void checkThings() {
353         logger.debug("Checking Things!");
354
355         allThingsInitialized = true;
356
357         List<Thing> things = getThing().getThings();
358
359         if (things.size() != thingCount) {
360             thingsHaveChanged = true;
361             thingCount = things.size();
362         }
363
364         for (Thing thing : things) {
365
366             DSCAlarmBaseThingHandler handler = (DSCAlarmBaseThingHandler) thing.getHandler();
367
368             if (handler != null) {
369                 logger.debug("***Checking '{}' - Status: {}, Initialized: {}", thing.getUID(), thing.getStatus(),
370                         handler.isThingHandlerInitialized());
371
372                 if (!handler.isThingHandlerInitialized() || thing.getStatus() != ThingStatus.ONLINE) {
373                     allThingsInitialized = false;
374                 }
375
376                 if (handler.getDSCAlarmThingType().equals(DSCAlarmThingType.PANEL)) {
377                     if (panelThingHandler == null) {
378                         panelThingHandler = handler;
379                     }
380                 }
381
382             } else {
383                 logger.error("checkThings(): Thing handler not found!");
384             }
385         }
386     }
387
388     /**
389      * Find a Thing.
390      *
391      * @param dscAlarmThingType
392      * @param partitionId
393      * @param zoneId
394      * @return thing
395      */
396     public Thing findThing(DSCAlarmThingType dscAlarmThingType, int partitionId, int zoneId) {
397         List<Thing> things = getThing().getThings();
398
399         Thing thing = null;
400
401         for (Thing t : things) {
402             try {
403                 Configuration config = t.getConfiguration();
404                 DSCAlarmBaseThingHandler handler = (DSCAlarmBaseThingHandler) t.getHandler();
405
406                 if (handler != null) {
407                     DSCAlarmThingType handlerDSCAlarmThingType = handler.getDSCAlarmThingType();
408
409                     if (handlerDSCAlarmThingType != null) {
410                         if (handlerDSCAlarmThingType.equals(dscAlarmThingType)) {
411                             switch (handlerDSCAlarmThingType) {
412                                 case PANEL:
413                                 case KEYPAD:
414                                     thing = t;
415                                     logger.debug("findThing(): Thing Found - {}, {}, {}", t, handler,
416                                             handlerDSCAlarmThingType);
417                                     return thing;
418                                 case PARTITION:
419                                     BigDecimal partitionNumber = (BigDecimal) config
420                                             .get(DSCAlarmPartitionConfiguration.PARTITION_NUMBER);
421                                     if (partitionId == partitionNumber.intValue()) {
422                                         thing = t;
423                                         logger.debug("findThing(): Thing Found - {}, {}, {}", t, handler,
424                                                 handlerDSCAlarmThingType);
425                                         return thing;
426                                     }
427                                     break;
428                                 case ZONE:
429                                     BigDecimal zoneNumber = (BigDecimal) config
430                                             .get(DSCAlarmZoneConfiguration.ZONE_NUMBER);
431                                     if (zoneId == zoneNumber.intValue()) {
432                                         thing = t;
433                                         logger.debug("findThing(): Thing Found - {}, {}, {}", t, handler,
434                                                 handlerDSCAlarmThingType);
435                                         return thing;
436                                     }
437                                     break;
438                                 default:
439                                     break;
440                             }
441                         }
442                     }
443                 }
444             } catch (Exception e) {
445                 logger.debug("findThing(): Error Seaching Thing - {} ", e.getMessage(), e);
446             }
447         }
448
449         return thing;
450     }
451
452     /**
453      * Handles an incoming message from the DSC Alarm System.
454      *
455      * @param incomingMessage
456      */
457     public synchronized void handleIncomingMessage(String incomingMessage) {
458         if (incomingMessage != null && !incomingMessage.isEmpty()) {
459             DSCAlarmMessage dscAlarmMessage = new DSCAlarmMessage(incomingMessage);
460             DSCAlarmMessageType dscAlarmMessageType = dscAlarmMessage.getDSCAlarmMessageType();
461
462             logger.debug("handleIncomingMessage(): Message received: {} - {}", incomingMessage,
463                     dscAlarmMessage.toString());
464
465             DSCAlarmEvent event = new DSCAlarmEvent(this);
466             event.dscAlarmEventMessage(dscAlarmMessage);
467             DSCAlarmThingType dscAlarmThingType = null;
468             int partitionId = 0;
469             int zoneId = 0;
470
471             DSCAlarmCode dscAlarmCode = DSCAlarmCode
472                     .getDSCAlarmCodeValue(dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.CODE));
473
474             if (panelThingHandler != null) {
475                 panelThingHandler.setPanelMessage(dscAlarmMessage);
476             }
477
478             if (dscAlarmCode == DSCAlarmCode.LoginResponse) {
479                 String dscAlarmMessageData = dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA);
480                 if ("3".equals(dscAlarmMessageData)) {
481                     sendCommand(DSCAlarmCode.NetworkLogin);
482                     // onConnected();
483                 } else if ("1".equals(dscAlarmMessageData)) {
484                     onConnected();
485                 }
486                 return;
487             } else if (dscAlarmCode == DSCAlarmCode.CommandAcknowledge) {
488                 String dscAlarmMessageData = dscAlarmMessage.getMessageInfo(DSCAlarmMessageInfoType.DATA);
489                 if ("000".equals(dscAlarmMessageData)) {
490                     setBridgeStatus(true);
491                 }
492             }
493
494             switch (dscAlarmMessageType) {
495                 case PANEL_EVENT:
496                     dscAlarmThingType = DSCAlarmThingType.PANEL;
497                     break;
498                 case PARTITION_EVENT:
499                     dscAlarmThingType = DSCAlarmThingType.PARTITION;
500                     partitionId = Integer
501                             .parseInt(event.getDSCAlarmMessage().getMessageInfo(DSCAlarmMessageInfoType.PARTITION));
502                     break;
503                 case ZONE_EVENT:
504                     dscAlarmThingType = DSCAlarmThingType.ZONE;
505                     zoneId = Integer.parseInt(event.getDSCAlarmMessage().getMessageInfo(DSCAlarmMessageInfoType.ZONE));
506                     break;
507                 case KEYPAD_EVENT:
508                     dscAlarmThingType = DSCAlarmThingType.KEYPAD;
509                     break;
510                 default:
511                     break;
512             }
513
514             if (dscAlarmThingType != null) {
515                 Thing thing = findThing(dscAlarmThingType, partitionId, zoneId);
516
517                 logger.debug("handleIncomingMessage(): Thing Search - '{}'", thing);
518
519                 if (thing != null) {
520                     DSCAlarmBaseThingHandler thingHandler = (DSCAlarmBaseThingHandler) thing.getHandler();
521
522                     if (thingHandler != null) {
523                         if (thingHandler.isThingHandlerInitialized() && thing.getStatus() == ThingStatus.ONLINE) {
524                             thingHandler.dscAlarmEventReceived(event, thing);
525
526                         } else {
527                             logger.debug("handleIncomingMessage(): Thing '{}' Not Refreshed!", thing.getUID());
528                         }
529                     }
530                 } else {
531                     logger.debug("handleIncomingMessage(): Thing Not Found! Send to Discovery Service!");
532
533                     if (dscAlarmDiscoveryService != null) {
534                         dscAlarmDiscoveryService.addThing(getThing(), dscAlarmThingType, event);
535                     }
536                 }
537             }
538         } else {
539             logger.debug("handleIncomingMessage(): No Message Received!");
540         }
541     }
542
543     @Override
544     public void handleCommand(ChannelUID channelUID, Command command) {
545         logger.debug("handleCommand(): Command Received - {} {}.", channelUID, command);
546
547         if (command instanceof RefreshType) {
548             return;
549         }
550
551         if (isConnected()) {
552             switch (channelUID.getId()) {
553                 case BRIDGE_RESET:
554                     if (command == OnOffType.OFF) {
555                         disconnect();
556                     }
557                     break;
558                 case SEND_COMMAND:
559                     if (!command.toString().isEmpty()) {
560                         String[] tokens = command.toString().split(",");
561
562                         String cmd = tokens[0];
563                         String data = "";
564                         if (tokens.length > 1) {
565                             data = tokens[1];
566                         }
567
568                         sendDSCAlarmCommand(cmd, data);
569
570                         updateState(channelUID, new StringType(""));
571                     }
572                     break;
573                 default:
574                     break;
575             }
576         }
577     }
578
579     /**
580      * Method to send a sequence of key presses one at a time using the '070' command.
581      *
582      * @param keySequence
583      */
584     private boolean sendKeySequence(String keySequence) {
585         logger.debug("sendKeySequence(): Sending key sequence '{}'.", keySequence);
586
587         boolean sent = false;
588
589         for (char key : keySequence.toCharArray()) {
590             sent = sendCommand(DSCAlarmCode.KeyStroke, String.valueOf(key));
591
592             if (!sent) {
593                 return sent;
594             }
595         }
596
597         return sent;
598     }
599
600     /**
601      * Sends a DSC Alarm command
602      *
603      * @param command
604      * @param data
605      */
606     public boolean sendDSCAlarmCommand(String command, String data) {
607         logger.debug("sendDSCAlarmCommand(): Attempting to send DSC Alarm Command: command - {} - data: {}", command,
608                 data);
609
610         DSCAlarmCode dscAlarmCode = DSCAlarmCode.getDSCAlarmCodeValue(command);
611
612         if (dscAlarmProtocol.equals(DSCAlarmProtocol.IT100_API) && dscAlarmCode.equals(DSCAlarmCode.KeySequence)) {
613             return sendKeySequence(data);
614         } else {
615             return sendCommand(dscAlarmCode, data);
616         }
617     }
618
619     /**
620      * Send an API command to the DSC Alarm system.
621      *
622      * @param dscAlarmCode
623      * @param dscAlarmData
624      * @return successful
625      */
626     public boolean sendCommand(DSCAlarmCode dscAlarmCode, String... dscAlarmData) {
627         boolean successful = false;
628         boolean validCommand = false;
629
630         String command = dscAlarmCode.getCode();
631         String data = "";
632         boolean confidentialData = false;
633
634         switch (dscAlarmCode) {
635             case Poll: /* 000 */
636             case StatusReport: /* 001 */
637                 validCommand = true;
638                 break;
639             case LabelsRequest: /* 002 */
640                 if (!dscAlarmProtocol.equals(DSCAlarmProtocol.IT100_API)) {
641                     break;
642                 }
643                 validCommand = true;
644                 break;
645             case NetworkLogin: /* 005 */
646                 if (!dscAlarmProtocol.equals(DSCAlarmProtocol.ENVISALINK_TPI)) {
647                     break;
648                 }
649
650                 if (password == null) {
651                     logger.error("sendCommand(): No password!");
652                     break;
653                 }
654                 data = password;
655                 confidentialData = true;
656                 validCommand = true;
657                 break;
658             case DumpZoneTimers: /* 008 */
659                 if (!dscAlarmProtocol.equals(DSCAlarmProtocol.ENVISALINK_TPI)) {
660                     break;
661                 }
662                 validCommand = true;
663                 break;
664             case SetTimeDate: /* 010 */
665                 Date date = new Date();
666                 SimpleDateFormat dateTime = new SimpleDateFormat("HHmmMMddYY");
667                 data = dateTime.format(date);
668                 validCommand = true;
669                 break;
670             case CommandOutputControl: /* 020 */
671                 if (dscAlarmData[0] == null || !dscAlarmData[0].matches("[1-8]")) {
672                     logger.error(
673                             "sendCommand(): Partition number must be a single character string from 1 to 8, it was: {}",
674                             dscAlarmData[0]);
675                     break;
676                 }
677
678                 if (dscAlarmData[1] == null || !dscAlarmData[1].matches("[1-4]")) {
679                     logger.error(
680                             "sendCommand(): Output number must be a single character string from 1 to 4, it was: {}",
681                             dscAlarmData[1]);
682                     break;
683                 }
684
685                 data = dscAlarmData[0];
686                 validCommand = true;
687                 break;
688             case KeepAlive: /* 074 */
689                 if (!dscAlarmProtocol.equals(DSCAlarmProtocol.ENVISALINK_TPI)) {
690                     break;
691                 }
692             case PartitionArmControlAway: /* 030 */
693             case PartitionArmControlStay: /* 031 */
694             case PartitionArmControlZeroEntryDelay: /* 032 */
695                 if (dscAlarmData[0] == null || !dscAlarmData[0].matches("[1-8]")) {
696                     logger.error(
697                             "sendCommand(): Partition number must be a single character string from 1 to 8, it was: {}",
698                             dscAlarmData[0]);
699                     break;
700                 }
701                 data = dscAlarmData[0];
702                 validCommand = true;
703                 break;
704             case PartitionArmControlWithUserCode: /* 033 */
705             case PartitionDisarmControl: /* 040 */
706                 if (dscAlarmData[0] == null || !dscAlarmData[0].matches("[1-8]")) {
707                     logger.error(
708                             "sendCommand(): Partition number must be a single character string from 1 to 8, it was: {}",
709                             dscAlarmData[0]);
710                     break;
711                 }
712
713                 if (userCode == null || userCode.length() < 4 || userCode.length() > 6) {
714                     logger.error("sendCommand(): User Code is invalid, must be between 4 and 6 chars");
715                     break;
716                 }
717
718                 if (dscAlarmProtocol.equals(DSCAlarmProtocol.IT100_API)) {
719                     data = dscAlarmData[0] + String.format("%-6s", userCode).replace(' ', '0');
720                 } else {
721                     data = dscAlarmData[0] + userCode;
722                 }
723
724                 confidentialData = true;
725                 validCommand = true;
726                 break;
727             case VirtualKeypadControl: /* 058 */
728                 if (!dscAlarmProtocol.equals(DSCAlarmProtocol.IT100_API)) {
729                     break;
730                 }
731             case TimeStampControl: /* 055 */
732             case TimeDateBroadcastControl: /* 056 */
733             case TemperatureBroadcastControl: /* 057 */
734                 if (dscAlarmData[0] == null || !dscAlarmData[0].matches("[0-1]")) {
735                     logger.error("sendCommand(): Value must be a single character string of 0 or 1: {}",
736                             dscAlarmData[0]);
737                     break;
738                 }
739                 data = dscAlarmData[0];
740                 validCommand = true;
741                 break;
742             case TriggerPanicAlarm: /* 060 */
743                 if (dscAlarmData[0] == null || !dscAlarmData[0].matches("[1-3]")) {
744                     logger.error("sendCommand(): FAPcode must be a single character string from 1 to 3, it was: {}",
745                             dscAlarmData[1]);
746                     break;
747                 }
748                 data = dscAlarmData[0];
749                 validCommand = true;
750                 break;
751             case KeyStroke: /* 070 */
752                 if (dscAlarmProtocol.equals(DSCAlarmProtocol.ENVISALINK_TPI)) {
753                     if (dscAlarmData[0] == null || dscAlarmData[0].length() != 1
754                             || !dscAlarmData[0].matches("[0-9]|A|#|\\*")) {
755                         logger.error(
756                                 "sendCommand(): \'keystroke\' must be a single character string from 0 to 9, *, #, or A, it was: {}",
757                                 dscAlarmData[0]);
758                         break;
759                     }
760                 } else if (dscAlarmProtocol.equals(DSCAlarmProtocol.IT100_API)) {
761                     if (dscAlarmData[0] == null || dscAlarmData[0].length() != 1
762                             || !dscAlarmData[0].matches("[0-9]|\\*|#|F|A|P|[a-e]|<|>|=|\\^|L")) {
763                         logger.error(
764                                 "sendCommand(): \'keystroke\' must be a single character string from 0 to 9, *, #, F, A, P, a to e, <, >, =, or ^, it was: {}",
765                                 dscAlarmData[0]);
766                         break;
767                     } else if ("L".equals(dscAlarmData[0])) { /* Long Key Press */
768                         try {
769                             Thread.sleep(1500);
770                             data = "^";
771                             validCommand = true;
772                             break;
773                         } catch (InterruptedException e) {
774                             logger.error("sendCommand(): \'keystroke\': Error with Long Key Press!");
775                             break;
776                         }
777                     }
778                 } else {
779                     break;
780                 }
781
782                 data = dscAlarmData[0];
783                 validCommand = true;
784                 break;
785             case KeySequence: /* 071 */
786                 if (!dscAlarmProtocol.equals(DSCAlarmProtocol.ENVISALINK_TPI)) {
787                     break;
788                 }
789
790                 if (dscAlarmData[0] == null || dscAlarmData[0].length() > 6
791                         || !dscAlarmData[0].matches("(\\d|#|\\*)+")) {
792                     logger.error(
793                             "sendCommand(): \'keysequence\' must be a string of up to 6 characters consiting of 0 to 9, *, or #, it was: {}",
794                             dscAlarmData[0]);
795                     break;
796                 }
797                 data = dscAlarmData[0];
798                 validCommand = true;
799                 break;
800             case CodeSend: /* 200 */
801                 if (userCode == null || userCode.length() < 4 || userCode.length() > 6) {
802                     logger.error("sendCommand(): Access Code is invalid, must be between 4 and 6 chars");
803                     break;
804                 }
805
806                 data = userCode;
807                 confidentialData = true;
808                 validCommand = true;
809                 break;
810
811             default:
812                 validCommand = false;
813                 break;
814
815         }
816
817         if (validCommand) {
818             String cmd = dscAlarmCommand(command, data);
819             write(cmd, confidentialData);
820             successful = true;
821             logger.debug("sendCommand(): '{}' Command Sent - {}", dscAlarmCode, confidentialData ? "***" : cmd);
822         } else {
823             logger.error("sendCommand(): Command '{}' Not Sent - Invalid!", dscAlarmCode);
824         }
825
826         return successful;
827     }
828
829     private String dscAlarmCommand(String command, String data) {
830         int sum = 0;
831
832         String cmd = command + data;
833
834         for (int i = 0; i < cmd.length(); i++) {
835             char c = cmd.charAt(i);
836             sum += c;
837         }
838
839         sum &= 0xFF;
840
841         String strChecksum = Integer.toHexString(sum >> 4) + Integer.toHexString(sum & 0xF);
842
843         return cmd + strChecksum.toUpperCase() + "\r\n";
844     }
845 }