Add some useful #define's. Eliminate redundant buffer copies and initializations.
[hai:hai.git] / hai.c
1 /*******************************************************************************/
2 /* Copyright (C) 2004-2005  Chuck Cannon                                       */
3 /*                                                                             */
4 /* This program is free software; you can redistribute it and/or               */
5 /* modify it under the terms of the GNU General Public License                 */
6 /* as published by the Free Software Foundation; either version 2              */
7 /* of the License, or (at your option) any later version.                      */
8 /*                                                                             */
9 /* This program is distributed in the hope that it will be useful,             */
10 /* but WITHOUT ANY WARRANTY; without even the implied warranty of              */
11 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               */
12 /* GNU General Public License for more details.                                */
13 /*                                                                             */
14 /* You should have received a copy of the GNU General Public License           */
15 /* along with this program; if not, write to the Free Software                 */
16 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */
17 /*******************************************************************************/
18
19 /* Includes */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <openssl/aes.h>
26 #ifdef GNUREADLINE_SUPPORT
27 #include <readline/readline.h>
28 #include <readline/history.h>
29 #endif
30 #ifdef WIN32
31 #include <winsock2.h>
32 #endif
33 #include "hai.h"
34 #include "hai_net.h"
35 #include "hai_serial.h"
36 #include "omni_protocol.h"
37 #include "help.h"
38 #ifdef READLINE_SUPPORT
39 #include "readline.h"
40 #endif
41
42 /* Defines */
43
44 #define EINVCMD         __ELASTOMNI + 1
45 #define EBADCODE        __ELASTOMNI + 2
46 #define EBADFILE        __ELASTOMNI + 3
47
48 #define MAX_LINE        16384
49 #define MAX_ARG         64
50
51 /* Typedefs */
52 typedef char name_str[MAX_NAME_CHARS];
53
54 /* Function prototypes */
55 void load_parm_file(char **environ);
56 void dump_parm_file(void);
57 void load_name_cache(void);
58 void save_name_cache(void);
59 int parse_cmdline(int argc, char *argv[]);
60 int parse_arg(char *arg);
61 int init_connection(hai_comm_id *id, val32 code);
62 void term_connection(hai_comm_id *id);
63 int cmd_loop(hai_comm_id *id);
64 int do_command(hai_comm_id *id, int argc, char *argv[]);
65 int do_encrypt(hai_comm_id *id, int argc, char *argv[]);
66 int do_csv(hai_comm_id *id, int argc, char *argv[]);
67 void encrypt_bytes(unsigned char *key, char *buf_in, int len_in,
68     char *buf_out, int *len_out);
69 void decrypt_bytes(unsigned char *key, char *buf_in, int len_in, char *buf_out,
70     int *len_out);
71 int do_sys_info(hai_comm_id *id, int argc, char *argv[]);
72 int do_sys_stat(hai_comm_id *id, int argc, char *argv[]);
73 int do_zone_stat(hai_comm_id *id, int argc, char *argv[]);
74 int do_unit_stat(hai_comm_id *id, int argc, char *argv[]);
75 int do_sensor_stat(hai_comm_id *id, int argc, char *argv[]);
76 int do_thermo_stat(hai_comm_id *id, int argc, char *argv[]);
77 int do_mesg_stat(hai_comm_id *id, int argc, char *argv[]);
78 int do_usersetting_stat(hai_comm_id *id, int argc, char *argv[]);
79 int do_events(hai_comm_id *id, int argc, char *argv[]);
80 int do_unit_cmd(hai_comm_id *id, int argc, char *argv[]);
81 int do_all_cmd(hai_comm_id *id, int argc, char *argv[]);
82 int do_counter_cmd(hai_comm_id *id, int argc, char *argv[]);
83 int do_alc_cmd(hai_comm_id *id, int argc, char *argv[]);
84 int do_compose_cmd(hai_comm_id *id, int argc, char *argv[]);
85 int do_upb_cmd(hai_comm_id *id, int argc, char *argv[]);
86 int do_radiora_cmd(hai_comm_id *id, int argc, char *argv[]);
87 int do_scene_cmd(hai_comm_id *id, int argc, char *argv[]);
88 int do_arm_cmd(hai_comm_id *id, int argc, char *argv[]);
89 int do_button_cmd(hai_comm_id *id, int argc, char *argv[]);
90 int do_cost_cmd(hai_comm_id *id, int argc, char *argv[]);
91 int do_saver_cmd(hai_comm_id *id, int argc, char *argv[]);
92 int do_sensor_cmd(hai_comm_id *id, int argc, char *argv[]);
93 int do_thermo_cmd(hai_comm_id *id, int argc, char *argv[]);
94 int do_mesg_cmd(hai_comm_id *id, int argc, char *argv[]);
95 int do_audio_cmd(hai_comm_id *id, int argc, char *argv[]);
96 int do_usersetting_cmd(hai_comm_id *id, int argc, char *argv[]);
97 int do_getnames(hai_comm_id *id, int argc, char *argv[]);
98 int do_putnames(hai_comm_id *id, int argc, char *argv[]);
99 int do_names(hai_comm_id *id, int argc, char *argv[]);
100 int do_buttons(hai_comm_id *id, int argc, char *argv[]);
101 int do_log(hai_comm_id *id, int argc, char *argv[]);
102 int do_sec_code_valid(hai_comm_id *id, int argc, char *argv[]);
103 int do_set_time(hai_comm_id *id, int argc, char *argv[]);
104 int do_getsetup(hai_comm_id *id, int argc, char *argv[]);
105 int do_putsetup(hai_comm_id *id, int argc, char *argv[]);
106 int do_getprogram(hai_comm_id *id, int argc, char *argv[]);
107 int do_putprogram(hai_comm_id *id, int argc, char *argv[]);
108 int do_getvoices(hai_comm_id *id, int argc, char *argv[]);
109 int do_putvoices(hai_comm_id *id, int argc, char *argv[]);
110 int do_stat_summary(hai_comm_id *id, int argc, char *argv[]);
111 int do_temp_stat(hai_comm_id *id, int argc, char *argv[]);
112 int do_zone_ready(hai_comm_id *id, int argc, char *argv[]);
113 int do_emergency(hai_comm_id *id, int argc, char *argv[]);
114 int do_memo(hai_comm_id *id, int argc, char *argv[]);
115 void print_code(FILE *f, int code);
116 int is_named(int type, int name);
117 void print_name(FILE *f, int type, int name);
118 void print_name_fixed(FILE *f, int type, int name);
119 void print_name_special_zone(int name, char *printStr);
120
121 /* Global variables */
122
123 char ip[128];
124 char dev[128];
125 char haiconf[128];
126 char password[64];
127 char hainame[128];
128 int port = 0, baud = 9600, serial_mode = 0;
129 unsigned char private_key[HAI_KEY_LEN];
130 val32 code;
131 val8 code_index;
132 val8 omni_model;              
133 val8 omni_major_version;
134 val8 omni_minor_version;
135 val8 omni_temp_format;
136 val8 omni_time_format;
137 val8 omni_date_format;
138 /*
139   Each panel model has a different number of security zones, "max_zones".
140   There are a number of additional zones with status values that vary by model:
141   freeze alarm, fire emergency (for each area), police emergency (for each area),
142   auxiliary emergency (for each area), duress alarm (for each area),
143   battery low trouble, AC power failure trouble, phone line dead trouble,
144   digital communicator trouble, fire tamper trouble, fuse trouble.
145   */
146 int max_zones;
147 int max_zones_status;
148 int max_units;  
149 /*
150   There are a possible 255 buttons that can be programmed (Buttons 1-255),
151   "max_buttons" of which can be named, given a voice description, and executed
152   by the user through a console, telephone or PC Access Software.
153   Buttons max_buttons+1 to 255 cannot be named or given a voice description,
154   and can only be executed through a program. "max_buttons" is different for
155   each panel model and ranges from 16 to 128.
156   */             
157 int max_buttons;
158 int max_buttons_program;             
159 int max_auxs;                
160 int max_codes;               
161 int max_areas;               
162 int max_thermos;             
163 int max_mesgs;               
164 int max_names;               
165 int max_temps;              
166 int max_usersettings;
167 int max_readers;
168 int name_cache_valid;
169 int name_cache_save;
170 int csv;
171 int dump_config;
172
173 struct {
174     name_str zone_name_cache[MAX_ZONES];
175     name_str unit_name_cache[MAX_UNITS];
176     name_str button_name_cache[MAX_BUTTONS];
177     name_str code_name_cache[MAX_CODES];
178     name_str area_name_cache[MAX_AREAS];
179     name_str thermo_name_cache[MAX_THERMOS];
180     name_str mesg_name_cache[MAX_MESGS];
181     name_str user_name_cache[MAX_USERS];
182     name_str reader_name_cache[MAX_READERS];
183 } name_cache;
184 name_str *name_index[NUM_NAME_TYPES] =
185 {
186     name_cache.zone_name_cache,
187     name_cache.unit_name_cache,
188     name_cache.button_name_cache,
189     name_cache.code_name_cache,
190     name_cache.area_name_cache,
191     name_cache.thermo_name_cache,
192     name_cache.mesg_name_cache,
193     name_cache.user_name_cache,
194     name_cache.reader_name_cache
195 };
196
197 const char *dow_text[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
198 const char *month_text[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
199     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
200 const char *sec_text[] = {"off", "day", "night", "away", "vacation",
201     "day instant", "night delayed"};
202 const char *lumina_sec_text[] = {"off", "home", "sleep", "away", "vacation",
203     "party", "special"};
204 const char *zone_text[] = {"Secure", "Not ready", "Trouble"};
205 const char *latched_text[] = {"Latch Secure", "Latch Tripped", "Latch Reset"};
206 const char *arming_text[] = {"Disarmed", "Armed", "User bypass",
207     "System bypass"};
208 const char *name_text[] = {"Zone", "Unit", "Button", "Code", "Area",
209     "Thermo", "Mesg", "User", "Reader"};
210 const char *alarm_text[] = {"Burglary", "Fire", "Gas", "Auxiliary", "Freeze",
211     "Water", "Duress", "Temp"};
212 const char *ac_text[] = {"Off", "Heat", "Cool", "Auto", "Emergency Heat"};  
213 const char *upb_text[] = {"Off", "On", "Set"};  
214 const char *usersetting_text[] = {"Unused", "Number", "Duration", "Temperature", "Humidity", "Date", "Time", "Days of Week", "Level"};  
215
216 cmd_item cmd_list[] =
217 {
218     {"help", "display help info",
219     do_help, help_help},
220     {"h", "display help info",
221     do_help, help_help},
222     {"?", "display help info",
223     do_help, help_help},
224     {"exit", "quit program",
225     NULL, NULL},
226     {"x", "quit program",
227     NULL, NULL},
228     {"quit", "quit program",
229     NULL, NULL},
230     {"q", "quit program",
231     NULL, NULL},
232     {"encrypt", "display encrypted configuration",
233     do_encrypt, help_encrypt},
234     {"csv", "change display mode",
235     do_csv, help_csv},
236
237     /* Status */
238     {"info", "display system information",
239     do_sys_info, help_sys_info},
240     {"stat", "display system status",
241     do_sys_stat, help_sys_stat},
242     {"sum", "display status summary",
243     do_stat_summary, help_stat_summary},
244     {"zones", "display zone status",
245     do_zone_stat, help_zone_stat},
246     {"ready", "display zone ready status",
247     do_zone_ready, help_zone_ready},
248     {"units", "display unit status",
249     do_unit_stat, help_unit_stat},
250     {"sensors", "display temp sensor status",
251     do_sensor_stat, help_sensor_stat},
252     {"thermos", "display thermostat status",
253     do_thermo_stat, help_thermo_stat},
254     {"temps", "display temps from thermos & sensors",
255     do_temp_stat, help_temp_stat},
256     {"mesgs", "display message status",
257     do_mesg_stat, help_mesg_stat},
258     {"users", "display user setting status",
259     do_usersetting_stat, help_usersetting_stat},
260
261     /* System events */
262     {"events", "display system events",
263     do_events, help_events},
264
265     /* Commands */
266     {"unit", "send unit command",
267     do_unit_cmd, help_unit_cmd},
268     {"all", "send all command",
269     do_all_cmd, help_all_cmd},
270     {"counter", "send counter command",
271     do_counter_cmd, help_counter_cmd},
272     {"alc", "send alc command",
273     do_alc_cmd, help_alc_cmd},
274     {"compose", "send compose command",
275     do_compose_cmd, help_compose_cmd},
276     {"upb", "send upb command",
277     do_upb_cmd, help_upb_cmd},
278     {"radiora", "send RadioRA command",
279     do_radiora_cmd, help_radiora_cmd},
280     {"scene", "send scene command",
281     do_scene_cmd, help_scene_cmd},
282     {"arm", "send arm command",
283     do_arm_cmd, help_arm_cmd},
284     {"button", "execute macro button",
285     do_button_cmd, help_button_cmd},
286     {"cost", "set energy cost",
287     do_cost_cmd, help_cost_cmd},
288     {"saver", "send saver command",
289     do_saver_cmd, help_saver_cmd},
290     {"sensor", "set temp sensor setpoint",
291     do_sensor_cmd, help_sensor_cmd},
292     {"thermo", "send thermostat commands",
293     do_thermo_cmd, help_thermo_cmd},
294     {"mesg", "send message commands",
295     do_mesg_cmd, help_mesg_cmd},
296     {"audio", "send audio commands",
297     do_audio_cmd, help_audio_cmd},
298     {"user", "send user commands",
299     do_usersetting_cmd, help_user_cmd},
300
301     /* Names */
302     {"getnames", "display/download names",
303     do_getnames, help_getnames},
304     {"putnames", "upload names",
305     do_putnames, help_putnames},
306     {"names", "display names",
307     do_names, help_names},
308     {"buttons", "display button names",
309     do_buttons, help_buttons},
310
311     /* Event log */
312     {"log", "display event log",
313     do_log, help_log},
314
315     /* Validate security codes */
316     {"valid", "validate security code",
317     do_sec_code_valid, help_sec_code_valid},
318
319     /* Misc. commands */
320     {"emergency", "activate keypad emergency",
321     do_emergency, help_emergency},
322     {"memo", "play/record voice memo",
323     do_memo, help_memo},
324
325     /* Undocumented commands */
326     {"settime", "set time",
327     do_set_time, help_set_time},   
328     {"getsetup", "display/download setup",
329     do_getsetup, help_getsetup},
330     {"putsetup", "upload setup",
331     do_putsetup, help_putsetup},
332     {"getprogram", "display/download program",
333     do_getprogram, help_getprogram},
334     {"putprogram", "upload program",
335     do_putprogram, help_putprogram},
336     {"getvoices", "display/download voices",
337     do_getvoices, help_getvoices},
338     {"putvoices", "upload voices",
339     do_putvoices, help_putvoices},
340
341     /* End of commands */
342     {NULL, NULL, NULL, NULL}
343 };
344
345 int max_names_array[NUM_NAME_TYPES] = {MAX_ZONES, MAX_UNITS, MAX_BUTTONS,
346     MAX_CODES, MAX_AREAS, MAX_THERMOS, MAX_MESGS, MAX_USERS, MAX_READERS};
347
348 /********************************************************************************/
349
350 int main(int argc, char **argv, char **environ)
351 {
352     hai_comm_id id;
353     int err = 0, nonparm;
354
355     /* Init parms */
356     *ip = 0;
357     *dev = 0;
358     *private_key = 0;
359     *code = 0;
360     *password = 0;
361     *haiconf = 0;
362     *hainame = 0;
363     csv = 0;
364     dump_config = 0;
365     name_cache_valid = 0;
366
367     /* Parse command line */
368     if ((nonparm = parse_cmdline(argc, argv)) < 0)
369     {
370         if (nonparm != -999999) {
371         printf("error: Unknown parameter %s\n", argv[-nonparm]);
372         }
373         return -1;
374     }
375
376     /* Load parm file */
377     load_parm_file(environ);
378
379     /* Display parm file */
380     if (dump_config)
381         dump_parm_file();
382
383     /* Load name cache */
384     load_name_cache();   
385
386     /* Init connection */
387     if ((err = init_connection(&id, code)) != 0)
388     {
389         printf("error initializing connection\n");
390         if (err == EHAITIMEOUT || err == EINVAL)
391             goto err_exit;
392         else
393             goto clean_exit;
394     }
395
396     /* Process command(s) */
397     if (nonparm == argc)
398     {
399         /* Loop on commands */
400         if ((err = cmd_loop(&id)) != 0)
401             goto clean_exit;
402     }
403     else
404         /* Single command */
405         err = do_command(&id, argc - nonparm, argv + nonparm);
406
407     /* Term connection */
408 clean_exit:
409     term_connection(&id);
410
411 err_exit:
412     /* Print error */
413     if (err > __ELASTOMNI)
414     {
415         switch(err)
416         {
417             case EINVCMD :
418                 printf("error (%d) : Invalid command\n", err);
419                 break;
420             case EBADCODE :
421                 printf("error (%d) : Invalid code\n", err);
422                 break;
423             case EBADFILE :
424                 printf("error (%d) : Bad file format\n", err);
425                 break;
426         }
427     }
428     else if (err > __ELASTHAI)
429     {
430         printf("error (%d) : %s\n", err, omni_strerror(err));
431     }
432     else if (err > __ELASTERROR)
433     {
434         printf("error (%d) : %s\n", err, hai_net_strerror(err));
435     }
436     else if (err != 0)
437     {
438         printf("error (%d) : %s\n", err, strerror(err));
439     }
440
441     /* Save name cache */
442     save_name_cache();
443
444     return err;
445 }
446
447 /********************************************************************************/
448
449 void load_parm_file(char **environ)
450 {
451     char *tok, *parm, *value;
452     char buffer[MAX_LINE];
453     char line[MAX_LINE];
454     FILE *f;
455
456     /* Find parm file path in env */
457     if (*haiconf == 0)
458     {
459         while (*environ != NULL)
460         {
461                         if (strlen(*environ) < MAX_LINE)
462                         {
463             strcpy(buffer, *environ);
464             tok = buffer;
465             parm = strtok(tok, "=\n\r");
466             value = strtok(NULL, "\n\r");
467
468             if (strcmp_no_case(parm, "HAICONF") == 0)
469             {
470                 if (value != NULL)
471                     strcpy(haiconf, value);
472                 break;
473             }
474                         }
475
476             environ++;
477         }
478     }
479
480     /* Default path */ 
481     if (*haiconf == 0)
482 #ifdef WIN32
483         strcpy(haiconf, "c:\\windows\\hai.ini");
484 #else
485         strcpy(haiconf, "/etc/hai.conf");
486 #endif
487
488     /* Open file */
489     if ((f = fopen(haiconf, "r")) == NULL)
490     {
491         printf("bad config file name (%s)\n", haiconf);
492         return;
493     }
494
495     /* Process lines */
496     while (fgets(line, MAX_LINE, f) != NULL)
497     {
498         if (line[0] != '#')
499             parse_arg(line);
500     }
501
502     /* Close file */
503     fclose(f);
504 }
505
506 /********************************************************************************/
507
508 void dump_parm_file(void)
509 {
510     int i;
511
512     printf("Password: %s\n", password);
513     printf("Key: ");
514     for (i = 0; i < 15; i++)
515         printf("%02X-", private_key[i]);
516     printf("%02X\n", private_key[15]);
517     printf("IP Address: %s\n", ip);
518     printf("Port: %d\n", port);
519     printf("Code: %c%c%c%c\n", code[0] + '0', code[1] + '0', code[2] + '0',
520         code[3] + '0');
521     printf("Config File: %s\n", haiconf);
522     printf("Name Cache File: %s\n", hainame);
523     printf("Serial Mode: %d\n", serial_mode);
524     printf("Serial Device: %s\n", dev);
525     printf("Baud: %d\n", baud = 9600);
526     printf("CSV: %d\n", csv);
527 }
528
529 /********************************************************************************/
530
531 void load_name_cache()
532 {
533     char line[MAX_LINE];
534     char *name, *anum, *atype;
535     int type, num, index;
536     name_str *ncache;
537     FILE *f;
538
539     /* Zero name cache */
540     name_cache_save = 0;
541     for (index = 0; index < MAX_NAMES; index++)
542         *name_cache.zone_name_cache[index] = 0;
543
544     /* Check file name */
545     if (*hainame == 0)
546         return;
547
548     /* Open file */
549     if ((f = fopen(hainame, "r")) == NULL)
550         return;
551
552     /* Put names */
553     index = 0;
554     while (fgets(line, MAX_LINE, f) != NULL)
555     {
556         /* Parse input line */
557         atype = strtok(line, "\t \r\n");
558         anum = strtok(NULL, "\t \r\n");
559         strtok(NULL, "\t \r\n");
560         name = strtok(NULL, "\r\n");
561
562         /* Skip blank lines */
563         if (atype == NULL)
564             continue;
565
566         /* Check line parse */
567         if ((anum == NULL) || (name == NULL))
568             break;
569
570         /* Lookup type */
571         num = atoi(anum);
572         for (type = 1; type <= NUM_NAME_TYPES ; type++)
573         {
574             if (strcmp_no_case(atype, name_text[type - 1]) == 0)
575                 break;
576         }
577         if (type > NUM_NAME_TYPES)
578             break;
579         ncache = name_index[type - 1];
580
581         /* Fill in cache */
582         strncpy(ncache[num - 1], name, MAX_NAME_CHARS);
583     }
584     name_cache_valid = 1;
585
586     /* Close file */
587     fclose(f);
588 }
589
590 /********************************************************************************/
591
592 void save_name_cache()
593 {
594     int type, num, index;
595     FILE *f;
596
597     /* Check file name and save flag */
598     if ((*hainame == 0) || (name_cache_save == 0))
599         return;
600
601     /* Open file */
602     if ((f = fopen(hainame, "w")) == NULL)
603         return;
604
605     /* Save names */
606     type = 1;
607     num = 1;
608     for (index = 0; index < MAX_NAMES; index++)
609     {
610         if ((type < NUM_NAME_TYPES) && (name_cache.zone_name_cache[index] 
611             == (void*) name_index[type]))
612         {
613             type++;
614             num = 1;
615         }
616         
617         /* Save name */
618         if (*name_cache.zone_name_cache[index] != 0)
619         {
620             /* Print name info */
621             fprintf(f, "%-6s", name_text[type - 1]);
622             fprintf(f, " %3d : %s\n", num, name_cache.zone_name_cache[index]);
623         }
624
625         /* Increment number */
626         num++;
627     }
628  
629     /* Close file */
630     fclose(f);  
631 }
632
633 /********************************************************************************/
634
635 int parse_cmdline(int argc, char *argv[])
636 {
637     int i, stat;
638
639     for (i = 1; i < argc; i++)
640     {
641         if (argv[i][0] == '-')
642         {
643             if ((stat = parse_arg(argv[i] + 1)) != 0)
644             {
645                  if (stat == -999999) return stat;
646                 return -i;
647         }
648         }
649         else
650             break;
651     }
652
653     return i;
654 }
655
656 /********************************************************************************/
657
658 int parse_arg(char *arg)
659 {
660     char *tok, *parm, *value;
661     char buffer[MAX_LINE];
662     unsigned char key[HAI_KEY_LEN];
663     int len;
664
665     /* Check arg */
666     if (arg == NULL)
667         return 0;
668     if (strlen(arg) == 0)
669         return 0;
670     if ((*arg == '\n') || (*arg == '\r'))
671         return 0;
672
673     /* Parse argument */
674     strcpy(buffer, arg);
675     tok = buffer;
676     parm = strtok(tok, "=\n\r");
677
678     /* First check for flags that don't have values (-h and -r) */
679
680     /* Test for help */
681     if ((strcmp_no_case("help", parm) == 0) || (strcmp("h", parm) == 0)
682         || (strcmp_no_case("?", parm) == 0))
683     {
684         printf("\nHAI command Program (ver 0.7)\n\n");
685         printf("Format:   hai [opts] [command]\n\n");
686         printf("Options:   -w=password\n");
687         printf("           -f=config file\n");       
688         printf("           -i=HAI ip address\n");
689         printf("          -ix=encrypted HAI ip address\n");       
690         printf("           -p=HAI port\n");
691         printf("          -px=encrypted HAI port\n");
692         printf("           -k=key\n");
693         printf("          -kx=encrypted key\n");
694         printf("           -c=user code\n");
695         printf("          -cx=encrypted user code\n");
696         printf("           -n=name cache file\n");       
697         printf("           -s=serial path\n");
698         printf("           -b=baud rate\n");
699         printf("           -r display comma separated values (CSV) display\n");       
700         printf("           -d dump config file\n");      
701         printf("           -h help\n\n");
702         printf("Commands: 'help' to see info on commands\n\n");
703         printf("Example:  hai -i=192.168.0.101 -p=4369 info\n\n");
704
705         exit(0);
706     }
707
708     /* Test for csv mode */
709     else if ((strcmp_no_case("csv", parm) == 0) || (strcmp("r", parm) == 0))
710     {
711         csv = 1;
712         return 0;
713     }
714
715     /* Test for dump mode */
716     else if ((strcmp_no_case("dump", parm) == 0) || (strcmp("d", parm) == 0))
717     {
718         dump_config = 1;
719         return 0;
720     }
721
722     /* Now check for options that must have "=" with no spaces */
723     
724     value = strtok(NULL, "\n\r");
725
726     if (value == NULL)
727     {
728         printf("error: parameter -%s not followed by \"=\"\n", buffer);
729         return -999999;
730     }
731     
732     /* Test for password */
733     if ((strcmp_no_case("password", parm) == 0) || (strcmp("w", parm) == 0))
734     {
735         if (value != NULL)
736             strcpy(password, value);
737     }
738
739     /* Test for config file */
740     else if ((strcmp_no_case("file", parm) == 0) || (strcmp("f", parm) == 0))
741     {
742         if (value != NULL)
743             strcpy(haiconf, value);
744     }
745
746     /* Test for ip */
747     else if ((strcmp_no_case("ip", parm) == 0) || (strcmp("i", parm) == 0))
748     {
749         if (value != NULL)
750             strcpy(ip, value);
751     }
752
753     /* Test for encrypted ip */
754     else if ((strcmp_no_case("ipx", parm) == 0) || (strcmp("ix", parm) == 0))
755     {
756         /* Get password */
757         if (*password == 0)
758         {
759             char *p;
760             
761             printf("Password: ");
762             if (fgets(password, 64, stdin) != NULL)
763             {
764             if ((p = strchr(password, '\r')) != 0)
765                 *p = 0;
766             if ((p = strchr(password, '\n')) != 0)
767                 *p = 0;            
768         }
769         }
770
771         /* Prepare key */
772         memset(key, 0, sizeof(key));
773         len = strlen(password);
774         if (len > HAI_KEY_LEN)
775             len = HAI_KEY_LEN;
776         memcpy(key, password, len);
777
778         if (value != NULL)
779         {
780             int j;
781             char *ptr = value;
782             char sec_buffer[128];
783
784             for (j = 0; j < 128; j++)
785             {
786                 sec_buffer[j] = (char) strtol(ptr, &ptr, 16);
787                 if (*ptr == ':')
788                     ptr++;
789                 else
790                     break;
791             }
792
793             len = 128;
794             decrypt_bytes(key, sec_buffer, j + 1, ip, &len);
795         }          
796     }
797
798     /* Test for port */
799     else if ((strcmp_no_case("port", parm) == 0) || (strcmp("p", parm) == 0))
800     {
801         if (value != NULL)
802             port = atoi(value);
803     }
804
805     /* Test for encrypted port */
806     else if ((strcmp_no_case("portx", parm) == 0) || (strcmp("px", parm) == 0))
807     {
808          /* Get password */
809         if (*password == 0)
810         {
811             char *p;
812             
813             printf("Password: ");
814             if (fgets(password, 64, stdin) != NULL)
815             {
816             if ((p = strchr(password, '\r')) != 0)
817                 *p = 0;
818             if ((p = strchr(password, '\n')) != 0)
819                 *p = 0;
820         }
821         }
822         
823         /* Prepare key */
824         len = strlen(password);
825         if (len > HAI_KEY_LEN)
826             len = HAI_KEY_LEN;
827         memcpy(key, password, len);
828
829         if (value != NULL)
830         {
831             int j, len;
832             char *ptr = value;
833             char buffer[128];
834             char sec_buffer[128];
835
836             for (j = 0; j < 128; j++)
837             {
838                 sec_buffer[j] = (char) strtol(ptr, &ptr, 16);
839                 if (*ptr == ':')
840                     ptr++;
841                 else
842                     break;
843             }
844
845             len = sizeof(buffer);
846             memset(buffer, 0, len);
847             decrypt_bytes(key, sec_buffer, j + 1, buffer, &len);
848             
849             port = atoi(buffer);
850         }          
851     }
852
853     /* Test for key */
854     else if ((strcmp_no_case("key", parm) == 0) || (strcmp("k", parm) == 0))
855     {
856         if (value != NULL)
857         {
858             int j;
859             char *ptr = value;
860
861             for (j = 0; j < HAI_KEY_LEN; j++)
862             {
863                 private_key[j] = (char) strtol(ptr, &ptr, 16);
864                 if (*ptr == ':')
865                     ptr++;
866             }
867         }
868     }
869
870     /* Test for encrypted key */
871     else if ((strcmp_no_case("keyx", parm) == 0) || (strcmp("kx", parm) == 0))
872     {
873          /* Get password */
874         if (*password == 0)
875         {
876             char *p;
877             
878             printf("Password: ");
879             if (fgets(password, 64, stdin) != NULL)
880             {
881             if ((p = strchr(password, '\r')) != 0)
882                 *p = 0;
883             if ((p = strchr(password, '\n')) != 0)
884                 *p = 0;
885         }
886         }
887
888         /* Prepare key */
889         memset(key, 0, sizeof(key));
890         len = strlen(password);
891         if (len > HAI_KEY_LEN)
892             len = HAI_KEY_LEN;
893         memcpy(key, password, len);
894
895         if (value != NULL)
896         {
897             int j, len;
898             char *ptr = value;
899             char sec_buffer[128];
900
901             for (j = 0; j < 128; j++)
902             {
903                 sec_buffer[j] = (char) strtol(ptr, &ptr, 16);
904                 if (*ptr == ':')
905                     ptr++;
906                 else
907                     break;
908             }
909
910             len = sizeof(private_key);
911             memset(private_key, 0, len);
912             decrypt_bytes(key, sec_buffer, j + 1, (char *)private_key, &len);
913         }          
914     }
915
916     /* Test for code */
917     else if ((strcmp_no_case("code", parm) == 0) || (strcmp("c", parm) == 0))
918     {
919         if (value != NULL)
920         {
921             int j, ncode;
922
923             ncode = atoi(value);
924
925             for (j = 3; j >= 0; j--)
926             {
927                 code[j] = ncode % 10;
928                 ncode /= 10;
929             }
930         }
931     }
932
933     /* Test for encrypted code */
934     else if ((strcmp_no_case("codex", parm) == 0) || (strcmp("cx", parm) == 0))
935     {
936          /* Get password */
937         if (*password == 0)
938         {
939             char *p;
940             
941             printf("Password: ");
942             if (fgets(password, 64, stdin) != NULL)
943             {
944             if ((p = strchr(password, '\r')) != 0)
945                 *p = 0;
946             if ((p = strchr(password, '\n')) != 0)
947                 *p = 0;
948         }
949         }
950
951         /* Prepare key */
952         memset(key, 0, sizeof(key));
953         len = strlen(password);
954         if (len > HAI_KEY_LEN)
955             len = HAI_KEY_LEN;
956         memcpy(key, password, len);
957
958         if (value != NULL)
959         {
960             int j, len;
961             char *ptr = value;
962             char sec_buffer[128];
963
964             for (j = 0; j < 128; j++)
965             {
966                 sec_buffer[j] = (char) strtol(ptr, &ptr, 16);
967                 if (*ptr == ':')
968                     ptr++;
969                 else
970                     break;
971             }
972
973             len = sizeof(code);
974             memset(code, 0, len);
975             decrypt_bytes(key, sec_buffer, j + 1,  (char *)code, &len);
976         }          
977     }
978
979     /* Test for config file */
980     else if ((strcmp_no_case("name", parm) == 0) || (strcmp("n", parm) == 0))
981     {
982         if (value != NULL)
983             strcpy(hainame, value);
984     }
985
986     /* Test for serial */
987     else if ((strcmp_no_case("serial", parm) == 0) || (strcmp("s", parm) == 0))
988     {
989         if (value != NULL)
990         {
991             strcpy(dev, value);
992             serial_mode = 1;
993         }
994     }
995
996     /* Test for baudrate */
997     else if ((strcmp_no_case("baud", parm) == 0) || (strcmp("b", parm) == 0))
998     {
999         if (value != NULL)
1000             baud = atoi(value);
1001     }
1002
1003     /* Must be unknown */
1004     else
1005         return -1;
1006
1007     return 0;
1008 }
1009
1010 /********************************************************************************/
1011
1012 int init_connection(hai_comm_id *id, val32 code)
1013 {
1014     int err;
1015     sys_info si;
1016     sec_code_valid data;
1017
1018     /* Create connection */
1019     if (serial_mode)
1020     {
1021 #ifdef HAISERIAL_SUPPORT
1022         /* Create serial connection */
1023         if ((err = hai_serial_open(id, dev, baud)) != 0)
1024             return err;
1025 #endif
1026     }
1027     else
1028     {
1029 #ifdef WIN32
1030         WORD sockVersion;
1031         WSADATA wsaData;
1032         sockVersion = MAKEWORD(1, 1); 
1033         if ((err = WSAStartup(sockVersion, &wsaData)) != 0)
1034             return err;
1035 #endif
1036
1037         /* Create network connection */
1038         if ((err = hai_net_open(id, ip, port, private_key)) != 0)
1039         {
1040             printf("error opening hai network connection\n");
1041             return err;
1042     }
1043     }
1044
1045     /* Send login message */
1046     if ((err = omni_login(id, code)) != 0)
1047     {
1048         printf("error logging in\n");
1049         return err;
1050     }
1051
1052     /* Request system info */
1053     if ((err = omni_sys_info(id, &si)) != 0)
1054     {
1055         printf("error getting system info\n");
1056         return err;
1057     }
1058     omni_model = si.model;
1059     omni_major_version = si.major;
1060     omni_minor_version = si.minor;
1061
1062     switch(omni_model)
1063     {
1064         case OMNILT :
1065             max_zones = 25;
1066             max_zones_status = 36;
1067             max_units = 36;    
1068             max_buttons = 16;
1069             max_buttons_program = 255;
1070             max_auxs = 25;     
1071             max_codes = 8;    
1072             max_areas = 0;    
1073             max_thermos = 2;  
1074             max_mesgs = 16;    
1075             max_temps = 25;
1076             max_usersettings = 0;
1077             max_readers = 0;
1078             break;
1079         case OMNI2E :
1080             max_zones = 48;  
1081             max_zones_status = 63;  
1082             max_units = 128;    
1083             max_buttons = 64;  
1084             max_buttons_program = 255;
1085             max_auxs = 48;     
1086             max_codes = 16;    
1087             max_areas = 2;    
1088             max_thermos = 4;  
1089             max_mesgs = 64;    
1090             max_temps = 48; 
1091             max_usersettings = 10;
1092             max_readers = 4;   
1093             break;
1094         case OMNIPRO2:
1095             max_zones = 176;  
1096             max_zones_status = 215;  
1097             max_units = 511;    
1098             max_buttons = 128;  
1099             max_buttons_program = 255;
1100             max_auxs = 176;     
1101             max_codes = 99;    
1102             max_areas = 8;    
1103             max_thermos = 64;  
1104             max_mesgs = 128;    
1105             max_temps = 176; 
1106             max_usersettings = 25;
1107             max_readers = 16;   
1108             break;
1109         case LUMINA:
1110             max_zones = 48;   
1111             max_zones_status = 54; 
1112             max_units = 128;    
1113             max_buttons = 64;  
1114             max_buttons_program = 255;
1115             max_auxs = 48;     
1116             max_codes = 16;    
1117             max_areas = 1;    
1118             max_thermos = 4;  
1119             max_mesgs = 64;    
1120             max_temps = 48; 
1121             max_usersettings = 10;
1122             max_readers = 4;   
1123             break;
1124         case LUMINAPRO:
1125             max_zones = 176;  
1126             max_zones_status = 182;  
1127             max_units = 511;    
1128             max_buttons = 128; 
1129             max_buttons_program = 255; 
1130             max_auxs = 176;     
1131             max_codes = 99;    
1132             max_areas = 1;    
1133             max_thermos = 64;  
1134             max_mesgs = 128;    
1135             max_temps = 176;  
1136             max_usersettings = 25;
1137             max_readers = 16;  
1138             break;
1139         case OMNI :
1140             max_zones = 32;  
1141             max_zones_status = 45;  
1142             max_units = 64;    
1143             max_buttons = 32;  
1144             max_buttons_program = 255;
1145             max_auxs = 32;     
1146             max_codes = 16;    
1147             max_areas = 2;    
1148             max_thermos = 4;  
1149             max_mesgs = 0;    
1150             max_temps = 32; 
1151             max_usersettings = 0;
1152             max_readers = 0;   
1153             break;
1154         case OMNI2:
1155             max_zones = 48;   
1156             max_zones_status = 63; 
1157             max_units = 128;    
1158             max_buttons = 64; 
1159             max_buttons_program = 255; 
1160             max_auxs = 48;     
1161             max_codes = 16;    
1162             max_areas = 2;    
1163             max_thermos = 4;  
1164             max_mesgs = 64;    
1165             max_temps = 48; 
1166             max_usersettings = 10;
1167             max_readers = 4;   
1168             break;
1169         case OMNIPRO:
1170             max_zones = 96;   
1171             max_zones_status = 133; 
1172             max_units = 255;    
1173             max_buttons = 64; 
1174             max_buttons_program = 255; 
1175             max_auxs = 96;     
1176             max_codes = 99;    
1177             max_areas = 8;    
1178             max_thermos = 64;  
1179             max_mesgs = 128;    
1180             max_temps = 96; 
1181             max_usersettings = 0;
1182             max_readers = 0;   
1183             break;
1184     }
1185     max_names =  max_zones + max_units + max_buttons + max_codes + max_areas
1186         + max_thermos + max_mesgs;
1187
1188     /* Request code validation */
1189     if ((err = omni_sec_code_valid(id, 0, code, &data)) != 0)
1190     {
1191         printf("error validating code\n");
1192         return err;
1193     }
1194     if (data.authority == CODE_INVALID)
1195         return EBADCODE;
1196     else
1197         code_index = GET8(data.number);
1198
1199     return 0;
1200 }
1201
1202 /********************************************************************************/
1203
1204 void term_connection(hai_comm_id *id)
1205 {
1206     /* Send logout message */
1207     omni_logout(id);
1208
1209     /* Close connection */
1210     if (serial_mode)
1211     {
1212 #ifdef HAISERIAL_SUPPORT
1213         /* Close serial connection */
1214         hai_serial_close(id);
1215 #endif
1216     }
1217     else
1218     {
1219         /* Close network connection */
1220         hai_net_close(id);
1221     }
1222 }
1223
1224 /********************************************************************************/
1225
1226 int cmd_loop(hai_comm_id *id)
1227 {
1228 #if defined(GNUREADLINE_SUPPORT) || defined(READLINE_SUPPORT)        
1229     char *line;
1230 #else
1231     char line[MAX_LINE];
1232 #endif
1233     int argc, err;
1234     char *argv[MAX_ARG];
1235
1236     /* Perform interactive loop */
1237     while (1)
1238     {
1239         /* Get input */
1240 #if defined(GNUREADLINE_SUPPORT)        
1241         if ((line = readline("hai> ")) == NULL)
1242             break;        
1243         add_history(line);
1244 #elif defined(READLINE_SUPPORT)
1245         printf("hai> ");
1246         if ((line = readline(stdin)) == NULL)
1247             break;
1248 #else
1249         printf("hai> ");
1250         if (fgets(line, MAX_LINE, stdin) == NULL)
1251             break;
1252 #endif
1253
1254         /* Parse command */
1255         argc = 0;
1256         argv[argc] = strtok(line, " \r\n");
1257         while (argv[argc] != NULL)
1258         {
1259             argc++;
1260             argv[argc] = strtok(NULL, " \r\n");
1261         }
1262
1263         /* Check for null command */
1264         if (argc == 0)
1265         {
1266 #if defined(GNUREADLINE_SUPPORT) || defined(READLINE_SUPPORT)        
1267             free(line);
1268 #endif
1269             continue;
1270         }
1271
1272         /* Check for exit */
1273         if (strcmp_no_case("exit", argv[0]) == 0 ||
1274             strcmp_no_case("quit", argv[0]) == 0 ||
1275             strcmp_no_case("x", argv[0]) == 0    ||
1276             strcmp_no_case("q", argv[0]) == 0)
1277         {
1278 #if defined(GNUREADLINE_SUPPORT) || defined(READLINE_SUPPORT)        
1279             free(line);
1280 #endif
1281             break;
1282         }
1283         if (strcmp_no_case(".", argv[0]) == 0)
1284         {
1285 #if defined(GNUREADLINE_SUPPORT) || defined(READLINE_SUPPORT)        
1286             free(line);
1287 #endif
1288             break;
1289         }
1290
1291         /* Do command */
1292         err = do_command(id, argc, argv);
1293
1294         /* Check for timeout */
1295         /* Also check for CRD errors and try again. I get these often, but this does the trick...RickM */
1296         if ((err == EOMNIRESPONSE) || (err == EHAITIMEOUT) || err == EOMNICRC)
1297         {
1298             /* Reconnect */
1299             term_connection(id);
1300             init_connection(id, code);
1301        
1302             err = do_command(id, argc, argv);    
1303         }
1304
1305 #if defined(GNUREADLINE_SUPPORT) || defined(READLINE_SUPPORT)        
1306         free(line);
1307 #endif
1308
1309         /* Print error message */
1310         if (err > __ELASTOMNI)
1311         {
1312             switch(err)
1313             {
1314                 case EINVCMD :
1315                     printf("error (%d) : Invalid command\n", err);
1316                     break;
1317                 case EBADCODE :
1318                     printf("error (%d) : Invalid code\n", err);
1319                     break;
1320                 case EBADFILE :
1321                     printf("error (%d) : Bad file format\n", err);
1322                     break;
1323             }
1324         }
1325         else if (err > __ELASTHAI)
1326             printf("error (%d) : %s\n", err, omni_strerror(err));
1327         else if (err > __ELASTERROR)
1328             printf("error (%d) : %s\n", err, hai_net_strerror(err));
1329         else if (err != 0)
1330             printf("error (%d) : %s\n", err, strerror(err));
1331
1332         printf("\n");
1333     }
1334
1335     return 0;
1336 }
1337
1338 /********************************************************************************/
1339
1340 int do_command(hai_comm_id *id, int argc, char *argv[])
1341 {
1342     cmd_item *cmd = cmd_list;
1343
1344     while (cmd->cmd_text != NULL)
1345     {
1346         if (strcmp_no_case(cmd->cmd_text, argv[0]) == 0)
1347         {
1348             if (cmd->func != NULL)
1349             {
1350                 return ((cmd->func)(id, argc, argv));
1351             }
1352             break;
1353         }
1354         cmd++;
1355     }
1356
1357     return EINVCMD;
1358 }
1359
1360 /********************************************************************************/
1361
1362 int do_encrypt(hai_comm_id *id, int argc, char *argv[])
1363 {
1364     char buffer[1024];
1365     char sec_buffer[1024];
1366     unsigned char key[HAI_KEY_LEN];
1367     int i, len;
1368
1369     /* Check arguments */
1370     if (argc < 2)
1371         return EINVAL;
1372
1373     /* Prepare key */
1374     memset(key, 0, sizeof(key));
1375     len = strlen(argv[1]);
1376     if (len == 0)
1377         return EINVAL;       
1378     if (len > HAI_KEY_LEN)
1379         len = HAI_KEY_LEN;
1380     memcpy(key, argv[1], len);
1381
1382     /* Encrypt ip address */
1383     if (*ip != 0)
1384     {
1385         len = 1024;
1386         encrypt_bytes(key, ip, strlen(ip) + 1, sec_buffer, &len);
1387         printf("ipx=");
1388         for (i = 0; i < len; i++)
1389             printf("%s%02x", ((i > 0) ? ":" : ""), sec_buffer[i] & 0xff);
1390         putchar('\n');
1391     }
1392
1393     /* Encrypt port */
1394     if (*ip != 0)
1395     {
1396         sprintf(buffer, "%d", port);
1397         len = strlen(buffer);
1398         encrypt_bytes(key, buffer, strlen(buffer) + 1, sec_buffer, &len);
1399         printf("portx=");
1400         for (i = 0; i < len; i++)
1401             printf("%s%02x", ((i > 0) ? ":" : ""), sec_buffer[i] & 0xff);
1402         putchar('\n');
1403     }
1404
1405     /* Encrypt key */
1406     if (*private_key != 0)
1407     {
1408         len = HAI_KEY_LEN;
1409         encrypt_bytes(key, (char *)private_key, HAI_KEY_LEN, sec_buffer, &len);
1410         printf("keyx=");
1411         for (i = 0; i < len; i++)
1412             printf("%s%02x", ((i > 0) ? ":" : ""), sec_buffer[i] & 0xff);
1413         putchar('\n');
1414     }
1415
1416     /* Encrypt code */
1417     if (*code != 0)
1418     {
1419         len = sizeof(code);
1420         encrypt_bytes(key, (char *)code, sizeof(code), sec_buffer, &len);
1421         printf("codex=");
1422         for (i = 0; i < len; i++)
1423             printf("%s%02x", ((i > 0) ? ":" : ""), sec_buffer[i] & 0xff);
1424         putchar('\n');
1425     }
1426     
1427     return 0;
1428 }
1429
1430 /********************************************************************************/
1431
1432 int do_csv(hai_comm_id *id, int argc, char *argv[])
1433 {
1434     /* Check arguments */
1435     if (argc < 2)
1436         return EINVAL;
1437
1438     /* Parse options */
1439     if (strcmp_no_case(argv[1], "on") == 0)
1440         csv = 1;
1441     else if (strcmp_no_case(argv[1], "off") == 0)
1442         csv = 0;
1443     else
1444         return EINVAL;
1445
1446     return 0;
1447 }
1448
1449 /********************************************************************************/
1450
1451 void encrypt_bytes(unsigned char *key, char *buf_in, int len_in, char *buf_out,
1452     int *len_out)
1453 {
1454     char buffer[1024];
1455     AES_KEY aes;
1456     int len_rnd, i;
1457   
1458     /* Copy data */
1459     memcpy(buffer, buf_in, len_in);
1460
1461     /* add padding */
1462     len_rnd = (len_in + (HAI_BLOCK_SIZE - 1)) & ~(HAI_BLOCK_SIZE - 1);
1463     if (len_rnd != len_in)
1464         memset(buffer + len_in, 0, (len_rnd - len_in));
1465
1466     /* Encrypt message */
1467     AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes);
1468
1469     for (i = 0; i < len_rnd; i += AES_BLOCK_SIZE)
1470         AES_ecb_encrypt((unsigned char *)buffer + i,
1471                         (unsigned char *)buf_out + i, &aes, AES_ENCRYPT);
1472
1473     *len_out = len_rnd;
1474 }
1475
1476 /********************************************************************************/
1477
1478 void decrypt_bytes(unsigned char *key, char *buf_in, int len_in, char *buf_out,
1479     int *len_out)
1480 {
1481     AES_KEY aes;
1482     int i;
1483   
1484     /* should we bomb if len_in isn't a multiple of AES_BLOCK_SIZE? */
1485
1486     /* Encrypt message */
1487     AES_set_decrypt_key(key, AES_BLOCK_SIZE * 8, &aes);
1488
1489     for (i = 0; i < len_in; i += AES_BLOCK_SIZE)
1490         AES_ecb_encrypt((unsigned char *)buf_in + i,
1491                         (unsigned char *)buf_out + i, &aes, AES_DECRYPT);
1492
1493     *len_out = len_in;
1494 }
1495
1496 /********************************************************************************/
1497
1498 int do_sys_info(hai_comm_id *id, int argc, char *argv[])
1499 {
1500     sys_info data;
1501     int err;
1502
1503     /* Request system info */
1504     if ((err = omni_sys_info(id, &data)) != 0)
1505         return err;
1506
1507     /* Print model */
1508     if (!csv)
1509         printf("Model: HAI ");
1510     switch (data.model)
1511     {
1512         case OMNILT :
1513             printf("OmniLT");
1514             break;
1515         case OMNI :
1516             printf("Omni");
1517             break;
1518         case OMNI2 :
1519             printf("OmniII");
1520             break;
1521         case OMNI2E :
1522             printf("Omni IIe");
1523             break;
1524         case OMNIPRO :
1525             printf("OmniPro");
1526             break;
1527         case OMNIPRO2 :
1528             printf("OmniPro II");
1529             break;
1530         case LUMINA:
1531             printf("Lumina");
1532             break;
1533         case LUMINAPRO:
1534             printf("Lumina Pro");
1535             break;
1536         default :
1537             printf("Unknown");
1538             break;
1539     }
1540     if (!csv)
1541         printf("\n");
1542
1543     /* Print firmware version */
1544     if (!csv)
1545         printf("Version: %d.%d", data.major, data.minor);
1546     else
1547         printf(",%d.%d", data.major, data.minor);
1548     if ((data.rev != 0) && (data.rev < 0x80))
1549         printf("%c", data.rev + 'A' - 1);
1550     else if (data.rev != 0)
1551         printf("X%d", 0x100 - data.rev);
1552     if (!csv)
1553         printf("\n");
1554
1555     /* Print my phone number */
1556     if (!csv)
1557         printf("Phone: %s\n", data.phone);
1558     else
1559         printf(",%s\n", data.phone);
1560
1561     return 0;
1562 }
1563
1564 /********************************************************************************/
1565
1566 int do_sys_stat(hai_comm_id *id, int argc, char *argv[])
1567 {
1568     sys_stat data;
1569     int err, i;
1570     int security_modes = 1, expansion_enclosures = 0;
1571
1572     /* Calc model parameters */
1573     switch (omni_model)
1574     {
1575         case OMNI :
1576         case OMNI2 :
1577         case OMNI2E :
1578             security_modes = 2;
1579             expansion_enclosures = 0;
1580             break;
1581         case OMNIPRO :
1582             security_modes = 8;
1583             expansion_enclosures = 4;
1584             break;
1585         case OMNIPRO2 :
1586             security_modes = 8;
1587             expansion_enclosures = 8;
1588             break;
1589         case LUMINAPRO:
1590             security_modes = 1;
1591             expansion_enclosures = 8;
1592             break;
1593     }
1594
1595     /* Request system status */
1596     if ((err = omni_sys_stat(id, &data)) != 0)
1597         return err;
1598
1599     /* Print time/date */
1600     if (!csv)
1601     {
1602         printf("Time: ");
1603         if (data.date_valid)
1604         {
1605             printf("%s ", dow_text[data.day_of_week - 1]);
1606             printf("%s ", month_text[data.month - 1]);
1607             printf("%d ", data.day);
1608             printf("%02d:%02d:%02d ", data.hour, data.minute, data.second);
1609             if (data.dst)
1610                 printf("(DST) ");
1611             printf("%d\n", data.year + TIME_YEAR_BASE);
1612         }
1613         else
1614             printf("not valid\n");
1615     }
1616     else
1617     {
1618         if (data.date_valid)
1619         {
1620             printf("Time,%s,", dow_text[data.day_of_week - 1]);
1621             printf("%s,", month_text[data.month - 1]);
1622             printf("%d,", data.day);
1623             printf("%02d:%02d:%02d,", data.hour, data.minute, data.second);
1624             if (data.dst)
1625                 printf("DST,");
1626             else
1627                 printf(",");
1628             printf("%d\n", data.year + TIME_YEAR_BASE);
1629         }
1630         else
1631             printf(",,,,,\n");
1632     }
1633
1634     /* Print Sunrise/Sunset */
1635     if (!csv)
1636     {
1637         if (data.date_valid)
1638         {
1639             printf("Sunrise: %02d:%02d\n", data.sunrise_hour, data.sunrise_minute);
1640             printf("Sunset:  %02d:%02d\n", data.sunset_hour, data.sunset_minute);
1641         }
1642     }
1643     else
1644     {
1645         if (data.date_valid)
1646         {
1647             printf("Sun,%02d:%02d,", data.sunrise_hour, data.sunrise_minute);
1648             printf("%02d:%02d\n", data.sunset_hour, data.sunset_minute);
1649         }
1650         else
1651             printf(",\n");
1652     }
1653
1654     /* Print battery reading */
1655     if (!csv)
1656         printf("Battery Reading: %d\n", data.battery);
1657     else
1658         printf("Battery,%d\n", data.battery);   
1659
1660     /* Print security modes */
1661     if ((omni_model == LUMINA) || (omni_model == LUMINAPRO))
1662     {
1663     if (!csv)
1664             printf("Security mode: %s\n", lumina_sec_text[data.security_mode[0]]);
1665         else
1666             printf("Security Mode,%s\n", lumina_sec_text[data.security_mode[0]]);
1667     }
1668     else
1669     {
1670         if (!csv)
1671         {
1672             printf("Security Modes:\n");
1673         for (i = 0; i < security_modes; i++)
1674             printf("    Area %d : %s\n", i + 1, sec_text[data.security_mode[i]]);
1675     }
1676     else
1677     {
1678             printf("Security Modes,");
1679         for (i = 0; i < security_modes; i++)
1680         {
1681             printf("%s", sec_text[data.security_mode[i]]);
1682             if (i < (security_modes - 1))
1683                 printf(",");
1684             else
1685                 printf("\n");            
1686         }
1687     }
1688     }
1689
1690     /* Expansion status */
1691     if (!csv)
1692     {
1693         if (expansion_enclosures)
1694         {
1695             printf("Expansion Enclosures:\n");
1696             for (i = 0; i < expansion_enclosures; i++)
1697             {
1698                 printf("    %d : Battery=%d", i + 1,
1699                     data.expansion_enclosure[i].battery);
1700                 if (data.expansion_enclosure[i].status & EXP_STAT_AC)
1701                     printf(", AC off");
1702                 if (data.expansion_enclosure[i].status & EXP_STAT_BATT)
1703                     printf(", Batt low");
1704                 if (data.expansion_enclosure[i].status & EXP_STAT_COMM)
1705                     printf(", Comm failure");
1706                 printf("\n");
1707             }
1708         }
1709     }
1710     else
1711     {
1712         printf("Expansion Enclosures,");
1713         for (i = 0; i < expansion_enclosures; i++)
1714         {
1715             if (expansion_enclosures)
1716                 printf("%d,%d", data.expansion_enclosure[i].battery,
1717                     data.expansion_enclosure[i].status);
1718             if (i < (expansion_enclosures - 1))
1719                 printf(",");
1720             else
1721                 printf("\n");
1722         }
1723     }
1724
1725     return 0;
1726 }
1727
1728 /********************************************************************************/
1729
1730 int do_zone_stat(hai_comm_id *id, int argc, char *argv[])
1731 {
1732     zone_stat data;
1733     int first, last, start, end;
1734     int err, i;
1735     int printAll = 0;
1736
1737     /* Check arguments */
1738     if (argc > 3)
1739         return EINVAL;
1740
1741     /* Setup arguments */
1742     if (argc > 1 && strcmp_no_case(argv[1], "all") == 0) {
1743          printAll = 1;
1744     }
1745     if (argc == 1 || printAll)
1746     {
1747         first = 1;
1748         last = max_zones_status;
1749     }
1750     else
1751     {
1752         first = atoi(argv[1]);
1753         if (argc > 2)
1754             last = atoi(argv[2]);
1755         else
1756             last = first;
1757     }
1758
1759     /* Check range */
1760     if ((first < 1) || (last < first) || (last > max_zones_status)) {
1761          printf("error: zone not in range from 1 to %d\n", max_zones_status);
1762          return EINVAL;
1763     }
1764
1765     /* Init loop parms */
1766     start = first;
1767     end = start + 50;
1768     if (end > last)
1769         end = last;
1770
1771     /* Loop to get all zones */
1772     while(start <= last)
1773     {
1774         /* Request zone status */
1775         if ((err = omni_zone_stat(id, start, end, &data)) != 0)
1776             return err;
1777
1778         /* Print zone status */
1779         for (i = 0; i < (end - start + 1); i++)
1780         {
1781             int current = ((data.zones[i].status >> 0)& 0x03);
1782             int latched = ((data.zones[i].status >> 2)& 0x03);
1783             int arming  = ((data.zones[i].status >> 4)& 0x03);
1784             int trouble = ((data.zones[i].status >> 6)& 0x01);
1785
1786             /* Skip unnamed */
1787             if (name_cache_valid && !printAll && (argc == 1) && !is_named(NAMES_ZONE, i + start))
1788                 continue;
1789
1790             if (!csv)
1791             {
1792                 printf("%3d : ", i + start);
1793                 print_name_fixed(stdout, NAMES_ZONE, i + start);
1794                 printf(" : %03d, %9s, %13s, %13s", data.zones[i].loop,
1795                     zone_text[current], latched_text[latched], arming_text[arming]);
1796                 if (trouble)
1797                     printf(" Latched Trouble");
1798                 printf("\n");
1799              }
1800              else
1801              {
1802                 printf("%d,", i + start);
1803                 print_name(stdout, NAMES_ZONE, i + start);
1804                 printf(",%d,%s,%s,%s,", data.zones[i].loop,
1805                     zone_text[current], latched_text[latched], arming_text[arming]);
1806                 if (trouble)
1807                     printf("LT\n");
1808                 printf("\n");
1809              }
1810         }
1811
1812         /* Next block */
1813         start = end + 1;
1814         end = start + 50;
1815         if (end > last)
1816            end = last;        
1817     }
1818
1819     return 0;
1820 }
1821
1822 /********************************************************************************/
1823
1824 int do_unit_stat(hai_comm_id *id, int argc, char *argv[])
1825 {
1826     unit_stat data;
1827     int first, last, start, end;
1828     int err, i;
1829     int printAll = 0;
1830
1831     /* Check arguments */
1832     if (argc > 3)
1833         return EINVAL;
1834
1835     /* Setup arguments */
1836     if (argc > 1 && strcmp_no_case(argv[1], "all") == 0) {
1837          printAll = 1;
1838     }
1839     if (argc == 1 || printAll)
1840     {
1841         first = 1;
1842         last = max_units;
1843     }
1844     else
1845     {
1846         first = atoi(argv[1]);
1847         if (argc > 2)
1848             last = atoi(argv[2]);
1849         else
1850             last = first;
1851     }
1852
1853     /* Check range */
1854     if ((first < 1) || (last < first) || (last > max_units)) {
1855          printf("error: unit not in range from 1 to %d\n", max_units);
1856          return EINVAL;
1857     }
1858
1859     /* Init loop parms */
1860     start = first;
1861     end = start + 50;
1862     if (end > last)
1863         end = last;
1864
1865                 
1866     /* Loop to get all units */
1867     while(start <= last)
1868     {
1869         /* Request unit status */
1870         if ((err = omni_unit_stat(id, start, end, &data)) != 0)
1871             return err;
1872
1873         /* Print unit status */
1874         for (i = 0; i < (end - start + 1); i++)
1875         {
1876             /* Skip unnamed */
1877             if (name_cache_valid && !printAll && (argc == 1) && !is_named(NAMES_UNIT, i + start))
1878                 continue;
1879
1880             if (!csv)
1881             {
1882                 printf("%3d : ", i + start);
1883                 print_name_fixed(stdout, NAMES_UNIT, i + start);
1884                 printf(" : %03d, ", data.units[i].status);
1885                 if (data.units[i].status == UNIT_OFF)
1886                     printf("Off");
1887                 else if (data.units[i].status == UNIT_ON)
1888                     printf("On");
1889                 else if ((data.units[i].status >= UNIT_SCENE_A)
1890                         && (data.units[i].status <= UNIT_SCENE_L))
1891                     printf("Scene %c", data.units[i].status - UNIT_SCENE_A + 'A');
1892                 else if ((data.units[i].status >= UNIT_DIM_1)
1893                         && (data.units[i].status <= UNIT_DIM_9))
1894                     printf("Dim %d", data.units[i].status - UNIT_DIM_1 + 1);
1895                 else if ((data.units[i].status >= UNIT_BRIGHTEN_1)
1896                         && (data.units[i].status <= UNIT_BRIGHTEN_9))
1897                     printf("Brighten %d", data.units[i].status - UNIT_BRIGHTEN_1 + 1);
1898                 else if ((data.units[i].status >= UNIT_LEVEL_0)
1899                         && (data.units[i].status <= UNIT_LEVEL_100))
1900                     printf("Level %d", data.units[i].status - UNIT_LEVEL_0);
1901                 if (GET16(data.units[i].time) != 0)
1902                     printf(" for %d secs", GET16(data.units[i].time));
1903                 printf("\n");
1904             }
1905             else
1906             {
1907                 printf("%d,", i + start);
1908                 print_name(stdout, NAMES_UNIT, i + start);
1909                 printf(",%d,", data.units[i].status);
1910                 if (data.units[i].status == UNIT_OFF)
1911                     printf("Off");
1912                 else if (data.units[i].status == UNIT_ON)
1913                     printf("On");
1914                 else if ((data.units[i].status >= UNIT_SCENE_A)
1915                         && (data.units[i].status <= UNIT_SCENE_L))
1916                     printf("Scene %c", data.units[i].status - UNIT_SCENE_A + 'A');
1917                 else if ((data.units[i].status >= UNIT_DIM_1)
1918                         && (data.units[i].status <= UNIT_DIM_9))
1919                     printf("Dim %d", data.units[i].status - UNIT_DIM_1 + 1);
1920                 else if ((data.units[i].status >= UNIT_BRIGHTEN_1)
1921                         && (data.units[i].status <= UNIT_BRIGHTEN_9))
1922                     printf("Brighten %d", data.units[i].status - UNIT_BRIGHTEN_1 + 1);
1923                 else if ((data.units[i].status >= UNIT_LEVEL_0)
1924                         && (data.units[i].status <= UNIT_LEVEL_100))
1925                     printf("Level %d", data.units[i].status - UNIT_LEVEL_0);
1926                 printf(",%d\n", GET16(data.units[i].time));
1927             }
1928         }
1929
1930         /* Next block */
1931         start = end + 1;
1932         end = start + 50;
1933         if (end > last)
1934            end = last;        
1935     }
1936
1937     return 0;
1938 }
1939
1940 /********************************************************************************/
1941
1942 int do_sensor_stat(hai_comm_id *id, int argc, char *argv[])
1943 {
1944     aux_stat data;
1945     int first, last, start, end;
1946     int err, i;
1947     int printAll = 0;
1948
1949     /* Check arguments */
1950     if (argc > 3)
1951         return EINVAL;
1952
1953     /* Setup arguments */
1954     if (argc > 1 && strcmp_no_case(argv[1], "all") == 0) {
1955          printAll = 1;
1956     }
1957     if (argc == 1 || printAll)
1958     {
1959         first = 1;
1960         last = max_auxs;
1961     }
1962     else
1963     {
1964         first = atoi(argv[1]);
1965         if (argc > 2)
1966             last = atoi(argv[2]);
1967         else
1968             last = first;
1969     }
1970
1971     /* Check range */
1972     if ((first < 1) || (last < first) || (last > max_auxs)) {
1973          printf("error: sensor not in range from 1 to %d\n", max_auxs);
1974          return EINVAL;
1975     }
1976
1977     /* Init loop parms */
1978     start = first;
1979     end = start + 25;
1980     if (end > last)
1981         end = last;
1982
1983     /* Loop to get all sensors */
1984     while(start <= last)
1985     {
1986         /* Request aux status */
1987         if ((err = omni_aux_stat(id, start, end, &data)) != 0)
1988             return err;
1989
1990         /* Print aux status */
1991         for (i = 0; i < (end - start + 1); i++)
1992         {
1993             /* Skip ones that are not temp sensors */
1994             if (data.auxs[i].temp == 0)
1995                 continue;
1996             /* Skip unnamed */
1997             if (name_cache_valid && !printAll && (argc == 1) && !is_named(NAMES_ZONE, i + start))
1998                 continue;
1999
2000             if (!csv)
2001             {
2002                 printf("%3d : ",  i + start);
2003                 print_name_fixed(stdout, NAMES_ZONE, i + start);
2004                 printf(" : %s\n",  data.auxs[i].status ? "On" : "Off");
2005                 printf("   Temp: %3dF, %5.1fC\n", GETF(data.auxs[i].temp),
2006                     GETC(data.auxs[i].temp));
2007                 printf("   High: %3dF, %5.1fC\n", GETF(data.auxs[i].high_setpoint),
2008                     GETC(data.auxs[i].high_setpoint));
2009                 printf("   Low:  %3dF, %5.1fC\n", GETF(data.auxs[i].low_setpoint),
2010                     GETC(data.auxs[i].low_setpoint));
2011             }
2012             else
2013             {
2014                 printf("%d,",  i + start);
2015                 print_name(stdout, NAMES_ZONE, i + start);
2016                 printf(",%s,",  data.auxs[i].status ? "On" : "Off");
2017                 printf("%d,%.1f,", GETF(data.auxs[i].temp),
2018                     GETC(data.auxs[i].temp));
2019                 printf("%d,%.1f,", GETF(data.auxs[i].high_setpoint),
2020                     GETC(data.auxs[i].high_setpoint));
2021                 printf("%d,%.1f\n", GETF(data.auxs[i].low_setpoint),
2022                     GETC(data.auxs[i].low_setpoint));
2023             }
2024         }
2025         
2026         /* Next block */
2027         start = end + 1;
2028         end = start + 25;
2029         if (end > last)
2030            end = last;        
2031     }
2032
2033     return 0;
2034 }
2035
2036 /********************************************************************************/
2037
2038 int do_thermo_stat(hai_comm_id *id, int argc, char *argv[])
2039 {
2040     thermo_stat data;
2041     int first, last, start, end;
2042     int err, i;
2043     int printAll = 0;
2044
2045     /* Check arguments */
2046     if (argc > 3)
2047         return EINVAL;
2048
2049     /* Setup arguments */
2050     if (argc > 1 && strcmp_no_case(argv[1], "all") == 0) {
2051          printAll = 1;
2052     }
2053     if (argc == 1 || printAll)
2054     {
2055         first = 1;
2056         last = max_thermos;
2057     }
2058     else
2059     {
2060         first = atoi(argv[1]);
2061         if (argc > 2)
2062             last = atoi(argv[2]);
2063         else
2064             last = first;
2065     }
2066
2067     /* Check range */
2068     if ((first < 1) || (last < first) || (last > max_thermos)) {
2069          printf("error: thermo not in range from 1 to %d\n", max_thermos);
2070          return EINVAL;
2071     }
2072
2073     /* Init loop parms */
2074     start = first;
2075     end = start + 25;
2076     if (end > last)
2077         end = last;
2078
2079     /* Loop to get all thermostats */
2080     while(start <= last)
2081     {
2082         /* Request thermo status */
2083         if ((err = omni_thermo_stat(id, start, end, &data)) != 0)
2084             return err;
2085
2086         /* Print thermo status */
2087         for (i = 0; i < (end - start + 1); i++)
2088         {
2089             /* Skip unused */
2090             if (!printAll && (argc == 1) && (data.thermos[i].temp == 0))
2091                 continue;
2092
2093             /* Skip unnamed */
2094             if (name_cache_valid && !printAll && (argc == 1) && !is_named(NAMES_THERMOSTAT, i + start))
2095                 continue;
2096
2097             if (!csv)
2098             {
2099                 printf("%2d : ",  i + start);
2100                 print_name_fixed(stdout, NAMES_THERMOSTAT, i + start);
2101                 printf(" : %s",  ac_text[data.thermos[i].sys_mode]);
2102                 printf(", Fan %s",  data.thermos[i].fan_mode ? (data.thermos[i].fan_mode == 1 ? "On" : "Cycle") : "Auto");
2103                 printf(", Hold %s",  data.thermos[i].hold_status ? "On" : "Off");       
2104                 if (data.thermos[i].status & THERMO_COMM_FAIL)
2105                     printf(" (Comms Failure)");
2106                 if (data.thermos[i].status & THERMO_FREEZE_ALARM)
2107                     printf(" (Freeze Alarm)");
2108                 printf("\n   Temp: %3dF, %5.1fC\n", GETF(data.thermos[i].temp),
2109                     GETC(data.thermos[i].temp));
2110                 printf("   Heat: %3dF, %5.1fC\n", GETF(data.thermos[i].heat_setpoint),
2111                     GETC(data.thermos[i].heat_setpoint));
2112                 printf("   Cool: %3dF, %5.1fC\n", GETF(data.thermos[i].cool_setpoint),
2113                     GETC(data.thermos[i].cool_setpoint));
2114             }
2115             else
2116             {
2117                 printf("%d,",  i + start);
2118                 print_name(stdout, NAMES_THERMOSTAT, i + start);
2119                 printf(",%s,",  ac_text[data.thermos[i].sys_mode]);
2120                 printf("%s,",  data.thermos[i].fan_mode ? (data.thermos[i].fan_mode == 1 ? "On" : "Cycle") : "Auto");
2121                 printf("%s,",  data.thermos[i].hold_status ? "On" : "Off");                      
2122                 printf("%d,%.1f,", GETF(data.thermos[i].temp),
2123                     GETC(data.thermos[i].temp));
2124                 printf("%d,%.1f,", GETF(data.thermos[i].heat_setpoint),
2125                     GETC(data.thermos[i].heat_setpoint));
2126                 printf("%d,%.1f,", GETF(data.thermos[i].cool_setpoint),
2127                     GETC(data.thermos[i].cool_setpoint));
2128                 if (data.thermos[i].status & THERMO_COMM_FAIL)
2129                     printf("CF");
2130                 printf(",");               
2131                 if (data.thermos[i].status & THERMO_FREEZE_ALARM)
2132                     printf("FA");
2133                 printf("\n");
2134             }
2135         }
2136     
2137         /* Next block */
2138         start = end + 1;
2139         end = start + 25;
2140         if (end > last)
2141            end = last;        
2142     }
2143
2144     return 0;
2145 }
2146
2147 /********************************************************************************/
2148
2149 int do_mesg_stat(hai_comm_id *id, int argc, char *argv[])
2150 {
2151     mesg_stat data;
2152     int first, last;
2153     int err, num, i;
2154     int printAll = 0;
2155
2156     /* Check arguments */
2157     if (argc > 3)
2158         return EINVAL;
2159
2160     /* Setup arguments */
2161     if (argc > 1 && strcmp_no_case(argv[1], "all") == 0) {
2162          printAll = 1;
2163     }
2164     if (argc == 1 || printAll)
2165     {
2166         first = 1;
2167         last = max_mesgs;
2168     }
2169     else
2170     {
2171         first = atoi(argv[1]);
2172         if (argc > 2)
2173             last = atoi(argv[2]);
2174         else
2175             last = first;
2176     }
2177
2178     /* Check range */
2179     if ((first < 1) || (last < first) || (last > max_mesgs)) {
2180          printf("error: mesg not in range from 1 to %d\n", max_mesgs);
2181          return EINVAL;
2182     }
2183
2184     /* Request message status */
2185     if ((err = omni_mesg_stat(id, &data, &num)) != 0)
2186         return err;
2187
2188     /* Print message status */
2189     if (data.memo && !csv)
2190         printf("Memo pending\n"); 
2191
2192     /* Adjust for num */
2193     if (first > num)
2194         return 0;
2195     if (last > num)
2196         last = num;
2197         
2198     for (i = (first - 1); i < last; i++)
2199     {
2200         int displayed = (data.mesgs[i / 4] >> (6 - ((i % 4) * 2))) & 0x01;
2201         int not_ack = (data.mesgs[i / 4] >> (6 - ((i % 4) * 2))) & 0x02;
2202
2203         /* Skip unnamed */
2204         if (name_cache_valid && !printAll && (argc == 1) && !is_named(NAMES_MESSAGE, i + 1))
2205             continue;
2206
2207         if (!csv)
2208         {
2209             printf("%3d : ",  i + 1);
2210             print_name_fixed(stdout, NAMES_MESSAGE, i + 1);
2211             if (displayed)
2212                 printf(" : displayed, %sacknowledged\n",  not_ack 
2213                     ? "not " : "    ");
2214             else
2215                 printf(" : not displayed\n");
2216         }
2217         else
2218         {
2219             printf("%d,",  i + 1);
2220             print_name(stdout, NAMES_MESSAGE, i + 1);
2221             if (displayed)
2222                 printf(",displayed,%sacknowledged\n",  not_ack 
2223                     ? "not " : "    ");
2224             else
2225                 printf(",not displayed,\n");
2226         }
2227     }
2228         
2229     return 0;
2230 }
2231
2232 /********************************************************************************/
2233
2234 int do_usersetting_stat(hai_comm_id *id, int argc, char *argv[])
2235 {
2236     usersetting_stat data;
2237     int first, last, start, end, value;
2238     int err, i;
2239     int printAll = 0;
2240
2241     if (omni_major_version < 3) {
2242          printf("error (%d) : Invalid command: Firmware v3.0 or greater required.\n", EINVCMD);
2243          return 0;
2244     }
2245     
2246     /* Check arguments */
2247     if (argc > 3)
2248         return EINVAL;
2249     
2250     /* Setup arguments */
2251     if (argc > 1 && strcmp_no_case(argv[1], "all") == 0) {
2252          printAll = 1;
2253     }
2254     if (argc == 1 || printAll)
2255     {
2256         first = 1;
2257         last = max_usersettings;
2258     }
2259     else
2260     {
2261         first = atoi(argv[1]);
2262         if (argc > 2)
2263             last = atoi(argv[2]);
2264         else
2265             last = first;
2266     }
2267
2268     /* Check range */
2269     if ((first < 1) || (last < first) || (last > max_usersettings)) {
2270          printf("error: usersetting not in range from 1 to %d\n", max_usersettings);
2271          return EINVAL;
2272     }
2273
2274     /* Init loop parms */
2275     start = first;
2276     end = start + 50;
2277     if (end > last)
2278         end = last;
2279
2280     /* Loop to get all usersettings */
2281     while(start <= last)
2282     {
2283         /* Request usersetting status */
2284         if ((err = omni_usersetting_stat(id, start, end, &data)) != 0)
2285             return err;
2286
2287         /* Print usersetting status */
2288         for (i = 0; i < (end - start + 1); i++)
2289         {
2290             val8 dow;
2291             int type = (data.usersettings[i].type);
2292             /* Skip unnamed */
2293             if (name_cache_valid && !printAll && (argc == 1) && !is_named(NAMES_USERSETTING, i + start))
2294                 continue;
2295 /*
2296   The user setting types are:
2297         0                   Not Used
2298         1                   Number
2299         2                   Duration
2300         3                   Temperature
2301         4                   Humidity
2302         5                   Date
2303         6                   Time
2304         7                   Days of Week
2305         8                   Level
2306
2307 Numbers are reported as 0-255 in the low byte.
2308 For durations, 1-99 = 1-99 seconds, 101-199 = 1-99 minutes, and 201-218 = 1-18 hours.
2309 Temperatures are reported in the Omni temperature format (see Appendix B).
2310 Humidity is reported in the Omni temperature format where Fahrenheit temperatures 0-100 correspond to 0-100% relative humidity
2311 (see Appendix B).
2312 Dates are reported with the month in the high byte and the day in the low byte.
2313 Times are reported with the hour in the high byte and the minute in the low byte.
2314 Days of the week are reported in the low byte, with bit 1 set for Monday, bit 2 set for Tuesday, and so on with bit 7 set for Sunday.
2315 Bit 0 is not used.
2316 Levels are reported as 0-100 in the low byte.
2317
2318 High byte is data1; Low byte is data2.
2319  */
2320             if (!csv) {
2321                 printf("%3d : ", i + start);
2322                 print_name_fixed(stdout, NAMES_USERSETTING, i + start);
2323                 printf(" : %12s: ", usersetting_text[type]);
2324             } else {
2325                 printf("%d,", i + start);
2326                 print_name(stdout, NAMES_USERSETTING, i + start);
2327                 printf(",");
2328             }
2329             switch (type)
2330             {
2331             case CMD_USER_SET_NOTUSED:
2332                  /* Nothing more to print */
2333                  printf("\n");
2334                  break;
2335             case CMD_USER_SET_NUMBER:
2336                  printf("%d\n", data.usersettings[i].data2);
2337                  break;
2338             case CMD_USER_SET_DURATION:
2339                  value = data.usersettings[i].data2;
2340                  if (value < 100) {
2341                       printf("%d", value);
2342                       if (!csv) {
2343                            printf(" seconds");
2344                       }
2345                  } else if (value < 200) {
2346                       printf("%d", value - 100);
2347                       if (!csv) {
2348                            printf(" minutes");
2349                       }
2350                  } else {
2351                       printf("%d", value - 200);
2352                       if (!csv) {
2353                            printf(" hours");
2354                       }
2355                  }
2356                  printf("\n");
2357                  break;
2358             case CMD_USER_SET_TEMPERATURE:
2359                  if (!csv) {
2360                       printf("%3dF, %5.1fC\n",
2361                              GETF(data.usersettings[i].data2),
2362                              GETC(data.usersettings[i].data2));
2363                  } else {
2364                       printf("%d,%.1f\n",
2365                              GETF(data.usersettings[i].data2),
2366                              GETC(data.usersettings[i].data2));
2367                  }
2368                  break;
2369             case CMD_USER_SET_HUMIDITY:
2370                  /* This is a work-around for a strange OmniLink/UDP issue. If you send 0% humidity, it later comes back from the controller as a binary 0 instead of the decimal 44 we sent to indicate 0 degrees F (0% humidity). */
2371                  if (data.usersettings[i].data2 == 0) {
2372                       data.usersettings[i].data2 = 44;
2373                  }
2374                  printf("%d", GETF(data.usersettings[i].data2));
2375                  if (!csv) {
2376                       printf("%%");
2377                  }
2378                  printf("\n");
2379                  printf("data1 %02x data2 %02x", data.usersettings[i].data1, data.usersettings[i].data2);
2380                  break;
2381             case CMD_USER_SET_DATE:
2382                  if (!csv) {
2383                       printf("%s %d\n", month_text[data.usersettings[i].data1 - 1],
2384                              data.usersettings[i].data2);
2385                  } else {
2386                       printf("%s,%d\n", month_text[data.usersettings[i].data1 - 1],
2387                              data.usersettings[i].data2);
2388                  }
2389                  break;
2390             case CMD_USER_SET_TIME:
2391                  printf("%02d:%02d\n", data.usersettings[i].data1,
2392                         data.usersettings[i].data2);
2393                  break;
2394             case CMD_USER_SET_DAYSOFWEEK: /* Days of Week */
2395                  dow = data.usersettings[i].data2;
2396                  printf("%c", dow & 0x02 ? 'M' : '-');
2397                  printf("%c", dow & 0x04 ? 'T' : '-');
2398                  printf("%c", dow & 0x08 ? 'W' : '-');
2399                  printf("%c", dow & 0x10 ? 'T' : '-');
2400                  printf("%c", dow & 0x20 ? 'F' : '-');
2401                  printf("%c", dow & 0x40 ? 'S' : '-');
2402                  printf("%c", dow & 0x80 ? 'S' : '-');
2403                  printf("\n");
2404                  break;
2405             case CMD_USER_SET_LEVEL:
2406                  printf("%d", data.usersettings[i].data2);
2407                  if (!csv) {
2408                       printf("%%");
2409                  }
2410                  printf("\n");
2411                  break;
2412             }
2413         }
2414
2415         /* Next block */
2416         start = end + 1;
2417         end = start + 50;
2418         if (end > last)
2419            end = last;  
2420     }
2421
2422     return 0;
2423 }
2424
2425 /********************************************************************************/
2426
2427 int do_events(hai_comm_id *id, int argc, char *argv[])
2428 {
2429     sys_events data;
2430     int err, i, num, event;
2431
2432     /* Request system events */
2433     if ((err = omni_sys_events(id, &data, &num)) != 0)
2434         return err;
2435
2436     for (i = 0; i < num; i++)
2437     {
2438         event = GET16(data.events[i]);
2439
2440         if (EVENT_BUTTON(event))
2441         {
2442             printf("User Macro ");
2443             print_name(stdout, NAMES_BUTTON, EVENT_BUTTON_NUM(event));
2444         }
2445         else if (EVENT_PROLINK(event))
2446         {
2447             printf("Pro-Link Message % d", EVENT_PROLINK_NUM(event));
2448         }
2449         else if (EVENT_CENTRALITE(event))
2450         {
2451             printf("Centralite Switch %d", EVENT_CENTRALITE_NUM(event));
2452         }
2453         else if (EVENT_ALARM(event))
2454         {
2455             printf("%s Alarm in ", alarm_text[EVENT_ALARM_TYPE(event)]);
2456             print_name(stdout, NAMES_AREA, EVENT_ALARM_AREA(event));
2457        }
2458         else if (EVENT_ZONE(event))
2459         {
2460             print_name(stdout, NAMES_ZONE, EVENT_ZONE_NUM(event));
2461             printf(" %s", EVENT_ZONE_STATE(event) ? "On" : "Off");
2462         }
2463         else if (EVENT_UNIT(event))
2464         {
2465             print_name(stdout, NAMES_UNIT, EVENT_UNIT_NUM(event));
2466             printf(" %s", EVENT_UNIT_STATE(event) ? "On" : "Off");
2467         }
2468         else if (EVENT_COMPOSE(event))
2469         {
2470             if (EVENT_COMPOSE_STATE(event) < EVENT_COMPOSE_SCENE)
2471                 printf("Compose %c%d %s", EVENT_COMPOSE_CODE(event) + 'A',
2472                     EVENT_COMPOSE_NUM(event), EVENT_COMPOSE_STATE(event) ? "On"
2473                     : "Off");
2474             else
2475                 printf("Compose %c%d Scene %c", EVENT_COMPOSE_CODE(event) + 'A',
2476                     EVENT_COMPOSE_NUM(event), EVENT_COMPOSE_STATE(event)
2477                     - EVENT_COMPOSE_SCENE + 'A');
2478         }
2479         else if (EVENT_X10(event))
2480         {
2481             if (EVENT_X10_ALL(event))   /* HAI bug workaround */
2482                 printf("X-10 All %c %s", ((EVENT_X10_HOUSE(event) + 1) & 0xF)
2483                     + 'A', EVENT_X10_STATE(event) ? "On" : "Off");
2484             else
2485                 printf("X-10 %c%d %s", EVENT_X10_HOUSE(event) + 'A',
2486                     EVENT_X10_UNIT(event) + 1, EVENT_X10_STATE(event) ? "On"
2487                     : "Off");
2488         }
2489         else if (EVENT_ARM(event))
2490         {
2491             if ((omni_model == LUMINA) || (omni_model == LUMINAPRO))
2492                 printf("Arm %s in ", lumina_sec_text[EVENT_ARM_MODE(event)]);
2493             else
2494             printf("Arm %s in ", sec_text[EVENT_ARM_MODE(event)]);
2495             print_name(stdout, NAMES_AREA, EVENT_ARM_AREA(event));
2496             print_code(stdout, EVENT_ARM_CODE(event));
2497             printf(" (%s)", EVENT_ARM_DELAY(event) ? "start" : "end");
2498         }
2499         else if (EVENT_SWITCH(event))
2500         {
2501             print_name(stdout, NAMES_UNIT, EVENT_SWITCH_UNIT(event));
2502             if (EVENT_SWITCH_STATE(event) <= 1)
2503                 printf(" Switch %s", EVENT_SWITCH_STATE(event) ? "On" : "Off");
2504             else
2505                 printf(" Switch %d", EVENT_SWITCH_STATE(event));
2506         }
2507         else if (EVENT_UPBLINK(event))
2508         {
2509             printf("UPB Link %d %s", EVENT_UPBLINK_NUM(event),
2510                     upb_text[EVENT_UPBLINK_CMD(event)]);
2511         }        
2512         else if (EVENT_ALL(event))
2513         {
2514             printf("All %s in ", EVENT_ALL_STATE(event) ? "On" : "Off");
2515             print_name(stdout, NAMES_AREA, EVENT_ALL_AREA(event));
2516         }
2517         else if (EVENT_PHONE_DEAD(event))
2518             printf("Phone Line Dead");
2519         else if (EVENT_PHONE_RING(event))
2520             printf("Phone Line Ring");
2521         else if (EVENT_PHONE_OFF(event))
2522             printf("Phone Line Off Hook");
2523         else if (EVENT_PHONE_ON(event))
2524             printf("Phone Line On Hook");
2525         else if (EVENT_AC_OFF(event))
2526             printf("AC Power Off");
2527         else if (EVENT_AC_ON(event))
2528             printf("AC Power On");
2529         else if (EVENT_BATT_LOW(event))
2530             printf("Battery Low");
2531         else if (EVENT_BATT_OK(event))
2532             printf("Battery Okay");
2533         else if (EVENT_DCM_TRBL(event))
2534             printf("DCM Trouble");
2535         else if (EVENT_DCM_OK(event))
2536             printf("DCM Okay");
2537         else if (EVENT_ENG_LOW(event))
2538             printf("Energy Cost Low");
2539         else if (EVENT_ENG_MID(event))
2540             printf("Energy Cost Mid");
2541         else if (EVENT_ENG_HIGH(event))
2542             printf("Energy Cost High");
2543         else if (EVENT_ENG_CRIT(event))
2544             printf("Energy Cost Critical");
2545         else
2546             printf("Unknown event 0x%04x", event);
2547
2548         printf("\n");
2549     }
2550
2551     return 0;
2552 }
2553
2554 /********************************************************************************/
2555
2556 int do_unit_cmd(hai_comm_id *id, int argc, char *argv[])
2557 {
2558     int cmd, level, step, p1 = 0, p2;
2559     int err;
2560     char *time = NULL;
2561     char *user = NULL;
2562
2563     /* Check arguments */
2564     if (argc < 3)
2565         return EINVAL;
2566
2567     /* Parse unit */
2568     p2 = atoi(argv[1]);
2569     if ((p2 < 0) || (p2 > max_units)) {
2570          printf("error: unit not in range from 1 to %d\n", max_units);
2571          return EINVAL;
2572     }
2573
2574     /* Parse command */
2575     if (strcmp_no_case(argv[2], "off") == 0)
2576     {
2577         cmd = CMD_OFF;
2578         time = argv[3]; 
2579         if (argc > 4 && strcmp_no_case(time, "user") == 0)
2580              user = argv[4];
2581     }
2582     else if (strcmp_no_case(argv[2], "on") == 0)
2583     {
2584         cmd = CMD_ON;
2585         time = argv[3]; 
2586         if (argc > 4 && strcmp_no_case(time, "user") == 0)
2587              user = argv[4];
2588     }
2589     else if (strcmp_no_case(argv[2], "dim") == 0)
2590     {
2591         if (argc < 4)
2592             return EINVAL;
2593         step = atoi(argv[3]);
2594         if ((step < 1) || (step > 9))
2595              return EINVAL;
2596         cmd = CMD_DIM + step;
2597         time = argv[4];
2598         if (argc > 5 && strcmp_no_case(time, "user") == 0)
2599              user = argv[5];
2600     }
2601     else if (strcmp_no_case(argv[2], "brighten") == 0)
2602     {
2603         if (argc < 4)
2604             return EINVAL;
2605         step = atoi(argv[3]);
2606         if ((step < 1) || (step > 9))
2607              return EINVAL;
2608         cmd = CMD_BRIGHTEN + step;
2609         time = argv[4];
2610         if (argc > 5 && strcmp_no_case(time, "user") == 0)
2611              user = argv[5];
2612     }
2613     else if (strcmp_no_case(argv[2], "level") == 0)
2614     {
2615         if (argc < 4)
2616             return EINVAL;
2617         cmd = CMD_LEVEL_TIMED;
2618         level = atoi(argv[3]);
2619         if ((level < 0) || (level > 100))
2620              return EINVAL;
2621         p2 = p2 | (level << 9);
2622         time = argv[4];
2623         if (argc > 5 && strcmp_no_case(time, "user") == 0)
2624              user = argv[5];
2625     }
2626     else if (strcmp_no_case(argv[2], "ramp") == 0)
2627     {
2628         if (argc < 4)
2629             return EINVAL;
2630         cmd = CMD_ALC;
2631         level = atoi(argv[3]);
2632         if ((level < 0) || (level > 100))
2633              return EINVAL;
2634         p2 = p2 | (level << 9);
2635         time = argv[4];
2636         if (argc > 5 && strcmp_no_case(time, "user") == 0)
2637              user = argv[5];
2638     }
2639     else
2640         return EINVAL;
2641
2642     /* Parse time */
2643     if (time != NULL)
2644     {
2645         if (strcmp_no_case(time, "user") == 0)
2646         {
2647              p1 = atoi(user) + CMD_USERSETTING;
2648         }
2649         else
2650         {
2651         p1 = atoi(time);
2652
2653         if (strchr(time, 'm') != 0)
2654             p1 += CMD_MINUTES;
2655         else if (strchr(time, 'h') != 0)
2656             p1 += CMD_HOURS;
2657     }
2658     }
2659
2660     /* Send command */
2661     if ((err = omni_command(id, cmd, p1, p2)) != 0)
2662         return err;
2663
2664     return 0;
2665 }
2666
2667 /********************************************************************************/
2668
2669 int do_all_cmd(hai_comm_id *id, int argc, char *argv[])
2670 {
2671     int cmd, area;
2672     int err;
2673
2674     /* Check arguments */
2675     if (argc < 2)
2676         return EINVAL;
2677
2678     /* Parse command */
2679     if (strcmp_no_case(argv[1], "off") == 0)
2680         cmd = CMD_ALL_OFF;
2681     else if (strcmp_no_case(argv[1], "on") == 0)
2682         cmd = CMD_ALL_ON;
2683     else
2684         return EINVAL;
2685
2686     /* Parse area */
2687     if (max_areas == 0)
2688         area = 0;
2689     else if (max_areas == 1)
2690         area = 1;
2691     else
2692     {
2693     if (argc == 3)
2694         area = atoi(argv[2]);
2695     else
2696         area = 0;
2697     }
2698
2699     /* Check range */
2700     if (area > max_areas) {
2701          printf("error: area not in range from 1 to %d\n", max_areas);
2702          return EINVAL;
2703     }
2704
2705     /* Send command */
2706     if ((err = omni_command(id, cmd, 0, area)) != 0)
2707         return err;
2708
2709     return 0;
2710 }
2711
2712 /********************************************************************************/
2713
2714 int do_counter_cmd(hai_comm_id *id, int argc, char *argv[])
2715 {
2716     int cmd, p1 = 0, p2;
2717     int err;
2718
2719     /* Check arguments */
2720     if ((argc < 3) || (argc > 4))
2721         return EINVAL;
2722
2723     /* Parse counter */
2724     p2 = atoi(argv[1]);
2725
2726     /* Parse command */
2727     if (strcmp_no_case(argv[2], "dec") == 0)
2728         cmd = CMD_COUNTER_DEC;
2729     else if (strcmp_no_case(argv[2], "inc") == 0)
2730         cmd = CMD_COUNTER_INC;
2731     else if (strcmp_no_case(argv[2], "set") == 0)
2732     {
2733         if (argc != 4)
2734             return EINVAL;
2735    
2736         cmd = CMD_COUNTER_SET;
2737         p1 = atoi(argv[3]);
2738     }
2739     else
2740         return EINVAL;
2741
2742     /* Send command */
2743     if ((err = omni_command(id, cmd, p1, p2)) != 0)
2744         return err;
2745
2746     return 0;
2747 }
2748
2749 /********************************************************************************/
2750
2751 int do_alc_cmd(hai_comm_id *id, int argc, char *argv[])
2752 {
2753     int unit, level, p1, p2;
2754     int err;
2755     char *time = NULL;
2756     char *user = NULL;
2757
2758     /* Check arguments */
2759     if (argc < 4)
2760         return EINVAL;
2761
2762     /* Parse unit */
2763     unit = atoi(argv[1]);
2764     if ((unit < 0) || (unit > max_units))
2765         return EINVAL;
2766        
2767     /* Parse level */
2768     level = atoi(argv[2]);
2769     if ((level < 0) || (level > 100))
2770         return EINVAL;
2771
2772     /* Calc p2 */
2773     p2 = (level << 9) | unit;
2774
2775     /* Parse time */
2776     time = argv[3];
2777     if (argc > 4 && strcmp_no_case(time, "user") == 0)
2778          user = argv[4];
2779     if (time != NULL)
2780     {
2781         if (strcmp_no_case(time, "user") == 0)
2782         {
2783              p1 = atoi(user) + CMD_USERSETTING;
2784         }
2785         else
2786         {
2787              p1 = atoi(time);
2788
2789              if (strchr(time, 'm') != 0)
2790         p1 += CMD_MINUTES;
2791              else if (strchr(time, 'h') != 0)
2792         p1 += CMD_HOURS;
2793         }
2794     }
2795
2796     /* Send command */
2797     if ((err = omni_command(id, CMD_ALC, p1, p2)) != 0)
2798         return err;
2799
2800     return 0;
2801 }
2802
2803 /********************************************************************************/
2804
2805 int do_compose_cmd(hai_comm_id *id, int argc, char *argv[])
2806 {
2807     int p1 = 0, p2;
2808     int err;
2809
2810     /* Check arguments */
2811     if ((argc < 3) || (argc > 4))
2812         return EINVAL;
2813
2814     /* Parse unit */
2815     p2 = atoi(argv[1]);
2816
2817     /* Parse command */
2818     if (strcmp_no_case(argv[2], "off") == 0)
2819         p1 = CMD_COMPOSE_OFF;
2820     else if (strcmp_no_case(argv[2], "on") == 0)
2821         p1 = CMD_COMPOSE_ON;
2822     else if (strcmp_no_case(argv[2], "scene") == 0)
2823     {
2824        /* Check arguments */
2825         if (argc != 4)
2826             return EINVAL;
2827
2828        p1 = CMD_COMPOSE_SCENE + *argv[2] - 'A';
2829     }
2830     else
2831         return EINVAL;
2832
2833     /* Send command */
2834     if ((err = omni_command(id, CMD_COMPOSE, p1, p2)) != 0)
2835         return err;
2836
2837     return 0;
2838 }
2839
2840 /********************************************************************************/
2841
2842 int do_upb_cmd(hai_comm_id *id, int argc, char *argv[])
2843 {
2844     int cmd, p1 = 0, p2;
2845     int err;
2846
2847     /* Check arguments */
2848     if ((argc < 3) || (argc > 5))
2849         return EINVAL;
2850
2851     /* Parse unit */
2852     p2 = atoi(argv[1]);
2853
2854     /* Parse command */
2855     if (strcmp_no_case(argv[2], "stat") == 0)
2856         cmd = CMD_UPB_STAT;
2857     else if (strcmp_no_case(argv[2], "link") == 0)
2858     {
2859        /* Check arguments */
2860         if (argc != 4)
2861             return EINVAL;
2862
2863         if (strcmp_no_case(argv[3], "off") == 0)
2864             cmd = CMD_UPB_OFF;
2865         else if (strcmp_no_case(argv[3], "on") == 0)
2866             cmd = CMD_UPB_ON;
2867         else if (strcmp_no_case(argv[3], "set") == 0)
2868             cmd = CMD_UPB_SET;
2869         else
2870             return EINVAL;
2871     }
2872     else if (strcmp_no_case(argv[2], "led") == 0)
2873     {
2874        /* Check arguments */
2875         if (argc != 5)
2876             return EINVAL;
2877
2878         /* Parse LED */
2879         p1 = atoi(argv[3]);
2880
2881        if (strcmp_no_case(argv[4], "off") == 0)
2882             cmd = CMD_UPB_LED_OFF;
2883         else if (strcmp_no_case(argv[4], "on") == 0)
2884             cmd = CMD_UPB_LED_ON;
2885         else
2886             return EINVAL;
2887     }
2888     else
2889         return EINVAL;
2890
2891     /* Send command */
2892     if ((err = omni_command(id, cmd, p1, p2)) != 0)
2893         return err;
2894
2895     return 0;
2896 }
2897
2898 /********************************************************************************/
2899
2900 int do_radiora_cmd(hai_comm_id *id, int argc, char *argv[])
2901 {
2902     int cmd, p2;
2903     int err;
2904
2905     /* Check arguments */
2906     if (argc != 3)
2907         return EINVAL;
2908
2909     /* Parse scene */
2910     p2 = atoi(argv[1]);
2911
2912     /* Parse command */
2913     if (strcmp_no_case(argv[2], "off") == 0)
2914         cmd = CMD_RADIORA_OFF;
2915     else if (strcmp_no_case(argv[2], "on") == 0)
2916         cmd = CMD_RADIORA_ON;
2917     else
2918         return EINVAL;
2919
2920     /* Send command */
2921     if ((err = omni_command(id, cmd, 0, p2)) != 0)
2922         return err;
2923
2924     return 0;
2925 }
2926
2927 /********************************************************************************/
2928
2929 int do_scene_cmd(hai_comm_id *id, int argc, char *argv[])
2930 {
2931     int cmd, p2;
2932     int err;
2933
2934     /* Check arguments */
2935     if (argc != 3)
2936         return EINVAL;
2937
2938     /* Parse scene */
2939     p2 = atoi(argv[1]);
2940
2941     /* Parse command */
2942     if (strcmp_no_case(argv[2], "off") == 0)
2943         cmd = CMD_SCENE_OFF;
2944     else if (strcmp_no_case(argv[2], "on") == 0)
2945         cmd = CMD_SCENE_ON;
2946     else if (strcmp_no_case(argv[2], "set") == 0)
2947         cmd = CMD_SCENE_SET;
2948     else
2949         return EINVAL;
2950
2951     /* Send command */
2952     if ((err = omni_command(id, cmd, 0, p2)) != 0)
2953         return err;
2954
2955     return 0;
2956 }
2957
2958 /********************************************************************************/
2959
2960 int do_arm_cmd(hai_comm_id *id, int argc, char *argv[])
2961 {
2962     int cmd, area;
2963     int err;
2964
2965     /* Check arguments */
2966     if ((argc < 2) || (argc > 3))
2967         return EINVAL;
2968
2969     /* Parse mode */
2970     if ((omni_model == LUMINA) || (omni_model == LUMINAPRO))
2971     {
2972         if (strcmp_no_case(argv[1], "home") == 0)
2973             cmd = CMD_HOME;
2974         else if (strcmp_no_case(argv[1], "sleep") == 0)
2975             cmd = CMD_SLEEP;
2976         else if (strcmp_no_case(argv[1], "away") == 0)
2977             cmd = CMD_AWAY;
2978         else if (strcmp_no_case(argv[1], "vac") == 0)
2979             cmd = CMD_VACATION;
2980         else if (strcmp_no_case(argv[1], "party") == 0)
2981             cmd = CMD_PARTY;
2982         else if (strcmp_no_case(argv[1], "special") == 0)
2983             cmd = CMD_SPECIAL;
2984         else
2985             return EINVAL;
2986     }
2987     else
2988     {
2989     if (strcmp_no_case(argv[1], "off") == 0)
2990         cmd = CMD_DISARM;
2991     else if (strcmp_no_case(argv[1], "disarm") == 0)
2992         cmd = CMD_DISARM;
2993     else if (strcmp_no_case(argv[1], "day") == 0)
2994         cmd = CMD_DAY;
2995     else if (strcmp_no_case(argv[1], "night") == 0)
2996         cmd = CMD_NIGHT;
2997     else if (strcmp_no_case(argv[1], "away") == 0)
2998         cmd = CMD_AWAY;
2999     else if (strcmp_no_case(argv[1], "vac") == 0)
3000         cmd = CMD_VACATION;
3001     else if (strcmp_no_case(argv[1], "dayi") == 0)
3002         cmd = CMD_DAY_INSTANT;
3003     else if (strcmp_no_case(argv[1], "nightd") == 0)
3004         cmd = CMD_NIGHT_DELAYED;
3005     else
3006         return EINVAL;
3007     }
3008
3009     /* Parse area */
3010     if (max_areas == 0)
3011         area = 0;
3012     else if (max_areas == 1)
3013         area = 1;
3014     else {
3015          if (argc == 3) {
3016               area = atoi(argv[2]);
3017               /* Check range */
3018               if (area > max_areas) {
3019                    printf("error: area not in range from 1 to %d\n", max_areas);
3020                    return EINVAL;
3021               }
3022          } else {
3023               area = 0;
3024          }
3025     }
3026     
3027     /* Send command */
3028     if ((err = omni_command(id, cmd, code_index, area)) != 0)
3029         return err;
3030
3031     return 0;
3032 }
3033
3034 /********************************************************************************/
3035
3036 int do_button_cmd(hai_comm_id *id, int argc, char *argv[])
3037 {
3038     int button;
3039     int err;
3040
3041     /* Check arguments */
3042     if (argc != 2)
3043         return EINVAL;
3044
3045     /* Parse button */
3046     button = atoi(argv[1]);
3047
3048     /* Check range */
3049     if ((button < 1) || button > max_buttons_program) {
3050          printf("error: button not in range from 1 to %d\n", max_buttons_program);
3051         return EINVAL;
3052     }
3053
3054     /* Send command */
3055     if ((err = omni_command(id, CMD_BUTTON, 0, button)) != 0)
3056         return err;
3057
3058     return 0;
3059 }
3060
3061 /********************************************************************************/
3062
3063 int do_cost_cmd(hai_comm_id *id, int argc, char *argv[])
3064 {
3065     int p1;
3066     int err;
3067
3068     /* Check arguments */
3069     if (argc != 2)
3070         return EINVAL;
3071
3072     /* Parse cost */
3073     if (strcmp_no_case(argv[1], "low") == 0)
3074         p1 = CMD_COST_LOW;
3075     else if (strcmp_no_case(argv[1], "mid") == 0)
3076         p1 = CMD_COST_MID;
3077     else if (strcmp_no_case(argv[1], "high") == 0)
3078         p1 = CMD_COST_HIGH;
3079     else if (strcmp_no_case(argv[1], "critical") == 0)
3080         p1 = CMD_COST_CRITICAL;
3081     else
3082         return EINVAL;
3083
3084     /* Send command */
3085     if ((err = omni_command(id, CMD_COST, p1, 0)) != 0)
3086         return err;
3087
3088     return 0;
3089 }
3090
3091 /********************************************************************************/
3092
3093 int do_saver_cmd(hai_comm_id *id, int argc, char *argv[])
3094 {
3095     int cmd, p1, p2;
3096     int err;
3097     char *time;
3098
3099     /* Check arguments */
3100     if (argc < 3)
3101         return EINVAL;
3102
3103     /* Parse saver */
3104     p2 = atoi(argv[1]);
3105
3106     /* Parse command */
3107     time = argv[3];
3108     if (strcmp_no_case(argv[2], "off") == 0)
3109         cmd = CMD_SAVER_OFF;
3110     else if (strcmp_no_case(argv[2], "on") == 0)
3111         cmd = CMD_SAVER_ON;
3112     else
3113         return EINVAL;
3114
3115     /* Parse time */
3116     if (time == NULL)
3117         p1 = 0;
3118     else
3119     {
3120         p1 = atoi(time);
3121
3122         if (strchr(time, 'm') != 0)
3123             p1 += CMD_MINUTES;
3124         else if (strchr(time, 'h') != 0)
3125             p1 += CMD_HOURS;
3126     }
3127
3128     /* Send command */
3129     if ((err = omni_command(id, cmd, p1, p2)) != 0)
3130         return err;
3131
3132     return 0;
3133 }
3134
3135 /********************************************************************************/
3136
3137 int do_sensor_cmd(hai_comm_id *id, int argc, char *argv[])
3138 {
3139     int cmd, p1 = 0, p2 = 0;
3140     int err;
3141     const char *units;
3142     double temp;
3143
3144     /* Check arguments */
3145     if (argc != 4)
3146         return EINVAL;
3147
3148     /* Parse device */
3149     if (strcmp_no_case(argv[1], "all") == 0) {
3150         p2 = 0;
3151     } else {
3152         p2 = atoi(argv[1]);
3153         /* Check range */
3154         if ((p2 < 1) || p2 > max_auxs) {
3155              printf("error: sensor not in range from 1 to %d\n", max_auxs);
3156              return EINVAL;
3157         }
3158     }
3159
3160     /* Parse command */
3161     if (strcmp_no_case(argv[2], "low") == 0)
3162         cmd = CMD_THERMO_HEAT;
3163     else if (strcmp_no_case(argv[2], "high") == 0)
3164         cmd = CMD_THERMO_COOL;
3165     else if (strcmp_no_case(argv[2], "heat") == 0)
3166         cmd = CMD_THERMO_HEAT;
3167     else if (strcmp_no_case(argv[2], "cool") == 0)
3168         cmd = CMD_THERMO_COOL;
3169     else
3170         return EINVAL;
3171
3172     /* Parse arguments */
3173 /*     p1 = PUTF(atoi(argv[3])); */ /* RickM */
3174     temp = atof(argv[3]);
3175     units = argv[3] + (strlen(argv[3]) - 1);
3176     /* printf("temp=%f units=%s\n", temp, units); */ /* RickM */
3177     if (!isalpha(units[0]) || strcmp_no_case(units, "f") == 0) {
3178          p1 = (int)(PUTF(temp));
3179     } else if (strcmp_no_case(units, "c") == 0) {
3180          p1 = (int)(PUTC(temp));
3181     } else {
3182          return EINVAL;
3183     }
3184     /* printf("temp=%f units=%s p2=%d tc=%f tf=%f\n", temp, units, p1, (double)GETC(p1), (double)GETF(p1)); */ /* RickM */
3185
3186     /* Send command */
3187     if ((err = omni_command(id, cmd, p1, p2)) != 0)
3188         return err;
3189
3190     return 0;
3191 }
3192
3193 /********************************************************************************/
3194
3195 int do_thermo_cmd(hai_comm_id *id, int argc, char *argv[])
3196 {
3197     int cmd, p1 = 0, p2 = 0;
3198     int err;
3199     const char *units;
3200     double temp;
3201
3202     /* Check arguments */
3203     if (argc != 4)
3204         return EINVAL;
3205
3206     /* Parse device */
3207     if (strcmp_no_case(argv[1], "all") == 0) {
3208         p2 = 0;
3209     } else {
3210         p2 = atoi(argv[1]);
3211         /* Check range */
3212         if ((p2 < 1) || p2 > max_thermos) {
3213              printf("error: thermo not in range from 1 to %d\n", max_thermos);
3214              return EINVAL;
3215         }
3216     }
3217
3218     /* Parse command */
3219     if (strcmp_no_case(argv[2], "mode") == 0)
3220         cmd = CMD_THERMO_MODE;
3221     else if (strcmp_no_case(argv[2], "fan") == 0)
3222         cmd = CMD_THERMO_FAN;
3223     else if (strcmp_no_case(argv[2], "hold") == 0)
3224         cmd = CMD_THERMO_HOLD;
3225     else if (strcmp_no_case(argv[2], "heat") == 0)
3226         cmd = CMD_THERMO_HEAT;
3227     else if (strcmp_no_case(argv[2], "cool") == 0)