1
2 /*----------------------------------------------------------------------------+
3 | |
4 | Enhanced HEYU Functionality |
5 | Copyright 2002-2008 Charles W. Sullivan |
6 | |
7 | |
8 | As used herein, HEYU is a trademark of Daniel B. Suthers. |
9 | X10, CM11A, and ActiveHome are trademarks of X-10 (USA) Inc. |
10 | The author is not affiliated with either entity. |
11 | |
12 | Charles W. Sullivan |
13 | Co-author and Maintainer |
14 | Greensboro, North Carolina |
15 | Email ID: cwsulliv01 |
16 | Email domain: -at- heyu -dot- org |
17 | |
18 +----------------------------------------------------------------------------*/
19
20 /*
21 * This program is free software: you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation, either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33 *
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #if defined(SYSV) || defined(FREEBSD) || defined(OPENBSD)
43 #include <string.h>
44 #else
45 #include <strings.h>
46 #endif
47
48 #if (defined(NSLEEP) || defined(ATTSVR4))
49 #include <sys/time.h>
50 #endif /* NSLEEP | ATTSVR4 */
51
52 #include <syslog.h>
53 #include <time.h>
54 #include <sys/time.h>
55 #include <limits.h>
56 #include "x10.h"
57 #include "process.h"
58 #include "x10state.h"
59
60 #if defined(LINUX)
61 #define RandMax RAND_MAX
62 #elif defined(NETBSD)
63 #define RandMax RANDOM_MAX
64 #elif defined DARWIN
65 #define RandMax LONG_MAX
66 #else
67 #define RandMax LONG_MAX
68 #endif
69
70 extern int tty, tty_aux;
71 extern int line_no;
72 extern int sptty, i_am_relay, i_am_aux, i_am_state;
73 extern int heyu_parent;
74 extern int verbose;
75 extern int xwrite(), xread(), exread(), sxread(), check4poll();
76 extern int is_modem_support(void);
77 extern CONFIG config;
78 extern CONFIG *configp;
79
80 extern struct x10global_st x10global;
81 extern x10hcode_t *x10state;
82
83
84 unsigned char alert_cmd[6];
85 unsigned char alert_ack[3];
86
87 int special_func = 0;
88
89 char *typename[] = {"???", "Scene", "Usersyn"};
90
91 #if 0 /* Reference - Flags for parse_addr() function */
92 #define A_VALID 1
93 #define A_HCODE 2
94 #define A_BMAP 4
95 #define A_PLUS 8
96 #define A_MINUS 16
97 #define A_ALIAS 32
98 #define A_DUMMY 64
99 #define A_MULT 128
100 #define A_STAR 256
101 #endif /* Reference */
102
103 /* Arguments for the various commands */
104 static char *helparg[] = {
105 "[H]", /* 0 HC optional */
106 "H", /* 1 HC only */
107 "Hu", /* 2 HC | single unit */
108 "HU", /* 3 HC | unit string */
109 "HU <level>", /* 4 Dim/Bright level*/
110 "HU <level>", /* 5 Preset dim level*/
111 "<level>", /* 6 Preset dim level only */
112 "HU <level>", /* 7 Extended Preset level*/
113 "<T/F> HU <Data>", /* 8 Arbitrary extended code command (hex)*/
114 "<command ...>", /* 9 Send HC|Funct only, no address */
115 "xx xx xx ...", /* 10 Arbitrary hex bytes */
116 "H <\"text\">", /* 11 Arbitrary quoted text message */
117 "", /* 12 No arguments */
118 "[<command>]", /* 13 For help command */
119 "N.NNN", /* 14 Pause delay */
120 "HU [HU [...]]", /* 15 Multiple addresses */
121 "HU <command>", /* 16 For Turn command */
122 "<query_command>", /* 17 Special functions */
123 "HU <linked>", /* 18 Preset for macros */
124 "H <mode 0-3>", /* 19 Ext status_ack mode */
125 "<\"text\">", /* 20 Message to log file */
126 "NNN", /* 21 Delay in minutes */
127 "H[U] <count>", /* 22 RF Dim/Bright */
128 "xx xx <count>", /* 23 Two hex bytes */
129 "HU <count>", /* 24 RF Dimbo */
130 "H[u]", /* 25 State commands */
131 "n[,n...]", /* 26 Flag set/clear commands */
132 "H[U]", /* 27 Status flag clear command */
133 "HU <byte>", /* 28 Virtual data */
134 "N <hh:mm:ss>", /* 29 Countdown timer */
135 "xxxx xxxx ...", /* 30 Multiple 16-bit hex words */
136 "<parameters>", /* 31 Special for UX17/23 command */
137 "HU g <level>", /* 32 Include in group at level*/
138 "HU g[,g,...]", /* 33 Delete from group(s) */
139 "H G", /* 34 Execute group[.subgroup] functions */
140 "HU G", /* 35 Include in group[.subgroup] */
141 "H g[,g,...]", /* 36 Delete from group(s) */
142 "HU <level> <ramp>", /* 37 Extended Preset level with ramp */
143 "N <count>", /* 38 Set counter */
144 "N", /* 39 Increment or Decrement counter */
145 "[parameters]", /* 40 Arm system */
146 "N [MIN] MAX", /* 41 Random countdown timer */
147 "[MIN] MAX", /* 42 Random delay */
148 };
149
150 static char *options[] = {
151 "Usage: heyu [options] <command> (Run 'heyu help' for commands)\n",
152 " [Options]",
153 " -v Enable verbose mode",
154 " -c <pathname> Specify full configuration file pathname",
155 " -s <pathname> Specify full schedule file pathname",
156 " -0 ... -9 Config file is in subdirectory /0 ... /9",
157 " of standard location, e.g., $HOME/.heyu/3/x10config",
158 };
159
160 /* Descriptions of "administrative" commands */
161 static char *helpadmin[][3] = {
162 {"info","","Display CM11A registers, clock, and upload status"},
163 {"help","[category|command]","This screen [category or command]"},
164 {"help","help","Explain help usage"},
165 {"start","","Start Heyu background processes (daemons)"},
166 {"stop","","Stop Heyu background processes"},
167 {"restart","","Reconfigure running Heyu background processes"},
168 {"engine","","Start the (optional) Heyu state engine daemon"},
169 {"aux","","Start the (optional) Heyu auxiliary RF input daemon"},
170 {"monitor","","Monitor X10 activity (end with <BREAK>)"},
171 {"date","","Return date in date(1) input format"},
172 {"erase","","Zero CM11A EEPROM, erasing all events and macros"},
173 {"syn","","Display built-in synonyms for direct commands"},
174 {"<label>","","Execute a scene or usersyn defined in the config file"},
175 {"show","<option>","(Enter 'heyu show' to see options)"},
176 {"script_ctrl","<option>","Launch scripts disable|enable"},
177 {"initstate","[H[U]]","Zero entire state table or just for address H[U]"},
178 {"initothers","","Zero cumulative address table"},
179 {"reset","[H]","Reset interface to housecode H or default"},
180 {"setclock","","Set CM11A clock to system clock (per schedule)"},
181 {"readclock","","Display CM11A and system clocks (per schedule)"},
182 {"newbattery","","Reset CM11A battery timer to zero"},
183 {"purge","","Cancel pending CM11A delayed macros"},
184 {"clear","","Clear CM11A registers"},
185 {"list","","Display Lock, Spool, and System base directory names"},
186 {"upload","[check|croncheck]","Upload schedule to CM11A or check schedule file"},
187 {"upload","status|cronstatus","Display status of uploaded schedule"},
188 {"catchup","","Emulate uploaded Timers from 0:00 to current time today"},
189 {"trigger","Hu on|off","Emulate Trigger in uploaded schedule"},
190 {"macro","<label>","Emulate Macro in uploaded schedule"},
191 {"utility","<option>","(Enter 'heyu utility' to see options)"},
192 {"logmsg","<\"text\">","Display text message in log file and/or monitor"},
193 {"wait","[timeout]","Wait until macro execution is completed"},
194 {"cm10a_init","","Initialize CM10A interface. (For CM10A only!)"},
195 {"restore_groups","","Restore extended group and xconfig settings"},
196 {"logtail","[N]","Display tail [N lines] of log file"},
197 {"sec_emu","Hu <func> <flags>","Emulate an X10 Security signal"},
198 {"launch","<parameters>","Launch script. Enter 'heyu launch' for usage"},
199 {"conflist","","Display list of all configuration directives"},
200 {"modlist","","Display list of all module types"},
201 {"stateflaglist","","Display list of all state flags"},
202 {"masklist","","Display lists of all environment variable masks"},
203 {"webhook","<parameters>","Enter 'heyu webhook' for usage"},
204 {"version","","Display the Heyu version and exit"},
205 {NULL, NULL, NULL}
206 };
207
208 static char *helpstate[][3] = {
209 {"enginestate","","Display 1 if state engine is running, else 0"},
210 {"armedstate","","Bitmap: 1 = armed, 2 = home, 4 = armpending, 8 = tamper"},
211 {"sensorfault","","Bitmap: 1 = low battery, 2 = inactive, 8 = tamper"},
212 {"flagstate","n","Boolean state of flag n"},
213 {"nightstate","","Boolean state of night flag"},
214 {"darkstate","","Boolean state of dark flag"},
215 {"sunstate","","Bitmap: 1 = night, 2 = dark"},
216 {"spendstate","H[u]","Status-pending bitmap of H or Boolean Hu"},
217 {"onstate","H[u]","On-state bitmap of H or Boolean Hu"},
218 {"offstate","H[u]","On-state complement bitmap of H or Boolean Hu"},
219 {"dimstate","H[u]","Dim-state bitmap of H or Boolean Hu"},
220 {"fullonstate","H[u]","FullOn-state bitmap of H or Boolean Hu"},
221 {"chgstate","H[u]","Changed-state bitmap of H or Boolean Hu"},
222 {"alertstate","H[u]","Alert-state bitmap of H or Boolean Hu"},
223 {"clearstate","H[u]","Clear-state bitmap of H or Boolean Hu"},
224 {"auxalertstate","H[u]","AuxAlert-state bitmap of H or Boolean Hu"},
225 {"auxclearstate","H[u]","AuxClear-state bitmap of H or Boolean Hu"},
226 {"addrstate","H[u]","Addressed-state bitmap of H or Boolean Hu"},
227 {"activestate","H[u]","Active-state bitmap of H or Boolean Hu"},
228 {"inactivestate","H[u]","Inactive-state bitmap of H or Boolean Hu"},
229 {"lobatstate","H[u]","Low_Battery-state bitmap of H or Boolean Hu"},
230 {"validstate","H[u]","Valid function state bitmap of H or Boolean Hu"},
231 {"statestr","H","State mini-bitmaps of all units as ASCII string"},
232 {"dimlevel","Hu","Brighness level of module Hu as 0-100%"},
233 {"rawlevel","Hu","Native level (0-210, 1-32, or 0-63) of module Hu"},
234 {"memlevel","Hu","Stored level 0-100% for module Hu with memory"},
235 {"rawmemlevel","Hu","Stored native level for module Hu with memory"},
236 {"heyu_state","Hu","Heyu script environment state bitmap (as integer)"},
237 {"heyu_rawstate","Hu","Heyu script raw environment state bitmap (as integer)"},
238 {"heyu_vflagstate","Hu","Heyu script vFlag environment state bitmap (as integer)"},
239 #if 0
240 #if defined(HASRFXS) || defined(HASRFXM)
241 {"rfxflag_state","Hu","RFXsensor/RFXmeter flag state bitmap (as integer)"},
242 #endif
243 #ifdef HASORE
244 {"oreflag_state","Hu","Oregon (Electrisave, Owl) flag state bitmap (as integer)"},
245 #endif
246 #ifdef HASDMX
247 {"dmxflag_state","Hu","Digimax flag state bitmap (as integer)"},
248 #endif
249 #endif
250 {"xtend_state","Hu","Xtend script environment state bitmap (as integer)"},
251 {"rcstemp","H","RCS compatible temperature (stored value)"},
252 {"fetchstate","","See man page heyu(1)"},
253 {NULL, NULL, NULL}
254 };
255
256 #ifdef HASRFXS
257 static char *helprfxsensor[][3] = {
258 {"rfxtemp","Hu","Temperature"},
259 {"rfxrh","Hu","Relative Humidity"},
260 {"rfxbp","Hu","Barometric Pressure"},
261 {"rfxpot","Hu","Potentiometer setting"},
262 {"rfxvs","Hu","Supply Voltage"},
263 {"rfxvad","Hu","A/D Voltage"},
264 {"rfxvadi","Hu","Internal A/D Voltage"},
265 {"rfxlobat","Hu","Low Battery state (Boolean)"},
266 {"rfxtemp2","Hu","Second Temperature"},
267 {NULL, NULL, NULL}
268 };
269 #endif /* HASRFXS */
270
271 #ifdef HASRFXM
272 static char *helprfxmeter[][3] = {
273 {"rfxpower","Hu","Watt-Hour meter reading"},
274 {"rfxpanel","[n]","Total Watt-Hours for Power Panel [n]"},
275 {"rfxwater","Hu","Water meter reading"},
276 {"rfxgas","Hu","Gas meter reading"},
277 {"rfxpulse","Hu","Pulse meter reading"},
278 {"rfxcount","Hu","Raw counter of any meter"},
279 {NULL, NULL, NULL}
280 };
281 #endif /* HASRFXM */
282
283 #ifdef HASDMX
284 static char *helpdigimax[][3] = {
285 {"dmxtemp","Hu","DigiMax current temperature (C)"},
286 {"dmxsetpoint","Hu","DigiMax setpoint temperature (C)"},
287 {"dmxstatus","Hu","DigiMax On/Off status (1 = On)"},
288 {"dmxmode","Hu","DigiMax Heat/Cool mode (1 = Heat)"},
289 {NULL, NULL, NULL}
290 };
291 #endif /* HASDMX */
292
293 #ifdef HASORE
294 static char *helporegon[][3] = {
295 {"oretemp","Hu","Oregon sensor Temperature"},
296 {"orerh","Hu","Oregon sensor Relative Humidity"},
297 {"orebp","Hu","Oregon sensor Barometric Pressure"},
298 {"orewindavsp","Hu","Oregon sensor Wind Average Speed"},
299 {"orewindsp","Hu","Oregon sensor Wind Instantaneous Speed"},
300 {"orewinddir","Hu","Oregon sensor Wind Direction"},
301 {"orerainrate","Hu","Oregon sensor Rainfall Rate"},
302 {"oreraintot","Hu","Oregon sensor Total Rainfall"},
303 {"ore_emu","Hu <func> <data>","Enter data in Oregon emulation module"},
304 {"elscurr","Hu","Electrisave CM113 Current"},
305 {"owlpower","Hu","Owl CM119 Power"},
306 {"owlenergy","Hu","Owl CM119 Energy"},
307 {NULL, NULL, NULL}
308 };
309 #endif /* HASORE */
310
311 /* Descriptions of direct and uploaded macro commands */
312 static char *helpdirect[] = {
313 /* 0 */ "Turn units ON",
314 /* 1 */ "Turn units OFF",
315 /* 2 */ "Turn All Lights ON",
316 /* 3 */ "Turn All Lights OFF (**)",
317 /* 4 */ "Turn All Units OFF",
318 /* 5 */ "Dim units by <level> (1-22)",
319 /* 6 */ "Dim units by <level> (1-22) after full bright",
320 /* 7 */ "Brighten units by <level> (1-22)",
321 /* 8 */ "Brighten units by <level> (1-22) after full bright",
322 /* 9 */ "Request ON/OFF status (two-way modules)",
323 /* 10 */ "Status Acknowledge ON",
324 /* 11 */ "Status Acknowledge OFF",
325 /* 12 */ "Preset units to <level> (1-32)",
326 /* 13 */ "Preset to <level> (1-32) (function only)",
327 /* 14 */ "Extended Preset <level> (0-63) (LM14A)",
328 /* 15 */ "Extended All Units ON (LM14A)",
329 /* 16 */ "Extended All Units OFF (LM14A)",
330 /* 17 */ "Extended Status Request (LM14A)",
331 /* 18 */ "Extended Status Acknowledge",
332 /* 19 */ "Extended command - general",
333 /* 20 */ "Extended command - general, await ack",
334 /* 21 */ "Send arbitrary hex bytes as addresses",
335 /* 22 */ "Send Housecode|Units addresses only",
336 /* 23 */ "Send command function only",
337 /* 24 */ "Hail other devices",
338 /* 25 */ "Hail Acknowledge",
339 /* 26 */ "Send quoted text (special protocol)",
340 /* 27 */ "Data Transfer (function code 0xC)",
341 /* 28 */ "Send All_Units_Off to All Housecodes",
342 /* 29 */ "Extended Turn Units Full ON (LM14A)",
343 /* 30 */ "Extended Turn Units Full OFF (LM14A)",
344 /* 31 */ "This screen, or specific command help",
345 /* 32 */ "Display synonyms for direct commands",
346 /* 33 */ "Hail other devices, await ack",
347 /* 34 */ "Legacy status command as above",
348 /* 35 */ "Legacy preset command as above",
349 /* 36 */ "Pause for N.NNN seconds",
350 /* 37 */ "Turn Units 1-16 ON",
351 /* 38 */ "Change state on|off|up|down [vv]",
352 /* 39 */ "Legacy turn command as above",
353 /* 40 */ "Request temperature (RCS compatible)",
354 /* 41 */ "Send bytes directly to interface",
355 /* 42 */ "Limited Preset for macros - see manpage",
356 /* 43 */ "Extended config auto status mode (LM14A)",
357 /* 44 */ "Request RCS compatible status",
358 /* 45 */ "Delay for NNN minutes, NNN = 0-240",
359 /* 46 */ "Pause until next tick of system clock",
360 /* 47 */ "Transmit RF On",
361 /* 48 */ "Transmit RF Off",
362 /* 49 */ "Transmit RF Dims [after On]",
363 /* 50 */ "Transmit RF Brights [after On]",
364 /* 51 */ "Transmit RF All Lights On",
365 /* 51 */ "Transmit RF All Lights Off (**)",
366 /* 53 */ "Transmit RF All Units Off",
367 /* 54 */ "Transmit RF Arbitrary 2-byte hex code",
368 /* 55 */ "Reset CM17A device",
369 /* 56 */ "Transmit RF Dims after Off",
370 /* 57 */ "Transmit RF Dims [after On] (#)",
371 /* 58 */ "Transmit RF Brights [after On] (#)",
372 /* 59 */ "Transmit RF Dims after Off (#)",
373 /* 60 */ "Transmit RF Arbitrary 2-byte hex code (#)",
374 /* 61 */ "Open shutter to level (0-25), enforce limit",
375 /* 62 */ "Set limit (0-25) and open shutter to limit",
376 /* 63 */ "Open shutter to level (0-25) and cancel limit",
377 /* 64 */ "Open all shutters fully and cancel limit",
378 /* 65 */ "Close all shutters fully",
379 /* 66 */ "Extended module power-up signal (LM14A)",
380 /* 67 */ "Set one or more flags (@)",
381 /* 68 */ "Clear one or more flags (@)",
382 /* 69 */ "Clear status-pending flags (@)",
383 /* 70 */ "Write data (0-255) to virtual module (@)",
384 /* 71 */ "Set countdown timer N to hh:mm:ss (@)",
385 /* 72 */ "Reset all countdown timers to zero (@)",
386 /* 73 */ "Transmit RF multiple 16-bit hex words",
387 /* 74 */ "Clear security tamper flags (@)",
388 /* 75 */ "Transmit RF multiple 16-bit hex words (#)",
389 /* 76 */ "Special for UX23A/UX17A - see manpage",
390 /* 77 */ "Special for UX23A/UX17A - see manpage (#)",
391 /* 78 */ "Sleep for N.NNN seconds",
392 /* 79 */ "Include in Group (G = 0-3) at current level",
393 /* 80 */ "Remove HU from one or more Groups (g = 0-3)",
394 /* 81 */ "Execute extended Group G function (LM14A)",
395 /* 82 */ "Extended Group G Status Request (LM14A)",
396 /* 83 */ "Remove H from one or more Groups (g = 0-3)",
397 /* 84 */ "Include in Group (g = 0-3) at <level>",
398 /* 85 */ "Extended Group G Off (**)",
399 /* 86 */ "Extended Group G Dim one level (**)",
400 /* 87 */ "Extended Group G Bright one level (**)",
401 /* 88 */ "Extended Preset <level> (0-63) <ramp> (0-3)",
402 /* 89 */ "Set counter N to <count> (0-64K) (@)",
403 /* 90 */ "Increment counter N by 1 (@)",
404 /* 91 */ "Decrement counter N by 1 (@)",
405 /* 92 */ "Decrement counter N and skip if Zero (@)",
406 /* 93 */ "Decrement counter N and skip if Greater than Zero (@)",
407 /* 94 */ "Arm system [home|away] [min|max] (@)",
408 /* 95 */ "Disarm system (@)",
409 /* 96 */ "Delay random time [MIN-]MAX minutes (0-240)",
410 /* 97 */ "Set timer N to random [MIN-]MAX hh:mm:ss (@)",
411 /* 98 */ "Write mem data (0-255) to virtual module (@)",
412 /* 99 */ "Null command - does nothing (@)",
413 /*100 */ "Dec counter N and skip if Non-Zero or Initial Zero (@)",
414 /*101 */ "Null command - does nothing",
415 /*102 */ "Emulate Status Acknowledge for 1-way modules",
416 };
417
418
display_helpnotes(void)419 void display_helpnotes ( void )
420 {
421 printf("\n (*) Not available for use in uploaded macros.\n");
422 printf(" (**) Many lamp modules do NOT support this command.\n");
423 #ifdef HASCM17A
424 printf(" (#) Fast CM17A command - see man x10cm17a(5) for configuration.\n");
425 #endif
426 printf(" (@) Ignored unless state engine is running.\n");
427
428 return;
429 }
430
display_manpage_list(void)431 void display_manpage_list ( void )
432 {
433 printf("\n Man pages:\n heyu(1), x10config(5), x10sched(5), x10scripts(5), x10aux(5)");
434 #ifdef HASCM17A
435 printf(", x10cm17a(5)");
436 #endif
437 #ifdef HASRFXS
438 printf(", x10rfxsensors(5)");
439 #endif
440 #ifdef HASRFXM
441 printf(", x10rfxmeters(5)");
442 #endif
443 #ifdef HASDMX
444 printf(", x10digimax(5)");
445 #endif
446 #ifdef HASORE
447 printf(", x10oregon(5)");
448 #endif
449 #ifdef HASKAKU
450 printf(", x10kaku(5)");
451 #endif
452 printf(".\n\n");
453 return;
454 }
455
456
457 /* Flags for x10command table below */
458 #define F_ALL 0x00000001 /* "All" command; no unit code required on input */
459 #define F_DED 0x00000002 /* Display extended code data byte in report */
460 #define F_NOA 0x00000004 /* Suppress HC|Unit address byte transmission */
461 #define F_BRI 0x00000008 /* Brighten before dimming */
462 #define F_FAD 0x00000010 /* Force HC|Unit address byte(s) transmission */
463 #define F_ARB 0x00000020 /* Use special format for arbitrary extended code */
464 #define F_NMA 0x00000040 /* Unavailable as an uploadable macro */
465 #define F_HID 0x00000080 /* Hidden command */
466 #define F_STA 0x00000100 /* Status/xStatus Request (await status) */
467 #define F_NUM 0x00000200 /* Arguments are all numbers */
468 #define F_HLP 0x00000400 /* For help menu only - not a CM11A command */
469 #define F_NMAI 0x00000800 /* Unavailable and ignored for uploaded macro */
470 #define F_TRN 0x00001000 /* For Turn command */
471 #define F_SPF 0x00002000 /* For special function processing */
472 #define F_MAC 0x00004000 /* Special version for uploaded macros */
473 #define F_FIR 0x00008000 /* CM17A (Firecracker) command */
474 #define F_FFM 0x00010000 /* CM17A "fast" mode */
475 #define F_SHU 0x00020000 /* Extended Type 0 (Marmitek SW10) shutter commands */
476 #define F_EXT 0x00040000 /* Extended Type 3 (LM14A, AM14A) commands */
477 #define F_FLSC 0x00080000 /* Flag set and clear commands */
478 #define F_GRP 0x00100000 /* Extended code group commands */
479 #define F_ONN 0x00200000 /* On required before brightening */
480 #define F_INT 0x00400000 /* Internal - no PLC interface required */
481 #define F_DUM 0x00800000 /* No PLC interface required */
482
483 #define F_RFS (F_FIR | F_NMA)
484 #define F_RFF (F_FIR | F_NMA | F_FFM)
485 #define F_FLG (F_FLSC | F_NMA | F_NUM)
486 #define F_XGP (F_GRP | F_EXT)
487
488 /*---------------------------------------------------------------------+
489 | Master table of commands and synonyms. The first of any grouping |
490 | having all fields identical except for the label is considered the |
491 | command; subsequent entries in the grouping are treated as built-in |
492 | synonyms. |
493 +---------------------------------------------------------------------*/
494 /* Names for arbitrary extended code functions */
495 #define XARBNAME "xfunc"
496 #define XARBNAMEW "xfuncw"
497
498 static struct cmd_list {
499 char *label;
500 unsigned char code;
501 int length; /* Length as a macro */
502 int minargs;
503 int maxargs;
504 unsigned long flags;
505 unsigned char subcode; /* Type|command for extended code commands */
506 unsigned char subsubcode;
507 unsigned char argtype;
508 unsigned char help;
509 } x10command[] = {
510 {"on", 2, 3, 1, 1, 0, 0, 0, 3, 0 }, /* Simple ON command */
511 {"turnon", 2, 3, 1, 1, 0, 0, 0, 3, 0 }, /* Simple ON command */
512 {"off", 3, 3, 1, 1, 0, 0, 0, 3, 1 }, /* Simple OFF command */
513 {"turnoff", 3, 3, 1, 1, 0, 0, 0, 3, 1 }, /* Simple OFF command */
514 {"bright", 5, 4, 2, 2, 0, 0, 0, 4, 7 }, /* Brighten command */
515 {"turnup", 5, 4, 2, 2, 0, 0, 0, 4, 7 }, /* Brighten command */
516 {"up", 5, 4, 2, 2, 0, 0, 0, 4, 7 }, /* Brighten command */
517 {"brighten", 5, 4, 2, 2, 0, 0, 0, 4, 7 }, /* Brighten command */
518 {"tbright", 5, 4, 2, 2, F_NMA | F_HID, 1, 0, 4, 7 }, /* Brighten command - triggered */
519 {"brightb", 5, 4, 2, 2, F_BRI, 0, 0, 4, 8 }, /* Brighten command with brighten first */
520 {"bbright", 5, 4, 2, 2, F_BRI, 0, 0, 4, 8 }, /* Brighten command with brighten first */
521 {"dim", 4, 4, 2, 2, 0, 0, 0, 4, 5 }, /* Dim command without brighten first */
522 {"turndown", 4, 4, 2, 2, 0, 0, 0, 4, 5 }, /* Dim command without brighten first */
523 {"down", 4, 4, 2, 2, 0, 0, 0, 4, 5 }, /* Dim command without brighten first */
524 {"dimb", 4, 4, 2, 2, F_BRI, 0, 0, 4, 6 }, /* Dim command with brighten first */
525 {"bdim", 4, 4, 2, 2, F_BRI, 0, 0, 4, 6 }, /* Dim command with brighten first */
526 {"obdim", 4, 4, 2, 2, F_BRI | F_ONN, 0, 0, 4, 6 }, /* Dim command with on, brighten first */
527 {"tdim", 4, 4, 2, 2, F_NMA | F_HID, 1, 0, 4, 5 }, /* Dim command - triggered */
528 {"lightson", 1, 3, 1, 1, F_ALL, 0, 0, 1, 2 }, /* All_Lights_On */
529 {"lights_on", 1, 3, 1, 1, F_ALL, 0, 0, 1, 2 }, /* All_Lights_On */
530 {"all_lights_on", 1, 3, 1, 1, F_ALL, 0, 0, 1, 2 }, /* All_Lights_On */
531 {"alllightson", 1, 3, 1, 1, F_ALL, 0, 0, 1, 2 }, /* All_Lights_On */
532 {"lightsoff", 6, 3, 1, 1, F_ALL, 0, 0, 1, 3 }, /* All_Lights_Off */
533 {"lights_off", 6, 3, 1, 1, F_ALL, 0, 0, 1, 3 }, /* All_Lights_Off */
534 {"all_lights_off", 6, 3, 1, 1, F_ALL, 0, 0, 1, 3 }, /* All_Lights_Off */
535 {"alllightsoff", 6, 3, 1, 1, F_ALL, 0, 0, 1, 3 }, /* All_Lights_Off */
536 {"allon", 26,-1, 1, 1, F_ALL, 0, 0, 1,37 }, /* Units 1-16 On */
537 {"all_on", 26,-1, 1, 1, F_ALL, 0, 0, 1,37 }, /* Units 1-16 On */
538 {"all_units_on", 26,-1, 1, 1, F_ALL, 0, 0, 1,37 }, /* Units 1-16 On */
539 {"allunitson", 26,-1, 1, 1, F_ALL, 0, 0, 1,37 }, /* Units 1-16 On */
540 {"units_on", 26,-1, 1, 1, F_ALL, 0, 0, 1,37 }, /* Units 1-16 On */
541 {"unitson", 26,-1, 1, 1, F_ALL, 0, 0, 1,37 }, /* Units 1-16 On */
542 {"alloff", 0, 3, 1, 1, F_ALL, 0, 0, 1, 4 }, /* All_Units_Off */
543 {"all_off", 0, 3, 1, 1, F_ALL, 0, 0, 1, 4 }, /* All_Units_Off */
544 {"all_units_off", 0, 3, 1, 1, F_ALL, 0, 0, 1, 4 }, /* All_Units_Off */
545 {"allunitsoff", 0, 3, 1, 1, F_ALL, 0, 0, 1, 4 }, /* All_Units_Off */
546 {"units_off", 0, 3, 1, 1, F_ALL, 0, 0, 1, 4 }, /* All_Units_Off */
547 {"unitsoff", 0, 3, 1, 1, F_ALL, 0, 0, 1, 4 }, /* All_Units_Off */
548 {"turn", 27,-1, 1,-1, F_TRN, 0, 0,16,38 }, /* Turn command */
549 {"preset", 10, 3, 2, 2, F_NMA, 0, 0, 5,12 }, /* Old Preset (not for macro) */
550 {"preset_dim", 10, 3, 2, 2, F_NMA, 0, 0, 5,12 }, /* Old Preset (not for macro) */
551 {"preset", 11, 3, 2, 2, F_NMA | F_HID, 0, 0, 5,12 }, /* Old Preset (not for macro) */
552 {"preset_dim", 11, 3, 2, 2, F_NMA | F_HID, 0, 0, 5,12 }, /* Old Preset (not for macro) */
553 {"mpreset", 10, 3, 2, 2, F_MAC, 0, 0,18,42 }, /* Limited old Preset for macros */
554 {"macro_preset", 10, 3, 2, 2, F_MAC, 0, 0,18,42 }, /* Limited old Preset for macros */
555 {"mpreset", 11, 3, 2, 2, F_MAC | F_HID, 0, 0,18,42 }, /* Limited old Preset for macros */
556 {"macro_preset", 11, 3, 2, 2, F_MAC | F_HID, 0, 0,18,42 }, /* Limited old Preset for macros */
557 {"preset_level", 10, 3, 1, 1, F_NOA | F_NUM, 0, 0, 6,13 }, /* Old Preset with no address byte */
558 {"presetlevel", 10, 3, 1, 1, F_NOA | F_NUM, 0, 0, 6,13 }, /* Old Preset with no address byte */
559 {"preset_level", 11, 3, 1, 1, F_NOA|F_NUM|F_HID, 0, 0, 6,13 }, /* Old Preset with no address byte */
560 {"presetlevel", 11, 3, 1, 1, F_NOA|F_NUM|F_HID, 0, 0, 6,13 }, /* Old Preset with no address byte */
561 {"status", 15, 3, 1, 1, F_STA, 0, 0, 3, 9 }, /* Status Request */
562 {"status_req", 15, 3, 1, 1, F_STA, 0, 0, 3, 9 }, /* Status Request */
563 {"statusreq", 15, 3, 1, 1, F_STA, 0, 0, 3, 9 }, /* Status Request */
564 {"status_on", 13, 3, 1, 1, F_ALL, 0, 0, 3,10 }, /* Status On */
565 {"statuson", 13, 3, 1, 1, F_ALL, 0, 0, 3,10 }, /* Status On */
566 {"status_off", 14, 3, 1, 1, F_ALL, 0, 0, 3,11 }, /* Status Off */
567 {"statusoff", 14, 3, 1, 1, F_ALL, 0, 0, 3,11 }, /* Status Off */
568 {"status_emu", 48,-1, 1, 1, F_NMA|F_HID, 0, 0, 2,102}, /* Emulate Status Ack */
569 {"hail", 8, 3, 0, 1, F_ALL, 0, 0, 0,24 }, /* Hail Request */
570 {"hail_req", 8, 3, 0, 1, F_ALL, 0, 0, 0,24 }, /* Hail Request */
571 {"hailw", 8, 3, 0, 1, F_ALL|F_STA|F_NMA, 0, 0, 0,33 }, /* Hail Request, wait for ack */
572 {"hail_ack", 9, 3, 0, 1, F_ALL, 0, 0, 0,25 }, /* Hail Acknowledge */
573 {"hailack", 9, 3, 0, 1, F_ALL, 0, 0, 0,25 }, /* Hail Acknowledge */
574 {"data_xfer", 12, 3, 1, 1, F_ALL, 0, 0, 1,27 }, /* Extended data transfer (0xC) */
575 {"dataxfer", 12, 3, 1, 1, F_ALL, 0, 0, 1,27 }, /* Extended data transfer (0xC) */
576
577 {"xon", 7, 6, 1, 1, F_EXT | F_DED, 0xfe, 0, 3,29 }, /* Extended Full ON (LM14A) */
578 {"xoff", 7, 6, 1, 1, F_EXT | F_DED, 0xfd, 0, 3,30 }, /* Extended Full OFF (LM14A) */
579 {"xpreset", 7, 6, 2, 2, F_EXT | F_DED, 0x31, 0, 7,14 }, /* Extended Preset Dim (LM14A) */
580 {"ext_preset", 7, 6, 2, 2, F_EXT | F_DED, 0x31, 0, 7,14 }, /* Extended Preset Dim (LM14A) */
581 {"xdim", 7, 6, 2, 2, F_EXT | F_DED, 0x31, 0, 7,14 }, /* Extended Preset Dim (LM14A) */
582 {"xpresetramp", 7, 6, 3, 3, F_EXT|F_DED|F_HID|F_NMA, 0x31, 0,37,88 }, /* Extended Preset Dim with ramp */
583 {"xallon", 7, 6, 1, 1, F_EXT | F_ALL, 0x33, 0, 1,15 }, /* Extended All Units On */
584 {"xall_on", 7, 6, 1, 1, F_EXT | F_ALL, 0x33, 0, 1,15 }, /* Extended All Units On */
585 {"ext_all_on", 7, 6, 1, 1, F_EXT | F_ALL, 0x33, 0, 1,15 }, /* Extended All Units On */
586 {"xalloff", 7, 6, 1, 1, F_EXT | F_ALL, 0x34, 0, 1,16 }, /* Extended All Units Off */
587 {"xall_off", 7, 6, 1, 1, F_EXT | F_ALL, 0x34, 0, 1,16 }, /* Extended All Units Off */
588 {"ext_all_off", 7, 6, 1, 1, F_EXT | F_ALL, 0x34, 0, 1,16 }, /* Extended All Units Off */
589 {"xstatus", 7, 6, 1, 1, F_EXT | F_STA, 0x37, 0, 3,17 }, /* Extended Status Request */
590 {"ext_status", 7, 6, 1, 1, F_EXT | F_STA, 0x37, 0, 3,17 }, /* Extended Status Request */
591 {"ext_status_req", 7, 6, 1, 1, F_EXT | F_STA, 0x37, 0, 3,17 }, /* Extended Status Request */
592 {"xconfig", 7, 6, 2, 2, F_EXT|F_ALL|F_DED, 0x3b, 0,19,43 }, /* Extended Auto Status Mode */
593 {"xpowerup", 7, 6, 1, 1, F_EXT, 0x37, 1, 3,66 }, /* Extended Module PowerUp */
594 {"xpup", 7, 6, 1, 1, F_EXT, 0x37, 1, 3,66 }, /* Extended Module PowerUp */
595 {"xgrpadd", 7, 6, 2, 2, F_XGP, 0x30, 0,35,79 }, /* Extended add to group */
596 {"xga", 7, 6, 2, 2, F_XGP, 0x30, 0,35,79 }, /* Extended add to group */
597 {"xgrpaddlvl", 7, 6, 3, 3, F_XGP, 0x32, 0,32,84 }, /* Extended add to group at level */
598 {"xgal", 7, 6, 3, 3, F_XGP, 0x32, 0,32,84 }, /* Extended add to group at level */
599 {"xgrprem", 7, 6, 2, 2, F_XGP, 0x35, 0,33,80 }, /* Extended remove from group(s) */
600 {"xgr", 7, 6, 2, 2, F_XGP, 0x35, 0,33,80 }, /* Extended remove from group(s) */
601 {"xgrpremall", 7, 6, 2, 2, F_XGP | F_ALL, 0x35, 3,36,83 }, /* Extended delete from group(s) */
602 {"xgra", 7, 6, 2, 2, F_XGP | F_ALL, 0x35, 3,36,83 }, /* Extended delete from group(s) */
603 {"xgrpexec", 7, 6, 2, 2, F_XGP | F_ALL, 0x36, 0,34,81 }, /* Extended delete from group */
604 {"xgx", 7, 6, 2, 2, F_XGP | F_ALL, 0x36, 0,34,81 }, /* Extended delete from group */
605 {"xgrpexec", 7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x36, 2,34,81 }, /* (Needed for report.txt) */
606 {"xgrpstatus", 7, 6, 2, 2, F_XGP | F_STA, 0x37, 2,35,82 }, /* Extended group status request */
607 {"xgs", 7, 6, 2, 2, F_XGP | F_STA, 0x37, 2,35,82 }, /* Extended group status request */
608 {"xgrpstatusreq", 7, 6, 2, 2, F_XGP | F_STA, 0x37, 2,35,82 }, /* Extended group status request */
609 {"xgrpstatus", 7, 6, 2, 2, F_XGP|F_STA|F_HID, 0x37, 3,35,82 }, /* (Needed for report.txt) */
610 {"xgrpoff", 7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x36, 1,34,85 }, /* Ext group Off - maybe!!!*/
611 {"xgrpdim", 7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 0,34,86 }, /* Ext group Dim */
612 {"xgrpdim", 7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 2,34,86 }, /* (Needed for report.txt) */
613 {"xgrpbright", 7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 1,34,87 }, /* Ext group Bright */
614 {"xgrpbright", 7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 3,34,87 }, /* (Needed for report.txt) */
615 { XARBNAME, 7, 6, 3, 3, F_ARB, 0xff, 0, 8,19 }, /* Extended code - arbitrary */
616 { XARBNAMEW, 7, 6, 3, 3, F_ARB|F_STA|F_NMA, 0xff, 0,8,20 }, /* Extended Status Req - arbitrary */
617
618 {"address", 20,-1, 1,-1, F_NMA, 0, 0,15,22 }, /* Address bytes only (*)*/
619 {"function", 21,-1, 1,-1, F_NOA, 0, 0, 9,23 }, /* HC|Func byte only */
620 {"kill_all_hc", 24,-1, 0, 0, 0, 0, 0,12,28 }, /* Send All_Units_Off to all housecodes */
621 {"pause", 25,-1, 1, 1, F_NMA|F_NUM|F_DUM, 0, 0,14,36 }, /* Pause seconds (locked) */
622 {"sleep", 41,-1, 1, 1, F_NMA|F_NUM|F_DUM, 0, 0,14,78 }, /* Sleep seconds (unlocked) */
623 {"delay", 32,-1, 1, 1, F_NMA|F_NUM|F_DUM, 0, 0,21,45 }, /* Delay minutes (unlocked) */
624 {"rdelay", 45,-1, 1, 2, F_NMA|F_NUM|F_DUM, 0, 0,42,96 }, /* Random delay minutes (unlocked) */
625 {"rcs_req", 28,-1, 1,-1, F_SPF | F_NMA, 0, 0,17,44 }, /* RCS-compatible general status query */
626 {"temp_req", 28,-1, 1,-1, F_SPF | F_NMA, 0, 0,17,40 }, /* RCS-compatible temperature query */
627 {"vdata", 37,-1, 2, 2, F_NMA|F_DUM, 0, 0,28,70 }, /* Virtual data */
628 {"vdatam", 37,-1, 2, 2, F_NMA|F_DUM, 1, 0,28,98 }, /* Virtual memory data */
629
630 {"command2cm11a", 31,-1, 1,-1, F_NMA|F_NUM|F_HID, 0, 0,10,41 }, /* Send bytes directly to interface */
631 {"bytes2cm11a", 29,-1, 1,-1, F_NMA|F_NUM|F_HID, 0, 0,10,41 }, /* Send bytes directly to interface */
632 {"sendbytes", 22,-1, 1,-1, F_NMA|F_NUM|F_HID, 0, 0,10,21 }, /* Send arbitrary hex bytes (*)*/
633 {"sendtext", 23,-1, 2, 2, F_NMA|F_ALL|F_HID, 0, 0,11,26 }, /* Send (quoted) text message (*)*/
634 {"pausetick", 33,-1, 0, 0, F_NMA|F_HID|F_DUM, 0, 0,12,46 }, /* Pause until next tick of sys clock */
635
636 {"arm", 43,-1, 0, 4, F_NMA|F_NUM|F_DUM, SETFLAG, 0,40,94 }, /* Arm system */
637 {"disarm", 43,-1, 0, 0, F_NMA|F_NUM|F_DUM, CLRFLAG, 0,12,95 }, /* Disarm system */
638
639 {"setflag", 35,-1, 1, 1, F_FLG|F_DUM, SETFLAG, 0,26,67 }, /* Set software flags */
640 {"setflags", 35,-1, 1, 1, F_FLG|F_DUM, SETFLAG, 0,26,67 }, /* Set software flags */
641 {"clrflag", 35,-1, 1, 1, F_FLG|F_DUM, CLRFLAG, 0,26,68 }, /* Clear software flags */
642 {"clrflags", 35,-1, 1, 1, F_FLG|F_DUM, CLRFLAG, 0,26,68 }, /* Clear software flags */
643 {"settimer", 38,-1, 2, 2, F_NMA|F_NUM|F_DUM, 0, 0,29,71 }, /* Set countdown timer */
644 {"setrtimer", 46,-1, 2, 3, F_NMA|F_NUM|F_DUM, 0, 0,41,97 }, /* Set random countdown timer */
645 {"clrtimers", 39,-1, 0, 0, F_NMA|F_NUM|F_DUM, 0, 0,12,72 }, /* Reset all timers to zero */
646 {"clrspend", 36,-1, 1, 1, F_NMA|F_DUM, 0, 0,27,69 }, /* Clear status flags */
647 {"clrstatus", 36,-1, 1, 1, F_NMA|F_DUM, 0, 0,27,69 }, /* Clear status flags */
648 {"clrtamper", 40,-1, 0, 0, F_NMA|F_NUM|F_DUM, 0, 0,12,74 }, /* Clear tamper flags */
649
650 {"setcount", 42,-1, 2, 2, F_NMA|F_NUM|F_DUM, CNT_SET, 0,38,89 }, /* Set counter value */
651 {"setc", 42,-1, 2, 2, F_NMA|F_NUM|F_DUM, CNT_SET, 0,38,89 }, /* Set counter value */
652 {"inccount", 42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_INC, 0,39,90 }, /* Increment counter value */
653 {"incc", 42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_INC, 0,39,90 }, /* Increment counter value */
654 {"deccount", 42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_DEC, 0,39,91 }, /* Decrement counter value */
655 {"decc", 42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_DEC, 0,39,91 }, /* Decrement counter value */
656 {"null", 47,-1, 0, 0, F_NMA|F_DUM, 0, 0, 12,101 }, /* Null, does nothing */
657
658 {"@arm", 43,-1, 0, 4, F_FLG|F_INT, SETFLAG, 0,40,94 }, /* Arm system */
659 {"@disarm", 43,-1, 0, 0, F_FLG|F_INT, CLRFLAG, 0,12,95 }, /* Disarm system */
660
661 {"@setflag", 35,-1, 1, 1, F_FLG|F_INT, SETFLAG, 0,26,67 }, /* Set software flags */
662 {"@setflags", 35,-1, 1, 1, F_FLG|F_INT, SETFLAG, 0,26,67 }, /* Set software flags */
663 {"@setf", 35,-1, 1, 1, F_FLG|F_INT, SETFLAG, 0,26,67 }, /* Set software flags */
664 {"@clrflag", 35,-1, 1, 1, F_FLG|F_INT, CLRFLAG, 0,26,68 }, /* Clear software flags */
665 {"@clrflags", 35,-1, 1, 1, F_FLG|F_INT, CLRFLAG, 0,26,68 }, /* Clear software flags */
666 {"@clrf", 35,-1, 1, 1, F_FLG|F_INT, CLRFLAG, 0,26,68 }, /* Clear software flags */
667 {"@settimer", 38,-1, 2, 2, F_NMA|F_NUM|F_INT, 0, 0,29,71 }, /* Set countdown timer */
668 {"@sett", 38,-1, 2, 2, F_NMA|F_NUM|F_INT, 0, 0,29,71 }, /* Set countdown timer */
669 {"@setrtimer", 46,-1, 2, 3, F_NMA|F_NUM|F_INT, 0, 0,41,97 }, /* Set random countdown timer */
670 {"@clrtimers", 39,-1, 0, 0, F_NMA|F_NUM|F_INT, 0, 0,12,72 }, /* Reset all timers to zero */
671 {"@clrt", 39,-1, 0, 0, F_NMA|F_NUM|F_INT, 0, 0,12,72 }, /* Reset all timers to zero */
672 {"@clrspend", 36,-1, 1, 1, F_NMA|F_INT, 0, 0,27,69 }, /* Clear status pending flags */
673 {"@clrstatus", 36,-1, 1, 1, F_NMA|F_INT, 0, 0,27,69 }, /* Clear status pending flags */
674 {"@clrs", 36,-1, 1, 1, F_NMA|F_INT, 0, 0,27,69 }, /* Clear status pending flags */
675 {"@vdata", 37,-1, 2, 2, F_NMA|F_INT, 0, 0,28,70 }, /* Virtual data */
676 {"@vdatam", 37,-1, 2, 2, F_NMA|F_INT, 1, 0,28,98 }, /* Virtual memory data */
677
678 {"@setcount", 42,-1, 2, 2, F_NMA|F_NUM|F_INT, CNT_SET, 0,38,89 }, /* Set counter value */
679 {"@setc", 42,-1, 2, 2, F_NMA|F_NUM|F_INT, CNT_SET, 0,38,89 }, /* Set counter value */
680 {"@inccount", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_INC, 0,39,90 }, /* Increment counter value */
681 {"@incc", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_INC, 0,39,90 }, /* Increment counter value */
682 {"@deccount", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DEC, 0,39,91 }, /* Decrement counter value */
683 {"@decc", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DEC, 0,39,91 }, /* Decrement counter value */
684 {"@decskpz", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPZ, 0,39,92 }, /* Dec Skip Z */
685 {"@decskpgz", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPNZ, 0,39,93 }, /* Dec Skip GTZ */
686 {"@decskpnziz", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPNZIZ, 0,39,100 }, /* Dec Skip NZ or Init Z */
687 {"@decskpnz", 42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPNZIZ, 0,39,100 }, /* Dec Skip NZ or Init Z */
688
689 {"@null", 44,-1, 0, 0, F_NMA|F_INT, 0, 0,12,99 }, /* Null command */
690
691
692
693 #ifdef HASEXT0 /* Extended Type 0 (SW10 shutter controller) commands */
694 {"shopen", 7, 6, 2, 2, F_DED | F_SHU, 0x03, 0, 7,63 }, /* Shutter open, ignore limit */
695 {"shopenlim", 7, 6, 2, 2, F_DED | F_SHU, 0x01, 0, 7,61 }, /* Shutter open, enforce limit */
696 {"shsetlim", 7, 6, 2, 2, F_DED | F_SHU, 0x02, 0, 7,62 }, /* Shutter set limit */
697 {"shopenall", 7, 6, 1, 1, F_ALL | F_SHU, 0x04, 0, 1,64 }, /* Shutter all full open */
698 {"shcloseall", 7, 6, 1, 1, F_ALL | F_SHU, 0x0B, 0, 1,65 }, /* Shutter all full close */
699 #endif
700
701 #ifdef HASCM17A /* CM17A ("Firecracker") commands */
702 {"freset", 34,-1, 0, 0, F_RFS, 8, 0,12,55 }, /* CM17A Reset */
703 {"fon", 34,-1, 1, 1, F_RFS, 2, 0, 3,47 }, /* CM17A On RF command */
704 {"foff", 34,-1, 1, 1, F_RFS, 3, 0, 3,48 }, /* CM17A Off RF command */
705 {"fbright", 34,-1, 2, 2, F_RFS, 5, 0,22,50 }, /* CM17A Bright RF command */
706 {"fdim", 34,-1, 2, 2, F_RFS, 4, 0,22,49 }, /* CM17A Dim RF command */
707 {"fdimbo", 34,-1, 2, 2, F_RFS, 7, 0,24,56 }, /* CM17A Dimb RF command */
708 {"flightson", 34,-1, 1, 1, F_RFS | F_ALL, 1, 0, 1,51 }, /* CM17A AllLightsOn RF command */
709 {"flightsoff", 34,-1, 1, 1, F_RFS | F_ALL, 6, 0, 1,52 }, /* CM17A AllLightsOff RF command */
710 {"falloff", 34,-1, 1, 1, F_RFS | F_ALL, 0, 0, 1,53 }, /* CM17A AllUnitsOff RF command */
711 {"farb", 34,-1, 3, 3, F_RFS | F_NUM, 9, 0,23,54 }, /* CM17A Arbitrary RF command */
712 {"farw", 34,-1, 1,-1, F_RFS | F_NUM, 10, 0,30,73 }, /* CM17A Arbit 16 bit RF command */
713 {"flux", 34,-1, 3,-1, F_RFS | F_NUM, 11, 0,31,76 }, /* CM17A Arbit UX17/23 RF command */
714 {"ffbright", 34,-1, 2, 2, F_RFF, 5, 0,22,58 }, /* CM17A Fast Bright RF command */
715 {"ffdim", 34,-1, 2, 2, F_RFF, 4, 0,22,57 }, /* CM17A Fast Dim RF command */
716 {"ffdimbo", 34,-1, 2, 2, F_RFF, 7, 0,24,59 }, /* CM17A Dimb RF command */
717 {"ffarb", 34,-1, 3, 3, F_RFF | F_NUM, 9, 0,23,60 }, /* CM17A Fast Arbit RF command */
718 {"ffarw", 34,-1, 1,-1, F_RFF | F_NUM, 10, 0,30,75 }, /* CM17A Arbit 16 bit RF command */
719 {"fflux", 34,-1, 3,-1, F_RFF | F_NUM, 11, 0,31,77 }, /* CM17A Arbit UX17/23 RF command */
720 #endif
721 };
722 int nx10cmds = sizeof(x10command)/sizeof(struct cmd_list);
723
724
725 /* Function labels for monitor display (must align with enum in process.h) */
726 /* (with fake add-on function "AllOn") */
727
728 char *funclabel[76] = {
729 "AllOff", "LightsOn", "On", "Off", "Dim", "Bright", "LightsOff",
730 "Extended", "Hail", "HailAck", "Preset", "Preset",
731 "DataXfer", "StatusOn", "StatusOff", "StatusReq", "AllOn",
732 "xPowerUp", "vData", "vDataM", "Panic", "Arm", "Disarm",
733 "Alert", "Clear", "Test", "sLightsOn", "sLightsOff", "secTamper",
734 "sDusk", "sDawn", "AkeyOn", "AkeyOff", "BkeyOn", "BkeyOff",
735 "rfxTemp", "rfxTemp2", "rfxRH", "rfxBP", "rfxVad", "rfxPot", "rfxVs",
736 "rfxLoBat", "rfxOther", "rfxPulse", "rfxPower", "rfxWater", "rfxGas",
737 "rfxCount", "dmxTemp", "dmxOn", "dmxOff", "dmxSetpoint",
738 "oreTemp", "oreRH", "oreBP", "oreWgt",
739 "oreWindSp", "oreWindAvSp", "oreWindDir",
740 "oreRainRate", "oreRainTot", "elsCurr", "oreUV",
741 "kOff", "kOn", "kGrpOff", "kGrpOn", "kUnkFunc", "kPreset", "kGrpPreset", "kUnkPresetFunc",
742 "owlPower", "owlEnergy", "Inactive",
743 "_Invalid_",
744 };
745
746
747 char *ext3funclabel[17] = {
748 "", "xPreset", "", "xAllOn", "xAllOff", "", "", "xStatusReq", "xStatusAck",
749 "", "", "xConfig", "", "", "", "", "xPowerUp",
750 };
751
752 char *rfxfunclabel[8] = {
753 "rfxTemp", "rfxTemp2", "rfxHumidity", "rfxPressure", "rfxVad",
754 "rfxVs", "rfxLoBat", "rfxOther",
755 };
756
757
rcs_temp(int function,int predim,char hc,int unit)758 char *rcs_temp ( int function, int predim, char hc, int unit )
759 {
760 static char buffer[80];
761 int temp;
762
763 if ( (function != 10 && function != 11) || unit < 11 ) {
764 buffer[0] = '\0';
765 return (char *)NULL;
766 }
767
768 temp = -60 + (predim - 1) + 32 * (unit - 11);
769 sprintf(buffer, " Temperature =%3d : Location %c\n",
770 temp, toupper((int)hc));
771
772 return buffer;
773 }
774
775 /*---------------------------------------------------------------------+
776 | Return the length of a macro element corresponding to the |
777 | argument command code. |
778 +---------------------------------------------------------------------*/
macro_element_length(unsigned char cmdcode)779 int macro_element_length ( unsigned char cmdcode )
780 {
781 int j, length = -1;
782
783
784 for ( j = 0; j < nx10cmds; j++ ) {
785 if ( x10command[j].code == cmdcode && x10command[j].length > 0 ) {
786 length = x10command[j].length;
787 break;
788 }
789 }
790 return length;
791 }
792
793 /*---------------------------------------------------------------------+
794 | Return 1 if argument command label is in the administrative command |
795 | (help) table, otherwise return 0. |
796 +---------------------------------------------------------------------*/
is_admin_cmd(char * label)797 int is_admin_cmd ( char *label )
798 {
799 int j;
800
801 j = 0;
802 while ( helpadmin[j][0] != NULL ) {
803 if ( strcmp(label, helpadmin[j][0]) == 0 )
804 return 1;
805 j++;
806 }
807 return 0;
808 }
809
810 /*---------------------------------------------------------------------+
811 | Return 1 if argument command label is in the x10command[] table, |
812 | otherwise return 0. |
813 +---------------------------------------------------------------------*/
is_direct_cmd(char * label)814 int is_direct_cmd ( char *label )
815 {
816 int j;
817
818 for ( j = 0; j < nx10cmds; j++ ) {
819 if ( strcmp(label, x10command[j].label) == 0 &&
820 !(x10command[j].flags & F_HLP) )
821 return 1;
822 }
823 return 0;
824 }
825
826 /*---------------------------------------------------------------------+
827 | Return 1 if argument command label is a scene or is in the |
828 | x10command[] table, otherwise return 0. |
829 +---------------------------------------------------------------------*/
is_heyu_cmd(char * label)830 int is_heyu_cmd ( char *label )
831 {
832 if ( strchr(label, ';') || strchr(label, ' ') || strchr(label, '\t') ) {
833 /* Possible compound command */
834 return 1;
835 }
836
837 if ( lookup_scene(configp->scenep, label) >= 0 ) {
838 return 1;
839 }
840
841 if ( is_direct_cmd(label) ) {
842 return 1;
843 }
844
845 return 0;
846 }
847
848
849 /*---------------------------------------------------------------------+
850 | Return a double random number between 0.0 and 1.0 |
851 +---------------------------------------------------------------------*/
random_float(void)852 double random_float ( void )
853 {
854 static int is_seeded;
855 unsigned int seed;
856 struct timeval tvstruct, *tv = &tvstruct;
857
858 if ( !is_seeded ) {
859 gettimeofday(tv, NULL);
860 seed = (unsigned long)tv->tv_sec + (unsigned long)tv->tv_usec;
861 srandom(seed);
862 is_seeded = 1;
863 }
864 return (double)random() / (double)RandMax;
865 }
866
867 /*---------------------------------------------------------------------+
868 | Sleep for argument milliseconds. |
869 +---------------------------------------------------------------------*/
millisleep(long millisec)870 void millisleep( long millisec )
871 {
872 #ifdef NSLEEP
873 struct timestruc_t tspec;
874
875 if ( millisec == 0 )
876 return;
877
878 tspec.tv_sec = millisec / 1000;
879 tspec.tv_nsec = 1000000L * (millisec % 1000);
880
881 while ( nsleep( &tspec, &tspec ) == -1 );
882 #else
883 #ifdef ATTSVR4
884 struct timeval tspec;
885 #else
886 struct timespec tspec;
887 #endif /* ATTSVR4 */
888
889 if ( millisec == 0 )
890 return;
891
892 #ifdef ATTSVR4
893 tspec.tv_sec = millisec / 1000;
894 tspec.tv_usec = 1000 * (millisec % 1000);
895 while ( usleep(tspec.tv_usec) == -1 );
896 #else
897 tspec.tv_sec = millisec / 1000;
898 tspec.tv_nsec = 1000000L * (millisec % 1000);
899 while ( nanosleep( &tspec, &tspec ) == -1 );
900 #endif /* ATTSVR4 */
901 #endif /* NSLEEP */
902
903 return;
904 }
905
906 /*---------------------------------------------------------------------+
907 | Sleep for argument microseconds. |
908 +---------------------------------------------------------------------*/
microsleep(long microsec)909 void microsleep( long microsec )
910 {
911 #ifdef NSLEEP
912 struct timestruc_t tspec;
913
914 if ( microsec == 0 )
915 return;
916
917 tspec.tv_sec = microsec / 1000000L;
918 tspec.tv_nsec = 1000L * (microsec % 1000000L);
919
920 while ( nsleep( &tspec, &tspec ) == -1 );
921 #else
922 #ifdef ATTSVR4
923 struct timeval tspec;
924 #else
925 struct timespec tspec;
926 #endif
927
928 if ( microsec == 0 )
929 return;
930
931 #ifdef ATTSVR4
932 tspec.tv_sec = microsec / 1000000;
933 tspec.tv_usec = microsec % 1000000;
934 while ( usleep(tspec.tv_usec) == -1 );
935 #else
936 tspec.tv_sec = microsec / 1000000L;
937 tspec.tv_nsec = 1000L * (microsec % 1000000L);
938 while ( nanosleep( &tspec, &tspec ) == -1 );
939 #endif /* ATTSVR4 */
940 #endif /* NSLEEP */
941
942 return;
943 }
944
945 /*---------------------------------------------------------------------+
946 | Compare two commands. Return 0 if identical except for label. |
947 +---------------------------------------------------------------------*/
cmp_commands(struct cmd_list * one,struct cmd_list * two)948 static int cmp_commands( struct cmd_list *one, struct cmd_list *two )
949 {
950
951 if ( !one || !two ||
952 one->flags != two->flags ||
953 one->code != two->code ||
954 one->subcode != two->subcode ||
955 one->length != two->length ||
956 one->minargs != two->minargs ||
957 one->maxargs != two->maxargs ||
958 one->argtype != two->argtype ||
959 one->help != two->help )
960 return 1;
961 return 0;
962 }
963
964
965 /*---------------------------------------------------------------------+
966 | Parse the units string. Pass back the X10 units bitmap through the |
967 | argument list. Return 0 if valid bitmap, otherwise write error msg |
968 | to error handler and return 1. Unit 0 is considered a valid unit |
969 | number but doesn't appear in the bitmap unless it's the only unit, |
970 | whereupon the bitmap will be 0. |
971 +---------------------------------------------------------------------*/
parse_units(char * str,unsigned int * bitmap)972 int parse_units ( char *str, unsigned int *bitmap )
973 {
974
975 static int umap[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
976 char buffer[256];
977 char errmsg[128];
978 char *tail, *sp;
979
980 int ulist[17];
981 int j, ustart, unit;
982
983 clear_error_message();
984
985 if ( strcmp(str, "*") == 0 ) {
986 *bitmap = 0xffff;
987 return 0;
988 }
989
990 for ( j = 0; j < 17; j++ )
991 ulist[j] = 0;
992
993 ustart = -1;
994 tail = str;
995 while ( *(get_token(buffer, &tail, "-,", 255)) ) {
996 unit = (int)strtol(buffer, &sp, 10);
997 if ( *sp ) {
998 sprintf(errmsg, "Invalid character '%c' in units list", *sp);
999 store_error_message(errmsg);
1000 return 1;
1001 }
1002
1003 if ( unit < 0 || unit > 16 ) {
1004 sprintf(errmsg, "Unit number outside range 0-16");
1005 store_error_message(errmsg);
1006 return 1;
1007 }
1008
1009 if ( *tail == ',' || *tail == '\0' ) {
1010 if ( ustart > -1 ) {
1011 if ( ustart <= unit ) {
1012 for ( j = ustart; j <= unit; j++ )
1013 ulist[j] = 1;
1014 ustart = -1;
1015 continue;
1016 }
1017 else {
1018 for ( j = unit; j <= ustart; j++ )
1019 ulist[j] = 1;
1020 ustart = -1;
1021 continue;
1022 }
1023 }
1024 else {
1025 ulist[unit] = 1;
1026 continue;
1027 }
1028 }
1029 else {
1030 ustart = unit;
1031 }
1032 }
1033
1034 *bitmap = 0;
1035 for ( j = 0; j < 16; j++ )
1036 *bitmap |= (ulist[j+1]) << umap[j];
1037
1038 return 0;
1039 }
1040
1041 /*---------------------------------------------------------------------+
1042 | Return the number of units represented by a bitmap |
1043 +---------------------------------------------------------------------*/
count_bitmap_units(unsigned int bitmap)1044 int count_bitmap_units ( unsigned int bitmap )
1045 {
1046 int j, mask, count;
1047
1048 mask = 1;
1049 count = 0;
1050 for ( j = 0; j < 16; j++ ) {
1051 if ( bitmap & mask )
1052 count++;
1053 mask = mask << 1;
1054 }
1055 return count;
1056 }
1057
1058 /*---------------------------------------------------------------------+
1059 | Parse the Houscode|Units token, which may be comprised of: |
1060 | Optional '+' or '-' prefix, plus: |
1061 | An ALIAS, or: |
1062 | Housecode letter in range A-P, or '_' for placeholder. |
1063 | Heyu format units string, e.g., 2,3,5-8,10,13-15 |
1064 | Pass back the Housecode letter and X10 units bitmap through the |
1065 | argument list. |
1066 | Return flags indicating the presence of each component: |
1067 | A_VALID Valid address. |
1068 | A_HCODE Housecode A-P has been returned. |
1069 | A_BMAP Non-zero bitmap has been returned |
1070 | A_PLUS Prefix '+' sign |
1071 | A_MINUS Prefix '-' sign |
1072 | A_ALIAS Housecode and bitmap came from an alias in config |
1073 | A_DUMMY Replaceable address, e.g., $1, $3 |
1074 | A_MULT Bitmap contains multiple units. |
1075 | A_STAR Asterisk ('*') for units list. |
1076 +---------------------------------------------------------------------*/
parse_addr(char * token,char * housecode,unsigned int * bitmap)1077 unsigned int parse_addr ( char *token, char *housecode,
1078 unsigned int *bitmap )
1079 {
1080 char errmsg[128];
1081 unsigned int flags = 0;
1082 char *sp;
1083 char hc;
1084
1085 sp = token;
1086
1087 if ( strchr(sp, '$') ) {
1088 /* Supply a fake value that will always pass scene verification */
1089 /* for a parameter containing a positional parameter. */
1090 *housecode = 'A';
1091 *bitmap = 1;
1092 flags = (A_DUMMY | A_VALID | A_HCODE | A_BMAP | A_PLUS);
1093 return flags;
1094 }
1095
1096 if ( *sp == '+' ) {
1097 flags |= A_PLUS;
1098 sp++;
1099 }
1100 else if ( *sp == '-' ) {
1101 flags |= A_MINUS;
1102 sp++;
1103 }
1104
1105 if ( *sp == '\0' ) {
1106 *housecode = '_';
1107 *bitmap = 0;
1108 flags |= A_VALID;
1109 return flags;
1110 }
1111
1112
1113 if ( alias_lookup(sp, housecode, bitmap) >= 0 ) {
1114 flags |= (A_ALIAS | A_HCODE | A_VALID);
1115 if ( *bitmap )
1116 flags |= A_BMAP;
1117 if ( count_bitmap_units(*bitmap) > 1 )
1118 flags |= A_MULT;
1119 return flags;
1120 }
1121
1122 hc = toupper((int)(*sp));
1123
1124 if ( hc == '_' )
1125 hc = configp->housecode;
1126
1127 if ( (hc != '_') && (hc < 'A' || hc > 'P') ) {
1128 *housecode = '_';
1129 *bitmap = 0;
1130 sprintf(errmsg, "Invalid housecode '%c' in HU '%s'", *sp, token);
1131 store_error_message(errmsg);
1132 flags &= ~(A_VALID | A_HCODE);
1133 return flags;
1134 }
1135
1136 *housecode = hc;
1137 if ( hc != '_' )
1138 flags |= A_HCODE;
1139 sp++;
1140
1141 if ( *sp == '\0' ) {
1142 *bitmap = 0;
1143 flags |= A_VALID;
1144 return flags;
1145 }
1146 else if ( *sp == '*' ) {
1147 *bitmap = 0xffff;
1148 flags |= (A_VALID | A_STAR);
1149 return flags;
1150 }
1151 else if ( parse_units(sp, bitmap) != 0 ) {
1152 /* Prefix the message written to error handler */
1153 sprintf(errmsg, "HU '%s': ", token);
1154 add_error_prefix(errmsg);
1155 return 0;
1156 }
1157
1158 if ( *bitmap )
1159 flags |= A_BMAP;
1160 if ( count_bitmap_units(*bitmap) > 1 )
1161 flags |= A_MULT;
1162
1163 flags |= A_VALID;
1164
1165 return flags;
1166 }
1167
1168 /*---------------------------------------------------------------+
1169 | Return the 8-bit checksum of the bytes in argument buffer. |
1170 +---------------------------------------------------------------*/
checksum(unsigned char * buffer,int length)1171 unsigned char checksum( unsigned char *buffer, int length )
1172 {
1173 int j;
1174 unsigned char sum = 0;
1175
1176 for ( j = 0; j < length; j++ )
1177 sum += buffer[j];
1178
1179 return (sum & 0xff);
1180 }
1181
1182
1183 /*---------------------------------------------------------------+
1184 | Create buffers with three two-byte command function codes and |
1185 | their checksums (which must all be different and less than |
1186 | 0x80). These are used to alert the relay that a command with |
1187 | a checksum 0x5A will be immediately following. |
1188 +---------------------------------------------------------------*/
create_alerts(void)1189 void create_alerts ( void )
1190 {
1191 extern unsigned char alert_cmd[6];
1192 extern unsigned char alert_ack[3];
1193
1194 static unsigned char cmdbytes[] = {0x86,0xCC, 0x46,0xCC, 0x86,0xBC};
1195
1196 int j;
1197 unsigned char *sp;
1198
1199 memcpy(alert_cmd, (void *)cmdbytes, 6);
1200
1201 for ( j = 0; j < 3; j++ ) {
1202 sp = alert_cmd + j + j;
1203 alert_ack[j] = checksum(sp, 2);
1204 }
1205 if ( verbose ) {
1206 fprintf(stderr, "Alert acks are 0x%02x, 0x%02x, 0x%02x\n",
1207 alert_ack[0], alert_ack[1], alert_ack[2]);
1208 }
1209
1210 return;
1211 }
1212
1213
1214 /*---------------------------------------------------------------+
1215 | Alert function which sends three function codes in a row |
1216 | without the "WRMI" byte after each. When the relay receives |
1217 | the sequence of three checksums, it is alerted that the next |
1218 | 0x5A it receives is a checksum rather than an incoming signal.|
1219 +---------------------------------------------------------------*/
send_checksum_alert(void)1220 int send_checksum_alert ( void )
1221 {
1222 extern unsigned char alert_cmd[6];
1223 extern unsigned char alert_ack[3];
1224
1225 int k, nread;
1226 char inbuf[16];
1227 int xread();
1228 int ignoret;
1229
1230 send_x10state_command(ST_CHKSUM, 0);
1231
1232 for ( k = 0; k < 3; k++ ) {
1233 ignoret = write(tty, (char *)(alert_cmd + k + k), 2);
1234 nread = exread(sptty, inbuf, 1, 1);
1235 if ( nread != 1 || inbuf[0] != alert_ack[k] ) {
1236 return 1;
1237 }
1238 }
1239 return 0;
1240
1241 fprintf(stderr, "send_checksum_alert() failed.\n");
1242
1243 check4poll(0, 0);
1244 return 1;
1245 }
1246
1247 /*---------------------------------------------------------------+
1248 | Display a message from one of the send_* functions. |
1249 +---------------------------------------------------------------*/
display_send_message(char * message)1250 void display_send_message ( char *message )
1251 {
1252 if ( i_am_aux || i_am_relay )
1253 display_x10state_message(message);
1254 else
1255 fprintf(stderr, "%s\n", message);
1256 return;
1257 }
1258
1259 /*---------------------------------------------------------------+
1260 | Send the contents of the buffer to the interface. |
1261 | Return 0 if successful, 1 otherwise. |
1262 | Argument 'chkoff' - which byte starts the checksum. |
1263 | Argument 'syncmode' - external triggering. |
1264 +---------------------------------------------------------------*/
send_buffer(unsigned char * buffer,int length,int chkoff,int timeout,int syncmode)1265 int send_buffer ( unsigned char *buffer, int length,
1266 int chkoff, int timeout, int syncmode )
1267 {
1268 unsigned char chksum;
1269 unsigned char inbuff[16];
1270 int nread;
1271 char msgbuff[80];
1272 extern int wait_external_trigger(int);
1273
1274 if ( tty == TTY_DUMMY ) {
1275 display_send_message("Command is invalid for dummy TTY.");
1276 return 1;
1277 }
1278
1279 chksum = checksum(buffer + chkoff, length - chkoff);
1280
1281 if ( chksum == 0x5a ) {
1282 if ( verbose )
1283 display_send_message("Alert relay to command with checksum 0x5A");
1284 if ( send_checksum_alert() != 0 )
1285 return 1;
1286 }
1287
1288 (void) xwrite(tty, (char *) buffer, length);
1289
1290 /* get a check sum in reply */
1291
1292 nread = xread(sptty, inbuff, 1, 1);
1293
1294 if ( chksum == inbuff[0] ) {
1295 /* Checksum is OK; tell interface to transmit the code. */
1296 if ( verbose )
1297 display_send_message("Checksum confirmed");
1298
1299 /* Defer sending until the appropriate half-cycle if so */
1300 /* specified (assuming we have the external hardware installed). */
1301 if ( syncmode != NO_SYNC )
1302 wait_external_trigger(syncmode);
1303
1304 (void) xwrite(tty, "\00" , 1); /* WRMI (we really mean it) */
1305 }
1306 else if (!nread) {
1307 if ( verbose )
1308 display_send_message("Checksum NOT confirmed, no response from interface");
1309 syslog(LOG_WARNING, "Checksum NOT confirmed, no response from interface");
1310 return 1;
1311 }
1312 else {
1313 if ( verbose ) {
1314 sprintf(msgbuff, "Checksum %02x NOT confirmed - should be %02x\n",
1315 inbuff[0], chksum);
1316 display_send_message(msgbuff);
1317 }
1318 return 1;
1319 }
1320
1321 if ( (nread = xread(sptty, inbuff, 1, timeout)) >= 1 ) {
1322 if( inbuff[0] == 0x55 ) {
1323 /* interface is ready again */
1324 if ( verbose )
1325 display_send_message("Interface is ready.");
1326 return 0;
1327 }
1328 else if ( nread == 1 && chksum == 0x5a ) {
1329 if ( verbose )
1330 display_send_message("Checksum 0x5a workaround.");
1331 return 0;
1332 }
1333 else if ( nread == 1 && inbuff[0] == 0x5a ) {
1334 /* Incoming command - forget about the 0x55 */
1335 if ( verbose )
1336 display_send_message("send_buffer() interrupted by incoming command");
1337 return 2;
1338 }
1339 else {
1340 sprintf(msgbuff,
1341 "Interface not ready - received %d bytes beginning %02x\n",
1342 nread, inbuff[0]);
1343 display_send_message(msgbuff);
1344 return 1;
1345 }
1346 }
1347
1348 return 1;
1349 }
1350
1351
1352 /*---------------------------------------------------------------+
1353 | Send an encoded housecode|bitmap to the interface. |
1354 | Try up to 3 times. Return 0 if successful, otherwise 1. |
1355 | Argument housecode is a X10 encoded char A-P |
1356 | Argument bitmap is encoded per X10 |
1357 +---------------------------------------------------------------*/
send_address(unsigned char hcode,unsigned int bitmap,int syncmode)1358 int send_address ( unsigned char hcode, unsigned int bitmap,
1359 int syncmode )
1360 {
1361 unsigned int mask;
1362 unsigned char buffer[4];
1363 char msgbuff[80];
1364 int j, k, result = 1;
1365 int count, timeout = 10;
1366 extern int is_ring ( void );
1367
1368 if ( tty == TTY_DUMMY ) {
1369 display_send_message("Command is invalid for dummy TTY.");
1370 return 1;
1371 }
1372
1373 if ( bitmap == 0 )
1374 return 0;
1375
1376 /* Defer sending if Ring Indicator line is asserted, which */
1377 /* indicates there is incoming X10 data. */
1378 if ( configp->check_RI_line == YES ) {
1379 count = 10;
1380 while ( is_ring() > 0 && --count ) {
1381 sleep(1);
1382 if ( verbose && !i_am_relay )
1383 display_send_message("Defer while RI line is asserted.");
1384 }
1385 if ( !count )
1386 display_send_message("RI serial line may be stuck.");
1387 }
1388
1389 if ( busywait(configp->auto_wait) != 0 ) {
1390 display_send_message("Unable to send address bytes - busywait() timeout.");
1391 return 1;
1392 }
1393
1394 /* Inform other processes of parent process */
1395 send_x10state_command(ST_SOURCE, (unsigned char)heyu_parent);
1396
1397 hcode = hcode << 4;
1398
1399 mask = 1;
1400 for ( j = 0; j < 16; j++ ) {
1401 if ( bitmap & mask ) {
1402 buffer[0] = 0x04;
1403 buffer[1] = hcode | j ;
1404
1405 /* Kluge "fix" for checksum 5A problem. */
1406 /* CM11A seems to disregard a bit in the Dim field. */
1407 /* (The only address affected by this is G1) */
1408 if ( checksum(buffer, 2) == 0x5A && configp->fix_5a == YES )
1409 buffer[0] = 0x0C;
1410
1411 if ( verbose ) {
1412 sprintf(msgbuff, "Sending address bytes: %02x %02x\n",
1413 buffer[0], buffer[1]);
1414 display_send_message(msgbuff);
1415 }
1416
1417 for ( k = 0; k < configp->send_retries; k++ ) {
1418 if ( (result = send_buffer(buffer, 2, 0, timeout, syncmode)) == 0 )
1419 break;
1420 sleep(1);
1421 }
1422
1423 /* Delay so many powerline cycles between commands */
1424 millisleep((long)configp->cm11_post_delay);
1425
1426 if ( result != 0 ) {
1427 display_send_message("Unable to send address bytes");
1428 return 1;
1429 }
1430 }
1431
1432 mask = mask << 1;
1433 }
1434
1435 return 0;
1436 }
1437
1438 /*---------------------------------------------------------------+
1439 | Send an X10 command buffer to the interface. Try up to 3 |
1440 | times. Return 0 if sucessful, otherwise 1. |
1441 +---------------------------------------------------------------*/
send_command(unsigned char * buffer,int len,unsigned int showstatus,int syncmode)1442 int send_command ( unsigned char *buffer, int len,
1443 unsigned int showstatus, int syncmode )
1444 {
1445 int j, result = 1;
1446 int count, timeout = 10;
1447 char msgbuff[128];
1448 extern int is_ring ( void );
1449
1450 if ( tty == TTY_DUMMY ) {
1451 display_send_message("Command is invalid for dummy TTY.");
1452 return 1;
1453 }
1454
1455 if ( len == 0 )
1456 return 0;
1457
1458 /* Defer sending if Ring Indicator line is asserted, which */
1459 /* indicates there is incoming X10 data. */
1460 if ( !i_am_aux && configp->check_RI_line == YES ) {
1461 count = 10;
1462 while ( is_ring() > 0 && --count ) {
1463 sleep(1);
1464 if ( verbose && !i_am_relay )
1465 display_send_message("Defer while RI line is asserted.");
1466 }
1467 if ( !count )
1468 display_send_message("RI serial line may be stuck.");
1469 }
1470
1471 if ( busywait(configp->auto_wait) != 0 ) {
1472 display_send_message("Unable to send command bytes - busywait() timeout.");
1473 return 1;
1474 }
1475
1476 /* Inform other processes of parent process */
1477 send_x10state_command(ST_SOURCE, (unsigned char)heyu_parent);
1478
1479 if ( verbose ) {
1480 sprintf(msgbuff, "Sending command bytes:");
1481 for ( j = 0; j < len; j++ )
1482 sprintf(msgbuff + strlen(msgbuff), " %02x", buffer[j]);
1483 display_send_message(msgbuff);
1484 }
1485
1486 for ( j = 0; j < configp->send_retries; j++ ) {
1487 if ( (result = send_buffer(buffer, len, 0, timeout, syncmode)) == 0 ||
1488 result == 2 ) {
1489 break;
1490 }
1491 sleep(1);
1492 }
1493 if ( result != 0 ) {
1494 display_send_message("Unable to send command bytes");
1495 return 1;
1496 }
1497
1498 if ( special_func > 0 ) {
1499 /* Command has called for special processing of */
1500 /* reply from module, e.g, decoding of preset */
1501 /* reply from TempLinc into temperature */
1502 for ( j = 0; j < configp->spf_timeout; j++ ) {
1503 check4poll(0, 1);
1504 }
1505 }
1506 else if ( showstatus ) {
1507 /* Display status if expected */
1508 for ( j = 0; j < configp->status_timeout; j++ ) {
1509 check4poll(1, 1);
1510 }
1511 }
1512 else {
1513 check4poll(0, 0);
1514 }
1515
1516 /* Delay so many powerline cycles between commands */
1517 millisleep((long)configp->cm11_post_delay);
1518
1519 if ( verbose )
1520 display_send_message("Transmission OK");
1521
1522 return 0;
1523
1524 }
1525
1526 /*---------------------------------------------------------------+
1527 | Disable the CM11A from asserting the RI serial line. |
1528 +---------------------------------------------------------------*/
ri_disable(void)1529 int ri_disable ( void )
1530 {
1531 unsigned char buf[2];
1532
1533 buf[0] = 0xdb;
1534
1535 return send_buffer(buf, 1, 0, 1, 0);
1536 }
1537
1538
1539 /*---------------------------------------------------------------+
1540 | Enable the CM11A to assert the RI serial line. |
1541 +---------------------------------------------------------------*/
ri_enable(void)1542 int ri_enable ( void )
1543 {
1544 unsigned char buf[2];
1545
1546 buf[0] = 0xeb;
1547
1548 return send_buffer(buf, 1, 0, 1, 0);
1549 }
1550
1551
1552 /*---------------------------------------------------------------+
1553 | Send an RI control command, either enable or disable, |
1554 | depending on the config directive RING_CTRL. |
1555 +---------------------------------------------------------------*/
ri_control(void)1556 int ri_control ( void )
1557 {
1558 unsigned char buf[2];
1559
1560 buf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb ;
1561
1562 return send_buffer(buf, 1, 0, 1, 0);
1563 }
1564
1565 /*---------------------------------------------------------------+
1566 | Send an RI control command from the Heyu relay, either enable |
1567 | or disable, depending on the config directive RING_CTRL. |
1568 +---------------------------------------------------------------*/
relay_ri_control(void)1569 int relay_ri_control ( void )
1570 {
1571 int j, nread;
1572 unsigned char outbuf[2], inbuf[2];
1573
1574 int ignoret;
1575
1576 outbuf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb;
1577
1578 for ( j = 0; j < 3; j++ ) {
1579 ignoret = write(tty, (char *) outbuf, 1);
1580 nread = sxread(tty, inbuf, 1, 1);
1581
1582 if ( nread != 1 || inbuf[0] != outbuf[0] ) {
1583 continue;
1584 }
1585
1586 ignoret = write(tty, "\0", 1); /* WRMI */
1587 nread = sxread(tty, inbuf, 1, 1);
1588
1589 if ( nread == 1 && inbuf[0] == 0x55 ) {
1590 return 0;
1591 }
1592 }
1593
1594 millisleep(SETCLOCK_DELAY);
1595
1596 return 1;
1597 }
1598
1599
1600
1601
1602 /*---------------------------------------------------------------+
1603 | Command to disable the CM11A RI line. |
1604 +---------------------------------------------------------------*/
c_ri_disable(int argc,char * argv[])1605 int c_ri_disable ( int argc, char *argv[] )
1606 {
1607 int j, result = 0;
1608
1609 for ( j = 0; j < 3; j++ ) {
1610 if ( (result = ri_disable()) == 0 )
1611 break;
1612 sleep(1);
1613 }
1614
1615 if ( result == 0 )
1616 printf("CM11A RI serial line disabled.\n");
1617 else
1618 fprintf(stderr, "No response to ri_disable command.\n");
1619
1620 check4poll(0, 0);
1621
1622 return result;
1623 }
1624
1625 /*---------------------------------------------------------------+
1626 | Command to enable the CM11A RI line. |
1627 +---------------------------------------------------------------*/
c_ri_enable(int argc,char * argv[])1628 int c_ri_enable ( int argc, char *argv[] )
1629 {
1630 int j, result = 0;
1631
1632 for ( j = 0; j < 3; j++ ) {
1633 if ( (result = ri_enable()) == 0 )
1634 break;
1635 sleep(1);
1636 }
1637
1638 if ( result == 0 )
1639 printf("CM11A RI serial line enabled.\n");
1640 else
1641 fprintf(stderr, "No response to ri_enable command.\n");
1642
1643 check4poll(0, 0);
1644
1645 return result;
1646 }
1647
1648 /*---------------------------------------------------------------+
1649 | Ping the CM11A retries times. |
1650 | Return 0 if response OK, 1 otherwise. |
1651 +---------------------------------------------------------------*/
relay_ping(int tty,int retries)1652 int relay_ping ( int tty, int retries )
1653 {
1654 int j, nread;
1655 unsigned char outbuf[2], inbuf[2];
1656
1657 int ignoret;
1658
1659 outbuf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb;
1660
1661 for ( j = 0; j < retries; j++ ) {
1662 ignoret = write(tty, (char *) outbuf, 1);
1663 nread = sxread(tty, inbuf, 1, 1);
1664
1665 if ( nread != 1 || inbuf[0] != outbuf[0] ) {
1666 continue;
1667 }
1668
1669 ignoret = write(tty, "\0", 1); /* WRMI */
1670 nread = sxread(tty, inbuf, 1, 1);
1671
1672 if ( nread == 1 && inbuf[0] == 0x55 ) {
1673 return 0;
1674 }
1675 }
1676
1677 millisleep(SETCLOCK_DELAY);
1678
1679 return 1;
1680 }
1681
1682
1683 /*---------------------------------------------------------------+
1684 | Ping the CM11A by sending the RI control command. |
1685 +---------------------------------------------------------------*/
c_ping(int argc,char * argv[])1686 int c_ping ( int argc, char *argv[] )
1687 {
1688 int j, result = 0;
1689
1690 for ( j = 0; j < 3; j++ ) {
1691 if ( (result = ri_control()) == 0 )
1692 break;
1693 sleep(1);
1694 }
1695
1696 if ( result == 0 )
1697 printf("Ping response OK\n");
1698 else
1699 fprintf(stderr, "No response to ping.\n");
1700
1701 check4poll(0, 0);
1702
1703 return result;
1704 }
1705
1706 /*---------------------------------------------------------------+
1707 | Called by c_busywait() below. |
1708 +---------------------------------------------------------------*/
busywait(int timeout)1709 int busywait ( int timeout )
1710 {
1711 int j, nread;
1712 unsigned char buf[2], inbuf[16];
1713 int xread();
1714
1715 int ignoret;
1716
1717 if ( timeout <= 0 )
1718 return 0;
1719
1720 /* RI control byte used for ping */
1721 buf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb ;
1722 buf[1] = 0x00;
1723
1724 send_x10state_command (ST_BUSYWAIT, 1);
1725
1726 for ( j = 0; j < timeout; j++ ) {
1727 ignoret = write(tty, (char *)buf, 1);
1728 nread = xread(sptty, (char *)inbuf, 1, 1);
1729
1730 if ( nread != 1 || inbuf[0] != buf[0] ) {
1731 millisleep(60);
1732 continue;
1733 }
1734 ignoret = write(tty, (char *)(buf + 1), 1);
1735 nread = xread(sptty, (char *)inbuf, 1, 1);
1736
1737 if ( nread == 1 && inbuf[0] == 0x55u ) {
1738 return 0;
1739 }
1740 break;
1741 }
1742
1743 send_x10state_command (ST_BUSYWAIT, 0);
1744 check4poll(0, 0);
1745 return 1;
1746 }
1747
1748 /*---------------------------------------------------------------+
1749 | Wait for CM11A to respond to "pings" or until timeout. |
1750 | Return 1 if timeout or error, otherwise 0. |
1751 +---------------------------------------------------------------*/
c_busywait(int argc,char * argv[])1752 int c_busywait ( int argc, char *argv[] )
1753 {
1754 int timeout;
1755 char *sp;
1756
1757 timeout = 30;
1758
1759 if ( argc > 2 ) {
1760 timeout = (int)strtol(argv[2], &sp, 10);
1761 if ( !strchr(" \t\n", *sp) || timeout < 0 || timeout > 300 ) {
1762 fprintf(stderr, "Invalid %s timeout parameter.\n", argv[1]);
1763 return 1;
1764 }
1765 }
1766 return busywait(timeout);
1767 }
1768
1769 /*---------------------------------------------------------------+
1770 | Display help for engine internal commands. |
1771 +---------------------------------------------------------------*/
display_internal_help(void)1772 void display_internal_help ( void )
1773 {
1774 int j;
1775 int szlbl, szarg, sztot;
1776 char buffer[128];
1777 struct cmd_list *one = NULL, *two;
1778
1779 /* Get max string lengths for formatting output */
1780 szlbl = szarg = sztot = 0;
1781 for ( j = 0; j < nx10cmds; j++ ) {
1782 if ( x10command[j].flags & F_HID || !(x10command[j].flags & F_INT) ) {
1783 continue;
1784 }
1785 sztot = max(sztot, ((int)strlen(x10command[j].label) +
1786 (int)strlen(helparg[x10command[j].argtype])) );
1787 }
1788 for ( j = 0; j < nx10cmds; j++ ) {
1789 if ( x10command[j].flags & F_HID || !(x10command[j].flags & F_INT) ) {
1790 continue;
1791 }
1792 two = &x10command[j];
1793 if ( cmp_commands(one, two) ) {
1794 sprintf(buffer, "%s %s", x10command[j].label, helparg[x10command[j].argtype]);
1795 printf(" %-*s %s%s\n", sztot + 2, buffer,
1796 helpdirect[x10command[j].help],
1797 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
1798 one = two;
1799 }
1800 }
1801 return;
1802 }
1803
1804 /*---------------------------------------------------------------+
1805 | Display help for general command list other than direct or |
1806 | cm17a commands. |
1807 +---------------------------------------------------------------*/
display_gen_help(char * helplist[][3])1808 void display_gen_help ( char *helplist[][3] )
1809 {
1810 int j;
1811 int szlbl, szarg, sztot;
1812 char buffer[128];
1813
1814 /* Get max string lengths for formatting output */
1815 szlbl = szarg = sztot = 0;
1816 j = 0;
1817 while ( helplist[j][0] != NULL ) {
1818 szlbl = max(szlbl, (int)strlen(helplist[j][0]));
1819 szarg = max(szarg, (int)strlen(helplist[j][1]));
1820 sztot = max(sztot, ((int)strlen(helplist[j][0]) +
1821 (int)strlen(helplist[j][1])));
1822 j++;
1823 }
1824
1825 j = 0;
1826 while ( helplist[j][0] != NULL ) {
1827 /* Compact format */
1828 sprintf(buffer, "%s %s", helplist[j][0], helplist[j][1]);
1829 printf(" heyu %-*s %s\n", sztot + 2, buffer, helplist[j][2]);
1830 j++;
1831 }
1832 return;
1833 }
1834
1835 /*---------------------------------------------------------------+
1836 | Display help for argument command, or all commands if empty |
1837 | string. |
1838 +---------------------------------------------------------------*/
1839 #define HELP_OPTIONS 1
1840 #define HELP_ADMIN 2
1841 #define HELP_STATE 4
1842 #define HELP_DIRECT 8
1843 #define HELP_CM17A 16
1844 #define HELP_RFXS 32
1845 #define HELP_RFXM 64
1846 #define HELP_INT 128
1847 #define HELP_SHUTTER 256
1848 #define HELP_HELP 512
1849 #define HELP_DMX 1024
1850 #define HELP_ORE 2048
1851 #define HELP_ALL (HELP_OPTIONS | HELP_ADMIN | HELP_STATE | HELP_DIRECT | HELP_CM17A | HELP_RFXS | HELP_RFXM | HELP_INT | HELP_SHUTTER | HELP_DMX | HELP_ORE )
1852 #define COMPACT_FORMAT
command_help(char * command)1853 void command_help ( char *command )
1854 {
1855 int j, ext0done = 0;
1856 int szlbl, szarg, sztot, list = 0;
1857 struct cmd_list *one = NULL, *two;
1858 char buffer[256];
1859
1860 strlower(command);
1861 list = (*command == '\0') ? HELP_ALL :
1862 (!strcmp(command, "help")) ? HELP_HELP :
1863 (!strncmp(command, "options", 6)) ? HELP_OPTIONS :
1864 (!strncmp(command, "admin", 5)) ? HELP_ADMIN :
1865 (!strcmp(command, "state")) ? HELP_STATE :
1866 (!strcmp(command, "direct")) ? HELP_DIRECT :
1867 (!strcmp(command, "shutter")) ? HELP_SHUTTER :
1868 (!strncmp(command, "cm17a", 4)) ? HELP_CM17A :
1869 (!strcmp(command, "firecracker")) ? HELP_CM17A :
1870 (!strncmp(command, "rfxsensor", 4)) ? HELP_RFXS :
1871 (!strncmp(command, "rfxmeter", 4)) ? HELP_RFXM :
1872 (!strncmp(command, "digimax", 3)) ? HELP_DMX :
1873 (!strncmp(command, "oregon", 3)) ? HELP_ORE :
1874 (!strncmp(command, "internal", 3)) ? HELP_INT : 0;
1875 #if 0
1876 (!strncmp(command, "rfx", 3)) ? (HELP_RFXS | HELP_RFXM) : 0 ;
1877 #endif
1878
1879 #ifdef HASRFXS
1880 j = 0;
1881 while ( helprfxsensor[j][0] != NULL ) {
1882 if ( strcmp(command, helprfxsensor[j][0]) == 0 ) {
1883 printf("Usage: heyu %s %s %s\n", helprfxsensor[j][0],
1884 helprfxsensor[j][1], helprfxsensor[j][2]);
1885 return;
1886 }
1887 j++;
1888 }
1889 #endif /* HASRFXS */
1890
1891 #ifdef HASRFXM
1892 j = 0;
1893 while ( helprfxmeter[j][0] != NULL ) {
1894 if ( strcmp(command, helprfxmeter[j][0]) == 0 ) {
1895 printf("Usage: heyu %s %s %s\n", helprfxmeter[j][0],
1896 helprfxmeter[j][1], helprfxmeter[j][2]);
1897 return;
1898 }
1899 j++;
1900 }
1901 #endif /* HASRFXM */
1902
1903 #ifdef HASDMX
1904 j = 0;
1905 while ( helpdigimax[j][0] != NULL ) {
1906 if ( strcmp(command, helpdigimax[j][0]) == 0 ) {
1907 printf("Usage: heyu %s %s %s\n", helpdigimax[j][0],
1908 helpdigimax[j][1], helpdigimax[j][2]);
1909 return;
1910 }
1911 j++;
1912 }
1913 #endif /* HASDMX */
1914
1915 #ifdef HASORE
1916 j = 0;
1917 while ( helporegon[j][0] != NULL ) {
1918 if ( strcmp(command, helporegon[j][0]) == 0 ) {
1919 printf("Usage: heyu %s %s %s\n", helporegon[j][0],
1920 helporegon[j][1], helporegon[j][2]);
1921 return;
1922 }
1923 j++;
1924 }
1925 #endif /* HASORE */
1926
1927 if ( list & HELP_OPTIONS ) {
1928 /* Display options */
1929 printf("\n");
1930 for ( j = 0; j < (int)(sizeof(options)/sizeof(char *)); j++ ) {
1931 printf("%s\n", options[j]);
1932 }
1933 }
1934
1935 if ( list & HELP_HELP ) {
1936 printf("\nUsage: heyu help [category|command] All help [or specific Category or Command]\n");
1937 printf(" Categories: options admin direct");
1938 #ifdef HASEXT0
1939 printf(" shutter");
1940 #endif
1941 #ifdef HASCM17A
1942 printf(" cm17a");
1943 #endif
1944 printf(" internal");
1945 printf(" state");
1946 #ifdef HASRFXS
1947 printf(" rfxsensor");
1948 #endif
1949 #ifdef HASRFXM
1950 printf(" rfxmeter");
1951 #endif
1952 #ifdef HASDMX
1953 printf(" digimax");
1954 #endif
1955 #ifdef HASORE
1956 printf(" oregon");
1957 #endif
1958 printf(" webhook");
1959 printf("\n");
1960 }
1961
1962 if ( list & HELP_ADMIN ) {
1963 /* Administrative commands */
1964 printf("\n [Administrative commands (H = Housecode)]\n");
1965 display_gen_help(helpadmin);
1966 }
1967
1968 if ( list & HELP_STATE ) {
1969 /* State commands */
1970 printf("\n [State commands (H = Housecode, u = Single unit) - require heyu engine]\n");
1971 display_gen_help(helpstate);
1972 }
1973
1974 #if HASRFXS
1975 if ( (list & HELP_STATE) || (list & HELP_RFXS) ) {
1976 /* RFXSensor state commands */
1977 printf("\n [RFXSensor state commands (H = Housecode, u = Single unit) - require heyu engine]\n");
1978 display_gen_help(helprfxsensor);
1979 }
1980 #endif /* HASRFXS */
1981
1982 #ifdef HASRFXM
1983 if ( (list & HELP_STATE) || (list & HELP_RFXM) ) {
1984 /* RFXMeter state commands */
1985 printf("\n [RFXMeter state commands (H = Housecode, u = Single unit) - require heyu engine]\n");
1986 display_gen_help(helprfxmeter);
1987 }
1988 #endif /* HASRFXM */
1989
1990 #ifdef HASDMX
1991 if ( (list & HELP_STATE) || (list & HELP_DMX) ) {
1992 /* Digimax state commands */
1993 printf("\n [DigiMax state commands (H = Housecode, u = Single unit) - require heyu engine]\n");
1994 display_gen_help(helpdigimax);
1995 }
1996 #endif /* HASDMX */
1997
1998 #ifdef HASORE
1999 if ( (list & HELP_STATE) || (list & HELP_ORE) ) {
2000 /* Oregon state commands */
2001 printf("\n [Oregon group state commands (H = Housecode, u = Single unit) - require heyu engine]\n");
2002 display_gen_help(helporegon);
2003 }
2004 #endif /* HASORE */
2005
2006 if ( list & HELP_DIRECT ) {
2007 /* Direct commands */
2008 /* Get max string lengths for formatting output */
2009 szlbl = szarg = sztot = 0;
2010 for ( j = 0; j < nx10cmds; j++ ) {
2011 if ( x10command[j].flags & (F_HID | F_INT))
2012 continue;
2013 sztot = max(sztot, ((int)strlen(x10command[j].label) +
2014 (int)strlen(helparg[x10command[j].argtype])) );
2015 }
2016 printf("\n [Direct commands (H = Housecode, U = Units list)]\n");
2017 for ( j = 0; j < nx10cmds; j++ ) {
2018 two = &x10command[j];
2019 if ( x10command[j].flags & (F_HID | F_INT | F_FIR | F_SHU) ) {
2020 /* Command not shown in this menu */
2021 continue;
2022 }
2023 if ( cmp_commands(one, two) ) {
2024 sprintf(buffer, "%s %s", x10command[j].label, helparg[x10command[j].argtype]);
2025 printf(" heyu %-*s %s%s\n", sztot + 2, buffer,
2026 helpdirect[x10command[j].help],
2027 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2028 one = two;
2029 }
2030 }
2031
2032 #ifdef HASEXT0
2033 printf("\n [Shutter (Extended Type 0) direct commands (H = Housecode, U = Units list)]\n");
2034 for ( j = 0; j < nx10cmds; j++ ) {
2035 two = &x10command[j];
2036 if ( !(x10command[j].flags & F_SHU) ) {
2037 continue;
2038 }
2039 if ( cmp_commands(one, two) ) {
2040 sprintf(buffer, "%s %s", x10command[j].label, helparg[x10command[j].argtype]);
2041 printf(" heyu %-*s %s%s\n", sztot + 2, buffer,
2042 helpdirect[x10command[j].help],
2043 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2044 one = two;
2045 }
2046 }
2047 #endif /* End ifdef */
2048 ext0done = 1;
2049 }
2050
2051 #ifdef HASEXT0
2052 if ( list & HELP_SHUTTER && !ext0done) {
2053 printf("\n [Shutter (Extended Type 0) direct commands (H = Housecode, U = Units list)]\n");
2054 /* Get max string lengths for formatting output */
2055 szlbl = szarg = sztot = 0;
2056 for ( j = 0; j < nx10cmds; j++ ) {
2057 if ( x10command[j].flags & F_HID || !(x10command[j].flags & F_SHU) )
2058 continue;
2059 sztot = max(sztot, ((int)strlen(x10command[j].label) +
2060 (int)strlen(helparg[x10command[j].argtype])) );
2061 }
2062
2063 for ( j = 0; j < nx10cmds; j++ ) {
2064 two = &x10command[j];
2065 if ( !(x10command[j].flags & F_SHU) ) {
2066 continue;
2067 }
2068 if ( cmp_commands(one, two) ) {
2069 sprintf(buffer, "%s %s", x10command[j].label, helparg[x10command[j].argtype]);
2070 printf(" heyu %-*s %s%s\n", sztot + 2, buffer,
2071 helpdirect[x10command[j].help],
2072 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2073 one = two;
2074 }
2075 }
2076 }
2077 #endif /* End ifdef */
2078
2079 #ifdef HASCM17A
2080 if ( list & HELP_CM17A ) {
2081 /* Get max string lengths for formatting output */
2082 szlbl = szarg = sztot = 0;
2083 for ( j = 0; j < nx10cmds; j++ ) {
2084 if ( x10command[j].flags & F_HID )
2085 continue;
2086 sztot = max(sztot, ((int)strlen(x10command[j].label) +
2087 (int)strlen(helparg[x10command[j].argtype])) );
2088 }
2089
2090 printf("\n [CM17A \"Firecracker\" commands (H = Housecode, U = Units list)]\n");
2091 for ( j = 0; j < nx10cmds; j++ ) {
2092 two = &x10command[j];
2093 if ( !(x10command[j].flags & F_FIR) ) {
2094 continue;
2095 }
2096 if ( cmp_commands(one, two) ) {
2097 sprintf(buffer, "%s %s", x10command[j].label, helparg[x10command[j].argtype]);
2098 printf(" heyu %-*s %s%s\n", sztot + 2, buffer,
2099 helpdirect[x10command[j].help],
2100 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2101 one = two;
2102 }
2103 }
2104 }
2105 #endif /* End ifdef */
2106
2107 if ( list & HELP_INT ) {
2108 /* Internal engine precommands */
2109 printf("\n [Internal engine precommands (See man pages)]\n");
2110 display_internal_help();
2111 }
2112
2113 if ( list & (HELP_DIRECT | HELP_CM17A) ) {
2114 display_helpnotes();
2115 display_manpage_list();
2116 return;
2117 }
2118 else if ( list > 0 ) {
2119 display_manpage_list();
2120 return;
2121 }
2122 else if ( list == 0 ) {
2123 /* Display specific command */
2124
2125 j = 0;
2126 while ( helpadmin[j][0] != NULL ) {
2127 if ( strcmp(command, helpadmin[j][0]) == 0 ) {
2128 printf("Usage: heyu %s %s %s\n", helpadmin[j][0],
2129 helpadmin[j][1], helpadmin[j][2]);
2130 return;
2131 }
2132 j++;
2133 }
2134
2135 j = 0;
2136 while ( helpstate[j][0] != NULL ) {
2137 if ( strcmp(command, helpstate[j][0]) == 0 ) {
2138 printf("Usage: heyu %s %s %s\n", helpstate[j][0],
2139 helpstate[j][1], helpstate[j][2]);
2140 return;
2141 }
2142 j++;
2143 }
2144
2145 for ( j = 0; j < nx10cmds; j++ ) {
2146 if ( strcmp(command, x10command[j].label) == 0 )
2147 break;
2148 }
2149 if ( j < nx10cmds ) {
2150 printf("Usage: %s%s %s %s%s\n",
2151 (x10command[j].flags & F_INT) ? "" : "heyu ",
2152 x10command[j].label,
2153 helparg[x10command[j].argtype], helpdirect[x10command[j].help],
2154 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2155 }
2156 #if 0
2157 if ( j < nx10cmds ) {
2158 printf("Usage: heyu %s %s %s%s\n", x10command[j].label,
2159 helparg[x10command[j].argtype], helpdirect[x10command[j].help],
2160 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2161 }
2162 #endif
2163 else {
2164 printf("Command '%s' not recognized.\n", command);
2165 return;
2166 }
2167 return;
2168 }
2169 }
2170
2171
2172 /*---------------------------------------------------------------+
2173 | Send a hail_ack (for use only by state engine) |
2174 +---------------------------------------------------------------*/
acknowledge_hail(void)2175 void acknowledge_hail ( void )
2176 {
2177 extern char default_housecode;
2178 extern int heyu_parent;
2179 int save_parent;
2180 unsigned char buf[4];
2181 unsigned char hcode;
2182
2183 hcode = hc2code(default_housecode);
2184
2185 buf[0] = 0x06;
2186 buf[1] = hcode << 4 | 9;
2187
2188 /* Temporarily set heyu_parent so that ack will be sent */
2189 /* as if from a script. */
2190
2191 save_parent = heyu_parent;
2192 heyu_parent = D_ENGINE;
2193 send_command(buf, 2, 0, NO_SYNC);
2194 heyu_parent = save_parent;
2195
2196 return;
2197 }
2198
2199
2200 /*---------------------------------------------------------------+
2201 | Display a list of all commands and their synonyms. |
2202 +---------------------------------------------------------------*/
display_syn(void)2203 void display_syn ( void )
2204 {
2205 int j;
2206 int szlbl;
2207 char delim = ' ';
2208 struct cmd_list *one = NULL, *two;
2209
2210 /* Get max label length for formatting output */
2211 szlbl = 0;
2212 for ( j = 0; j < nx10cmds; j++ ) {
2213 if ( x10command[j].flags & (F_HLP | F_HID) )
2214 continue;
2215 szlbl = max(szlbl, (int)strlen(x10command[j].label));
2216 }
2217
2218 printf(" %-*s %s\n", szlbl, "Command","Built-In Synonyms");
2219 printf(" %-*s %s", szlbl, "-------","-----------------");
2220
2221 for ( j = 0; j < nx10cmds; j++ ) {
2222 two = &x10command[j];
2223 if ( x10command[j].flags & (F_HLP | F_HID) ) {
2224 /* Don't display hidden or purely help commands */
2225 continue;
2226 }
2227 if ( cmp_commands(one, two) ) {
2228 printf("\n %-*s ", szlbl, x10command[j].label);
2229 one = two;
2230 delim = ' ';
2231 }
2232 else {
2233 printf("%c %s", delim, x10command[j].label);
2234 delim = ',';
2235 }
2236 }
2237 printf("\n");
2238 return;
2239 }
2240
2241
2242
cmd_usage(FILE * fd,int index)2243 void cmd_usage ( FILE *fd, int index )
2244 {
2245 fprintf(fd, "Usage: heyu %s %s\n",
2246 x10command[index].label, helparg[x10command[index].argtype]);
2247 }
2248
2249 /*---------------------------------------------------------------+
2250 | Send to the CM11A or merely verify the syntax of the command |
2251 | represented by the argc tokens in list argv[] according to |
2252 | input argument 'mode': |
2253 | mode = CMD_VERIFY Merely verify the syntax |
2254 | mode = CMD_RUN Actually send the command |
2255 | mode = CMD_INT Run internal engine precommand |
2256 | Return 0 if successful; 1 otherwise. |
2257 | (The CMD_VERIFY mode is intended for verifying the syntax |
2258 | of scene commands in the x10config file.) |
2259 +---------------------------------------------------------------*/
2260
direct_command(int argc,char * argv[],int mode)2261 int direct_command( int argc, char *argv[], int mode )
2262 {
2263
2264 #ifdef HASCM17A /* Used only for CM17A commands */
2265 extern unsigned int rf_unit_code[16];
2266 extern unsigned int rf_func_code[7];
2267 extern unsigned int rf_nosw_code[8];
2268 extern int display_rf_xmit(unsigned char, unsigned int, int);
2269 extern int write_cm17a( unsigned int, int, unsigned char );
2270 extern int reset_cm17a( void );
2271 extern void rf_post_delay( void );
2272 extern void rf_farb_delay( void );
2273 extern void rf_farw_delay( void );
2274 extern void rf_flux_delay( long );
2275
2276 int precode;
2277 int bursts, burstgroup, savebursts;
2278 int byte1, byte2;
2279 unsigned int rfword, rfonword, noswitch, nsw;
2280 unsigned char rfhcode, rfmode = RF_SLOW;
2281 long lxdata;
2282 int flux_delay;
2283 #endif /* End of HASCM17A block */
2284
2285 extern int line_sync_mode( void );
2286 extern int send_flag_update ( unsigned int, unsigned char );
2287 extern int send_long_flag_update ( unsigned char, unsigned long, unsigned char );
2288 extern int clear_status_flags ( unsigned char, unsigned int );
2289 extern void send_settimer_msg ( unsigned short, long );
2290 extern int munlock ( char * );
2291 extern int lock_for_write ( void );
2292 extern int parse_ext3_group ( char *, unsigned char *, unsigned char * );
2293 extern int set_counter ( int, unsigned short, unsigned char );
2294 extern void send_setcounter_msg ( int, unsigned short, unsigned char );
2295 extern void engine_update_flags ( unsigned char, unsigned long, unsigned char );
2296 extern void engine_internal_vdata ( unsigned char, unsigned char, unsigned char, unsigned char );
2297 extern int set_globsec_flags ( unsigned char );
2298 extern char *datstrf ( void );
2299 extern char *display_armed_status ( void );
2300 extern int read_x10state_file ( void );
2301 int get_armed_parms ( int, char **, unsigned char *, unsigned char );
2302 extern int line_no;
2303 int j, k, icmd, nargs, radix, subcode, subsubcode;
2304 unsigned int mask, aflags, bitmap, sendmap;
2305 unsigned long flags;
2306 char *label, *sp;
2307 char hc;
2308 char errmsg[128];
2309 int dimval, dimcode, fullbright, timeout;
2310 long delay, mindelay, maxdelay;
2311 double pause;
2312 unsigned char cmdbuf[16], cmdcode, hcode, switchv, switchcode, testbuf[128];
2313 unsigned char vtype;
2314 int minargs, maxargs;
2315 int xdata = 0;
2316 int ramp;
2317 unsigned char group, subgroup;
2318 static char reorder[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
2319 int syncmode;
2320 int timer_no;
2321 long timer_countdown, min_countdown, max_countdown;
2322 int value;
2323 long longvalue;
2324 int counter_no;
2325 unsigned short counter_value;
2326 char writefilename[PATH_LEN + 1];
2327 unsigned char sflags;
2328
2329 int flaglist2banks ( unsigned long *flagbank, int banks, char *str );
2330 unsigned long flagbank[NUM_FLAG_BANKS];
2331
2332 timeout = 10;
2333 line_no = 0;
2334 flags = 0;
2335 special_func = 0;
2336 syncmode = line_sync_mode();
2337
2338 clear_error_message();
2339
2340 /* Handle a few special cases */
2341
2342 if ( strcmp(argv[0], "function") == 0 ) {
2343 /* Special case: "function" command */
2344 if ( argc < 2 ) {
2345 store_error_message("Command 'function': Too few parameters");
2346 return 1;
2347 }
2348 argv++;
2349 argc--;
2350 flags |= F_NOA;
2351 }
2352 else if ( strcmp(argv[0], "turn") == 0 ) {
2353 /* Special case: "turn" command */
2354 if ( argc < 3 ) {
2355 store_error_message("Command 'turn': Too few parameters");
2356 return 1;
2357 }
2358 argv++;
2359 argc--;
2360 flags |= F_TRN;
2361 /* Swap first two parameters */
2362 sp = argv[0];
2363 argv[0] = argv[1];
2364 argv[1] = sp;
2365 }
2366 else if ( strcmp(argv[0], "temp_req") == 0 ||
2367 strcmp(argv[0], "rcs_req") == 0 ) {
2368 if ( argc < 2 ) {
2369 sprintf(errmsg, "Command '%s': Too few parameters", argv[0]);
2370 store_error_message(errmsg);
2371 return 1;
2372 }
2373 special_func = (mode == CMD_VERIFY) ? 0 : 1;
2374 argv++;
2375 argc--;
2376 flags |= F_SPF;
2377 }
2378 nargs = argc - 1;
2379
2380 /* Identify the command */
2381 for ( icmd = 0; icmd < nx10cmds; icmd++ ) {
2382 if ( !strcmp(argv[0], x10command[icmd].label) )
2383 break;
2384 }
2385 if ( icmd == nx10cmds ) {
2386 sprintf(errmsg, "Invalid command '%s'", argv[0]);
2387 store_error_message(errmsg);
2388 return 1;
2389 }
2390
2391 label = x10command[icmd].label;
2392 minargs = x10command[icmd].minargs;
2393 maxargs = x10command[icmd].maxargs;
2394 flags |= x10command[icmd].flags;
2395 cmdcode = x10command[icmd].code;
2396 subcode = x10command[icmd].subcode;
2397 subsubcode = x10command[icmd].subsubcode;
2398 radix = (flags & F_ARB) ? 16 : 10;
2399
2400 if ( flags & F_INT && mode != CMD_INT ) {
2401 sprintf(errmsg, "Invalid use of internal engine precommand '%s'", label);
2402 store_error_message(errmsg);
2403 return 1;
2404 }
2405
2406 if ( (configp->device_type & DEV_DUMMY) && !(flags & (F_INT | F_DUM)) ) {
2407 sprintf(errmsg, "Command '%s' is not valid for TTY dummy", label);
2408 store_error_message(errmsg);
2409 return 1;
2410 }
2411
2412 #ifndef HASCM17A
2413 /* CM17A support not included */
2414 if ( flags & F_FIR ) {
2415 sprintf(errmsg,
2416 "Command '%s': Heyu was compiled without CM17A support.", label);
2417 store_error_message(errmsg);
2418 return 1;
2419 }
2420 #else
2421 if ( flags & F_FIR && mode != CMD_VERIFY && !is_modem_support() ) {
2422 sprintf(errmsg,
2423 "Command '%s': CM17A operation is unsupported by serial port hardware.", label);
2424 store_error_message(errmsg);
2425 return 1;
2426 }
2427 #endif /* End ifndef */
2428
2429 /* Help menu items are invalid in scenes */
2430 if ( flags & F_HLP && mode == CMD_VERIFY ) {
2431 sprintf(errmsg, "Command '%s' is not valid in scenes/usersyns", label);
2432 store_error_message(errmsg);
2433 return 1;
2434 }
2435
2436 /* If this is just a help menu item, return */
2437 if ( flags & F_HLP )
2438 return 0;
2439
2440 /* Only commands with command code <= 6 or code == 26 or 34-7 are valid with "turn" */
2441 if ( flags & F_TRN && !(cmdcode <= 6 || cmdcode == 26 || (cmdcode == 34 && subcode <= 7)) ) {
2442 sprintf(errmsg,
2443 "Command '%s' cannot be used with the 'turn' command", label);
2444 store_error_message(errmsg);
2445 return 1;
2446 }
2447
2448 if ( nargs < minargs ) {
2449 sprintf(errmsg, "Command '%s': Too few parameters", label);
2450 store_error_message(errmsg);
2451 return 1;
2452 }
2453 if ( maxargs != -1 && nargs > maxargs ) {
2454 sprintf(errmsg, "Command '%s': Too many parameters", label);
2455 store_error_message(errmsg);
2456 return 1;
2457 }
2458
2459 /* Handle a few special cases */
2460
2461 if ( flags & F_ARB ) {
2462 /* If arbitrary extended code, get subcode and adjust argument count */
2463 if ( strchr(argv[1], '$') && mode == CMD_VERIFY) {
2464 /* Fake a dummy parameter for scene verification */
2465 subcode = 0x30;
2466 sp = " ";
2467 }
2468 else {
2469 subcode = (int)strtol(argv[1], &sp, radix);
2470 }
2471
2472 if ( !strchr(" \t\n", *sp) || subcode < 0 || subcode > 0xff ) {
2473 sprintf(errmsg,
2474 "Command '%s': Extended function code '%s' outside range 00-0xff",
2475 label, argv[1]);
2476 store_error_message(errmsg);
2477 return 1;
2478 }
2479 argv++;
2480 argc--;
2481 nargs--;
2482 }
2483
2484
2485 /* Default HC|Unit */
2486 hc = '_';
2487 bitmap = 0;
2488 aflags = A_VALID;
2489
2490 if ( !(flags & F_NUM) && nargs > 0 ) {
2491 /* parse it as an alias or HC|Units token. */
2492 aflags = parse_addr(argv[1], &hc, &bitmap);
2493 }
2494
2495 /* Make sure a positional parameter didn't slip through */
2496 if ( aflags & A_DUMMY && mode == CMD_RUN ) {
2497 sprintf(errmsg,
2498 "Command '%s': Unresolved positional parameter '%s'",
2499 label, argv[1]);
2500 store_error_message(errmsg);
2501 return 1;
2502 }
2503
2504 if ( !(aflags & A_VALID) ) {
2505 sprintf(errmsg, "Command '%s': ", label);
2506 add_error_prefix(errmsg);
2507 return 1;
2508 }
2509
2510 if ( !(aflags & A_HCODE) && !(flags & F_NUM) && minargs > 0 ) {
2511 sprintf(errmsg, "Command '%s': Housecode required", label);
2512 store_error_message(errmsg);
2513 return 1;
2514 }
2515
2516 /* Heyu shouldn't wait for (x)status_ack when executed from a script */
2517 if ( heyu_parent == D_ENGINE )
2518 flags &= ~F_STA;
2519
2520 switchv = cmdcode;
2521
2522 switch ( switchv ) {
2523
2524 case 21 : /* Function only - we should never get here */
2525 case 27 : /* Turn command - we should never get here */
2526 case 28 : /* Temp_Req - we should never get here */
2527 case 99 : /* Legacy commands - we should never get here */
2528 fprintf(stderr, "Internal error - direct_command() - case %d\n", switchv);
2529 return 1;
2530
2531 case 8 : /* Hail */
2532 case 9 : /* Hail Ack */
2533 if ( !(aflags & A_HCODE) ) {
2534 hc = configp->housecode;
2535 }
2536 case 0 : /* All Units Off */
2537 case 1 : /* All Lights On */
2538 case 6 : /* All Lights Off */
2539 case 12 : /* Data Transfer */
2540 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO ) {
2541 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
2542 store_error_message(errmsg);
2543 bitmap = 0;
2544 }
2545 if ( bitmap == 0 && (aflags & A_PLUS || configp->force_addr == YES) )
2546 bitmap = 1;
2547
2548 case 2 : /* On */
2549 case 3 : /* Off */
2550 case 13 : /* Status On */
2551 case 14 : /* Status Off */
2552 if ( aflags & A_MINUS || flags & F_NOA )
2553 bitmap = 0;
2554 else if ( bitmap == 0 && !(flags & F_ALL) ) {
2555 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2556 store_error_message(errmsg);
2557 return 1;
2558 }
2559
2560 if ( mode == CMD_VERIFY )
2561 break;
2562
2563 hcode = hc2code(hc);
2564
2565 cmdbuf[0] = 0x06;
2566 cmdbuf[1] = (hcode << 4) | cmdcode;
2567
2568
2569 if ( send_address(hcode, bitmap, syncmode) != 0 ||
2570 send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2571 return 1;
2572
2573 break;
2574
2575 case 48 : /* Status Ack emulation (experimental) */
2576 if ( aflags & A_MINUS || bitmap == 0 ) {
2577 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2578 store_error_message(errmsg);
2579 return 1;
2580 }
2581 else if ( aflags & A_MULT ) {
2582 sprintf(errmsg, "Command '%s': Only a single unitcode is allowed", label);
2583 store_error_message(errmsg);
2584 return 1;
2585 }
2586
2587 if ( mode == CMD_VERIFY )
2588 break;
2589
2590 if ( read_x10state_file() != 0 ) {
2591 sprintf(errmsg, "Command '%s': Unable to read state file", label);
2592 store_error_message(errmsg);
2593 return 1;
2594 }
2595
2596 hcode = hc2code(hc);
2597
2598 cmdcode = (x10state[hcode].state[OnState] & bitmap) ? 13 : 14; /* StatusOn : StatusOff */
2599
2600 cmdbuf[0] = 0x06;
2601 cmdbuf[1] = (hcode << 4) | cmdcode;
2602
2603 if ( send_address(hcode, bitmap, syncmode) != 0 ||
2604 send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2605 return 1;
2606
2607 break;
2608
2609 case 4 : /* Dim */
2610 case 5 : /* Bright */
2611 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
2612 /* Fake a dim value for a dummy parameter */
2613 dimval = 10;
2614 sp = " ";
2615 }
2616 else {
2617 dimval = (int)strtol(argv[2], &sp, 10);
2618 }
2619
2620 if ( !strchr(" \t\n", *sp) ) {
2621 sprintf(errmsg,
2622 "Command '%s': Invalid Dim/Bright parameter '%s'", label, argv[2]);
2623 store_error_message(errmsg);
2624 return 1;
2625 }
2626 else if ( configp->restrict_dims == YES && (dimval < 1 || dimval > 22) ) {
2627 sprintf(errmsg,
2628 "Command '%s': Dim/Bright value outside range 1-22", label);
2629 store_error_message(errmsg);
2630 return 1;
2631 }
2632 else if ( configp->restrict_dims == NO && (dimval < 0 || dimval > 31) ) {
2633 sprintf(errmsg,
2634 "Command '%s': Dim/Bright value outside range 0-31", label);
2635 store_error_message(errmsg);
2636 return 1;
2637 }
2638
2639 if ( aflags & A_MINUS || flags & F_NOA )
2640 bitmap = 0;
2641 else if ( bitmap == 0 ) {
2642 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2643 store_error_message(errmsg);
2644 return 1;
2645 }
2646
2647 if ( mode == CMD_VERIFY )
2648 break;
2649
2650 hcode = hc2code(hc);
2651
2652 fullbright = configp->full_bright;
2653
2654 syncmode = subcode ? RISE_SYNC : syncmode ;
2655
2656 /* Kluge for housecode D to circumvent 0x5A checksum problem */
2657 /* when sending dim Dx 22. Set dim value to 23 instead. */
2658 if ( hcode == hc2code('D') && configp->fix_5a == YES ) {
2659 fullbright = 23;
2660 dimval = (dimval == 22) ? 23 : dimval;
2661 }
2662
2663 /* Address the device(s) */
2664 if ( send_address(hcode, bitmap, syncmode) != 0 ) {
2665 return 1;
2666 }
2667
2668 /* If On is required (e.g., for WS467-1) send it */
2669 if ( flags & F_ONN ) {
2670 cmdbuf[0] = 0x06;
2671 cmdbuf[1] = (hcode << 4) | 0x02;
2672 if ( send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2673 return 1;
2674 }
2675
2676 /* If "brighten before dimming" flag is set, emulate a CM11A macro */
2677 /* by first brightening to full brightness. */
2678 if ( flags & F_BRI ) {
2679 cmdbuf[0] = (fullbright << 3) | 0x06;
2680 cmdbuf[1] = (hcode << 4) | 5;
2681
2682 cmdbuf[2] = (dimval << 3) | 0x06;
2683 cmdbuf[3] = (hcode << 4) | cmdcode;
2684
2685 if ( send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 ||
2686 send_command(cmdbuf + 2, 2, (flags & F_STA), syncmode) != 0 ) {
2687 return 1;
2688 }
2689 }
2690 else {
2691 cmdbuf[0] = (dimval << 3) | 0x06;
2692 cmdbuf[1] = (hcode << 4) | cmdcode;
2693
2694 if ( send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2695 return 1;
2696 }
2697
2698 break;
2699
2700 case 10 : /* Old style preset 1 or preset_level 1 */
2701 case 11 : /* Old style preset 2 or preset_level 2 */
2702 if ( aflags & A_MINUS || flags & F_NOA )
2703 bitmap = 0;
2704 else if ( bitmap == 0 ) {
2705 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2706 store_error_message(errmsg);
2707 return 1;
2708 }
2709
2710 /* (nargs = 2 for preset; nargs = 1 for preset_level) */
2711
2712 if ( strchr(argv[nargs], '$') && mode == CMD_VERIFY ) {
2713 /* Fake a dim dummy parameter for scene verification */
2714 dimval = 10;
2715 sp = " ";
2716 }
2717 else
2718 dimval = (int)strtol(argv[nargs], &sp, 10);
2719
2720 if ( !strchr(" \t\n", *sp) || dimval < 1 || dimval > 32 ) {
2721 sprintf(errmsg, "Command '%s': dim level outside range 1-32", label);
2722 store_error_message(errmsg);
2723 return 1;
2724 }
2725
2726 if ( mode == CMD_VERIFY )
2727 break;
2728
2729 if ( (dimcode = dimval) > 16 ) {
2730 cmdcode = 11;
2731 dimcode -= 16;
2732 }
2733 dimcode = rev_low_nybble(dimcode - 1);
2734
2735 hcode = hc2code(hc);
2736
2737 if ( flags & F_MAC && dimcode != hcode ) {
2738 sprintf(errmsg, "Command '%s': Preset level %d not supported for housecode %c",
2739 label, dimval, hc);
2740 store_error_message(errmsg);
2741 return 1;
2742 }
2743
2744 cmdbuf[0] = 0x06;
2745 cmdbuf[1] = (dimcode << 4) | cmdcode;
2746
2747 if ( send_address(hcode, bitmap, syncmode) != 0 ||
2748 send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2749 return 1;
2750
2751 break;
2752
2753 case 15 : /* Status Req */
2754 sendmap = bitmap;
2755 if ( aflags & A_MINUS || flags & F_NOA ) {
2756 bitmap = 1;
2757 sendmap = 0;
2758 }
2759 else if ( bitmap == 0 ) {
2760 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2761 store_error_message(errmsg);
2762 return 1;
2763 }
2764
2765 if ( mode == CMD_VERIFY )
2766 break;
2767
2768 hcode = hc2code(hc);
2769
2770 cmdbuf[0] = 0x06;
2771 cmdbuf[1] = (hcode << 4) | cmdcode;
2772
2773 if ( flags & F_SPF )
2774 flags &= ~F_STA;
2775
2776 /* Send each address/command pair individually to allow */
2777 /* associating a response with a particular unit code */
2778 mask = 1;
2779 for ( k = 0; k < 16; k++ ) {
2780 if ( bitmap & mask ) {
2781 if ( send_address(hcode, sendmap & mask, syncmode) != 0 ||
2782 send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2783 return 1;
2784 }
2785 mask = mask << 1;
2786 }
2787 break;
2788
2789 case 20 : /* Send address bytes only */
2790 /* Start over again with the address tokens */
2791 for ( k = 1; k < argc; k++ ) {
2792 aflags = parse_addr(argv[k], &hc, &bitmap);
2793 if ( !(aflags & A_VALID) ) {
2794 sprintf(errmsg, "Command '%s': ", label);
2795 add_error_prefix(errmsg);
2796 return 1;
2797 }
2798 else if ( !(aflags & A_HCODE) || bitmap == 0 ) {
2799 sprintf(errmsg,
2800 "Command '%s': invalid HU or Alias '%s'", label, argv[k]);
2801 store_error_message(errmsg);
2802 return 1;
2803 }
2804
2805 if ( mode == CMD_VERIFY )
2806 continue;
2807
2808 hcode = hc2code(hc);
2809
2810 if ( send_address(hcode, bitmap, syncmode) != 0 )
2811 return 1;
2812
2813 /* A delay between addresses may be needed for */
2814 /* programming some SwitchLinc devices. (The */
2815 /* TempLinc programs OK without it.) */
2816 millisleep(60);
2817 }
2818 break;
2819
2820 case 29 : /* Send bytes directly to cm11a (highly experimental) */
2821 case 31 : /* Send bytes directly to cm11a for transmission (highly experimental) */
2822 /* First check that each arg is a byte */
2823 for ( k = 1; k < argc; k++ ) {
2824 if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
2825 /* Fake a dummy value for scene verification */
2826 xdata = 10;
2827 sp = " ";
2828 }
2829 else {
2830 xdata = (int)strtol(argv[k], &sp, 16);
2831 }
2832 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
2833 sprintf(errmsg,
2834 "Command '%s': Data '%s' outside range 0-FF", label, argv[k]);
2835 store_error_message(errmsg);
2836 return 1;
2837 }
2838 }
2839
2840 if ( mode == CMD_VERIFY )
2841 break;
2842
2843 for ( k = 1; k < argc; k++ ) {
2844 xdata = (int)strtol(argv[k], NULL, 16);
2845 testbuf[k-1] = xdata;
2846 }
2847
2848 if ( switchv == 29 )
2849 (void) xwrite(tty, (char *) testbuf, argc - 1);
2850 else
2851 send_command(testbuf, argc - 1, 0, syncmode);
2852 break;
2853
2854
2855 case 22 : /* Send arbitrary series of hex bytes as addresses */
2856 /* First check that each byte is in range 00-FF */
2857 for ( k = 1; k < argc; k++ ) {
2858 if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
2859 /* Fake a dummy value for scene verification */
2860 xdata = 10;
2861 sp = " ";
2862 }
2863 else {
2864 xdata = (int)strtol(argv[k], &sp, 16);
2865 }
2866 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
2867 sprintf(errmsg,
2868 "Command '%s': Data '%s' outside range 0-FF", label, argv[k]);
2869 store_error_message(errmsg);
2870 return 1;
2871 }
2872 }
2873
2874 if ( mode == CMD_VERIFY )
2875 break;
2876
2877 for ( k = 1; k < argc; k++ ) {
2878 xdata = (int)strtol(argv[k], NULL, 16);
2879 if ( send_address(xdata >> 4, (1 << (xdata & 0x0f)), syncmode) != 0 )
2880 return 1;
2881 }
2882 break;
2883
2884 case 23 : /* Send (quoted) text message */
2885 /* Format: */
2886 /* Send each character as two address bytes - high nybble first */
2887 if ( !(aflags & A_DUMMY) && bitmap != 0 ) {
2888 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
2889 store_error_message(errmsg);
2890 bitmap = 0;
2891 }
2892 sp = argv[2];
2893
2894 if ( mode == CMD_VERIFY )
2895 break;
2896
2897 hcode = hc2code(hc);
2898
2899 printf("Sending %d characters as Hcode|hi Hcode|low bytes:\n",
2900 (int)strlen(sp));
2901
2902 while ( *sp != '\0' ) {
2903 if ( send_address(hcode, (1 << (*sp >> 4)), syncmode) != 0 ||
2904 send_address(hcode, (1 << (*sp & 0x0f)), syncmode) != 0 )
2905 return 1;
2906 printf("%c", *sp);
2907 fflush(stdout);
2908 sp++;
2909 }
2910 printf("\n");
2911 break;
2912
2913 case 24 : /* Send All_Units_Off to all Housecodes */
2914 if ( mode == CMD_VERIFY )
2915 break;
2916
2917 bitmap = (configp->force_addr == YES) ? 1 : 0;
2918 cmdbuf[0] = 0x06;
2919 for ( j = 0; j < 16; j++ ) {
2920 hcode = reorder[j];
2921 cmdbuf[1] = (hcode << 4);
2922 if ( send_address(hcode, bitmap, syncmode) != 0 ||
2923 send_command(cmdbuf, 2, 0, syncmode) != 0 )
2924 return 1;
2925 millisleep(60);
2926 }
2927 break;
2928
2929 case 25 : /* Pause (for use in scenes) */
2930 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
2931 /* Fake a dummy parameter for scene verification */
2932 pause = 1.0;
2933 sp = " ";
2934 }
2935 else {
2936 pause = strtod(argv[1], &sp);
2937 }
2938 if ( !strchr(" \t\n", *sp) || pause < 0.0 ) {
2939 sprintf(errmsg, "Command '%s': invalid Pause value '%s'", label, argv[1]);
2940 store_error_message(errmsg);
2941 return 1;
2942 }
2943
2944 if ( mode == CMD_VERIFY )
2945 break;
2946
2947 if ( verbose )
2948 printf("pause %.3f seconds\n", pause);
2949 millisleep((int)(1000. * pause));
2950
2951 break;
2952
2953 case 41 : /* Sleep (for use in scenes) */
2954 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
2955 /* Fake a dummy parameter for scene verification */
2956 pause = 1.0;
2957 sp = " ";
2958 }
2959 else {
2960 pause = strtod(argv[1], &sp);
2961 }
2962 if ( !strchr(" \t\n", *sp) || pause < 0.0 ) {
2963 sprintf(errmsg, "Command '%s': invalid sleep value '%s'", label, argv[1]);
2964 store_error_message(errmsg);
2965 return 1;
2966 }
2967
2968 if ( mode == CMD_VERIFY )
2969 break;
2970
2971 sprintf(writefilename, "%s%s", WRITEFILE, configp->suffix);
2972 munlock(writefilename);
2973
2974 millisleep((int)(1000. * pause));
2975
2976 if ( lock_for_write() != 0 ) {
2977 fprintf(stderr, "Unable to re-lock after sleep command\n");
2978 exit(1);
2979 }
2980
2981 break;
2982
2983 case 45 : /* Random delay in minutes */
2984 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
2985 /* Fake a dummy parameter for scene verification */
2986 delay = 1;
2987 sp = "";
2988 }
2989 else {
2990 delay = strtol(argv[1], &sp, 10);
2991 }
2992
2993 if ( !strchr(" \t\n", *sp) || delay < 0 || delay > 240 ) {
2994 sprintf(errmsg, "Command '%s': Invalid delay time '%s'", label, argv[1]);
2995 store_error_message(errmsg);
2996 return 1;
2997 }
2998
2999 if ( nargs == 1 ) {
3000 maxdelay = delay;
3001 mindelay = 0;
3002 sp = "";
3003 }
3004 else {
3005 mindelay = delay;
3006 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3007 /* Fake a dummy parameter for scene verification */
3008 maxdelay = 5;
3009 sp = " ";
3010 }
3011 else {
3012 maxdelay = strtol(argv[2], &sp, 10);
3013 }
3014 }
3015
3016 if ( !strchr(" \t\n", *sp) || maxdelay < 0 || maxdelay > 240 ) {
3017 sprintf(errmsg, "Command '%s': Invalid delay time '%s'", label, argv[1]);
3018 store_error_message(errmsg);
3019 return 1;
3020 }
3021
3022 if ( maxdelay < mindelay ) {
3023 delay = maxdelay;
3024 maxdelay = mindelay;
3025 mindelay = delay;
3026 }
3027
3028 if ( mode == CMD_VERIFY )
3029 break;
3030
3031 delay = (double)(maxdelay - mindelay) * random_float() + 0.5;
3032 delay += mindelay;
3033
3034 if ( !i_am_relay && !i_am_aux ) {
3035 printf("%s Random delay %ld minutes\n", datstrf(), delay);
3036 fflush(stdout);
3037 }
3038
3039 sprintf(writefilename, "%s%s", WRITEFILE, configp->suffix);
3040 munlock(writefilename);
3041
3042 millisleep(60000L * delay);
3043
3044 if ( lock_for_write() != 0 ) {
3045 fprintf(stderr, "Unable to re-lock after rdelay command\n");
3046 exit(1);
3047 }
3048
3049 break;
3050
3051 case 32 : /* Delay in minutes */
3052 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3053 /* Fake a dummy parameter for scene verification */
3054 delay = 1;
3055 sp = " ";
3056 }
3057 else {
3058 delay = strtol(argv[1], &sp, 10);
3059 }
3060 if ( !strchr(" \t\n", *sp) || delay < 0 || delay > 240 ) {
3061 sprintf(errmsg, "Command '%s': Invalid delay time '%s'", label, argv[1]);
3062 store_error_message(errmsg);
3063 return 1;
3064 }
3065
3066 if ( mode == CMD_VERIFY )
3067 break;
3068
3069 if ( verbose ) {
3070 printf("%s Delay %ld minutes\n", datstrf(), delay);
3071 }
3072
3073 sprintf(writefilename, "%s%s", WRITEFILE, configp->suffix);
3074 munlock(writefilename);
3075
3076 millisleep(60000L * delay);
3077
3078 if ( lock_for_write() != 0 ) {
3079 fprintf(stderr, "Unable to re-lock after delay command\n");
3080 exit(1);
3081 }
3082
3083 break;
3084
3085 case 33 : /* Wait for next tick of system clock */
3086 if ( mode == CMD_VERIFY )
3087 break;
3088
3089 wait_next_tick();
3090
3091 break;
3092
3093 case 26 : /* All Units On */
3094 if ( bitmap != 0 && !(aflags & A_MINUS) ) {
3095 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3096 store_error_message(errmsg);
3097 }
3098 bitmap = 0xFFFF;
3099
3100 if ( mode == CMD_VERIFY )
3101 break;
3102
3103 hcode = hc2code(hc);
3104
3105 cmdbuf[0] = 0x06;
3106 cmdbuf[1] = (hcode << 4) | 2 ;
3107
3108 if ( send_address(hcode, bitmap, syncmode) != 0 ||
3109 send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
3110 return 1;
3111
3112 break;
3113
3114 case 30 : /* Show X10 state */
3115 if ( mode == CMD_VERIFY )
3116 break;
3117
3118 hcode = hc2code(hc);
3119
3120 cmdbuf[0] = 0x7b;
3121 cmdbuf[1] = 0;
3122 cmdbuf[2] = hcode << 4;
3123
3124 xwrite(tty, cmdbuf, 3);
3125 millisleep(100);
3126
3127 break;
3128
3129 case 35 : /* Set or Clear flags */
3130
3131 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3132 /* Fake a dummy parameter for scene verification */
3133 sp = "1";
3134 }
3135 else {
3136 sp = argv[1];
3137 }
3138
3139 if ( flaglist2banks(flagbank, NUM_FLAG_BANKS, sp) ) {
3140 return 1;
3141 }
3142
3143 if ( mode == CMD_VERIFY )
3144 break;
3145
3146 if ( flags & F_INT ) {
3147 for ( j = 0; j < NUM_FLAG_BANKS; j++ ) {
3148 if ( flagbank[j] )
3149 engine_update_flags(j, flagbank[j], subcode);
3150 }
3151 break;
3152 }
3153
3154 if ( verbose ) {
3155 if ( subcode == SETFLAG )
3156 printf("Set flags %s\n", argv[1]);
3157 else
3158 printf("Clear flags %s\n", argv[1]);
3159 }
3160
3161 for ( j = 0; j < NUM_FLAG_BANKS; j++ ) {
3162 if ( flagbank[j] )
3163 send_long_flag_update(j, flagbank[j], subcode);
3164 }
3165
3166 break;
3167 #if 0
3168 case 35 : /* Set or Clear flags */
3169 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3170 /* Fake a dummy parameter for scene verification */
3171 longmap = 1;
3172 }
3173 else if ( (longmap = flags2longmap(argv[1])) == 0 ) {
3174 /*
3175 sprintf(errmsg, "Command '%s': invalid flags specified '%s'", label, argv[1]);
3176 store_error_message(errmsg);
3177 */
3178 return 1;
3179 }
3180
3181 if ( mode == CMD_VERIFY )
3182 break;
3183
3184 if ( flags & F_INT ) {
3185 engine_update_flags(0, longmap, subcode);
3186 break;
3187 }
3188
3189 if ( verbose ) {
3190 if ( subcode == SETFLAG )
3191 printf("Set flags %s\n", argv[1]);
3192 else
3193 printf("Clear flags %s\n", argv[1]);
3194 }
3195
3196 send_long_flag_update(0, longmap, subcode);
3197
3198 break;
3199 #endif
3200
3201 case 36 : /* Clear status flags */
3202 if ( mode == CMD_VERIFY )
3203 break;
3204
3205 if ( aflags & A_MINUS || bitmap == 0 )
3206 bitmap = 0xffff;
3207
3208 if ( flags & F_INT ) {
3209 clear_statusflags(hc2code(hc), bitmap);
3210 break;
3211 }
3212
3213 send_clear_statusflags(hc2code(hc), bitmap);
3214
3215 break;
3216
3217 case 37 : /* Virtual Data */
3218 if ( aflags & A_PLUS || aflags & A_MINUS ) {
3219 sprintf(errmsg,
3220 "Command '%s': '+/-' prefixes invalid for vdata[x] command.", label);
3221 store_error_message(errmsg);
3222 return 1;
3223 }
3224 if ( flags & F_NOA ) {
3225 sprintf(errmsg, "'function' is invalid for vdata[x] command.");
3226 store_error_message(errmsg);
3227 return 1;
3228 }
3229 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3230 /* Fake a dim value for a dummy parameter */
3231 dimval = 10;
3232 sp = " ";
3233 }
3234 else {
3235 strlower(argv[2]);
3236 if ( strncmp(argv[2], "0x", 2) == 0 )
3237 dimval = (int)strtol(argv[2], &sp, 16);
3238 else
3239 dimval = (int)strtol(argv[2], &sp, 10);
3240 }
3241
3242 if ( !strchr(" \t\n", *sp) || dimval < 0 || dimval > 0xff ) {
3243 sprintf(errmsg,
3244 "Command '%s': Invalid virtual data parameter '%s'", label, argv[2]);
3245 store_error_message(errmsg);
3246 return 1;
3247 }
3248
3249 if ( mode == CMD_VERIFY )
3250 break;
3251
3252 hcode = hc2code(hc);
3253 vtype = (subcode == 1) ? RF_VDATAM : RF_STD;
3254
3255 if ( flags & F_INT ) {
3256 mask = 1;
3257 for ( j = 0; j < 16; j++ ) {
3258 if ( bitmap & mask ) {
3259 engine_internal_vdata(hcode, j, dimval, vtype);
3260 }
3261 mask = mask << 1;
3262 }
3263 if ( i_am_state )
3264 write_x10state_file();
3265
3266 break;
3267
3268 }
3269
3270 mask = 1;
3271 for ( j = 0; j < 16; j++ ) {
3272 if ( bitmap & mask ) {
3273 send_virtual_data((hcode << 4) | j, dimval, vtype, 0, 0, 0, 0);
3274 }
3275 mask = mask << 1;
3276 }
3277
3278 break;
3279
3280 case 38 : /* Set user countdown timer */
3281 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3282 /* Fake a dummy parameter for scene verification */
3283 timer_no = 1;
3284 }
3285 else {
3286 timer_no = (int)strtol(argv[1], &sp, 10);
3287 if ( !strchr(" \t\n\r", *sp) ) {
3288 sprintf(errmsg,
3289 "Command '%s': Invalid timer number '%s'", label, argv[1]);
3290 store_error_message(errmsg);
3291 return 1;
3292 }
3293 }
3294
3295 if ( timer_no < 1 || timer_no > NUM_USER_TIMERS ) {
3296 sprintf(errmsg,
3297 "Command '%s': Timer number outside range 1-%d", label, NUM_USER_TIMERS);
3298 store_error_message(errmsg);
3299 return 1;
3300 }
3301
3302
3303 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3304 /* Fake a dummy parameter for scene verification */
3305 timer_countdown = 10;
3306 }
3307 else {
3308 timer_countdown = parse_hhmmss(argv[2], 3);
3309 if ( timer_countdown < 0 ) {
3310 sprintf(errmsg,
3311 "Command '%s': Invalid timer countdown '%s'", label, argv[2]);
3312 store_error_message(errmsg);
3313 return 1;
3314 }
3315 }
3316
3317 if ( mode == CMD_VERIFY )
3318 break;
3319
3320 if ( flags & F_INT ) {
3321 set_user_timer_countdown(timer_no, timer_countdown);
3322 display_settimer_setting(timer_no, timer_countdown);
3323 break;
3324 }
3325
3326 send_settimer_msg(timer_no, timer_countdown);
3327
3328 break;
3329
3330 case 46 : /* Set user random countdown timer */
3331 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3332 /* Fake a dummy parameter for scene verification */
3333 timer_no = 1;
3334 }
3335 else {
3336 timer_no = (int)strtol(argv[1], &sp, 10);
3337 if ( !strchr(" \t\n\r", *sp) ) {
3338 sprintf(errmsg,
3339 "Command '%s': Invalid timer number '%s'", label, argv[1]);
3340 store_error_message(errmsg);
3341 return 1;
3342 }
3343 }
3344
3345 if ( timer_no < 1 || timer_no > NUM_USER_TIMERS ) {
3346 sprintf(errmsg,
3347 "Command '%s': Timer number outside range 1-%d", label, NUM_USER_TIMERS);
3348 store_error_message(errmsg);
3349 return 1;
3350 }
3351
3352
3353 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3354 /* Fake a dummy parameter for scene verification */
3355 timer_countdown = 10;
3356 }
3357 else {
3358 /* Get first time parameter */
3359 timer_countdown = parse_hhmmss(argv[2], 3);
3360 if ( timer_countdown < 0 ) {
3361 sprintf(errmsg,
3362 "Command '%s': Invalid timer countdown '%s'", label, argv[2]);
3363 store_error_message(errmsg);
3364 return 1;
3365 }
3366 }
3367
3368 if ( nargs == 2 ) {
3369 max_countdown = timer_countdown;
3370 min_countdown = 1;
3371 }
3372 else {
3373 min_countdown = timer_countdown;
3374 if ( strchr(argv[3], '$') && mode == CMD_VERIFY ) {
3375 max_countdown = 5;
3376 }
3377 else {
3378 max_countdown = parse_hhmmss(argv[3], 3);
3379 if ( max_countdown < 0 ) {
3380 sprintf(errmsg,
3381 "Command '%s': Invalid timer countdown '%s'", label, argv[2]);
3382 store_error_message(errmsg);
3383 return 1;
3384 }
3385 }
3386 }
3387
3388 if ( max_countdown < min_countdown ) {
3389 longvalue = min_countdown;
3390 min_countdown = max_countdown;
3391 max_countdown = longvalue;
3392 }
3393
3394 if ( mode == CMD_VERIFY )
3395 break;
3396
3397 timer_countdown = (double)(max_countdown - min_countdown) * random_float() + 0.5;
3398 timer_countdown += min_countdown;
3399
3400 if ( flags & F_INT ) {
3401 set_user_timer_countdown(timer_no, timer_countdown);
3402 display_settimer_setting(timer_no, timer_countdown);
3403 break;
3404 }
3405
3406 send_settimer_msg(timer_no, timer_countdown);
3407
3408 break;
3409
3410 case 39 : /* Reset all user timers */
3411
3412 if ( mode == CMD_VERIFY )
3413 break;
3414
3415 if ( flags & F_INT ) {
3416 reset_user_timers();
3417 break;
3418 }
3419
3420 send_x10state_command(ST_CLRTIMERS, 0);
3421
3422 break;
3423
3424 case 40 : /* Clear tamper flags */
3425
3426 if ( mode == CMD_VERIFY )
3427 break;
3428
3429 send_x10state_command(ST_CLRTAMPER, 0);
3430
3431 break;
3432
3433
3434 case 42 : /* Set counter */
3435 if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3436 /* Fake a dummy parameter for scene verification */
3437 counter_no = 1;
3438 }
3439 else {
3440 counter_no = (int)strtol(argv[1], &sp, 10);
3441 if ( !strchr(" \t\n\r", *sp) ) {
3442 sprintf(errmsg,
3443 "Command '%s': Invalid counter number '%s'", label, argv[1]);
3444 store_error_message(errmsg);
3445 return 1;
3446 }
3447 }
3448
3449 if ( counter_no < 1 || counter_no > (32 * NUM_COUNTER_BANKS) ) {
3450 sprintf(errmsg,
3451 "Command '%s': Counter number outside range 1-%d", label, 32 * NUM_COUNTER_BANKS);
3452 store_error_message(errmsg);
3453 return 1;
3454 }
3455
3456 counter_value = 10;
3457 if ( subcode == CNT_SET ) {
3458 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3459 /* Fake a dummy parameter for scene verification */
3460 counter_value = 10;
3461 }
3462 else {
3463 value = (int)strtol(argv[2], &sp, 10);
3464 if ( !strchr(" \t\n\r", *sp) || value < 0 || value > 0xffff ) {
3465 sprintf(errmsg,
3466 "Command '%s': Invalid counter value '%s'", label, argv[2]);
3467 store_error_message(errmsg);
3468 return 1;
3469 }
3470 counter_value = (unsigned short)value;
3471 }
3472 }
3473
3474 if ( mode == CMD_VERIFY )
3475 break;
3476
3477 if ( flags & F_INT ) {
3478 return set_counter(counter_no, counter_value, subcode);
3479 break;
3480 }
3481
3482 send_setcounter_msg(counter_no, counter_value, subcode);
3483
3484 break;
3485
3486 case 43 : /* Arm or Disarm system */
3487 if ( subcode == CLRFLAG ) {
3488 sflags = 0;
3489 }
3490 else {
3491 if ( get_armed_parms(argc, argv, &sflags, mode) != 0 )
3492 return 1;
3493 }
3494
3495 if ( mode == CMD_VERIFY )
3496 break;
3497
3498 if ( subcode == SETFLAG ) {
3499 read_x10state_file();
3500 warn_zone_faults(stdout, "");
3501 }
3502
3503 if ( flags & F_INT ) {
3504 set_globsec_flags(sflags);
3505 write_x10state_file();
3506 printf("%s %s\n", datstrf(), display_armed_status());
3507 fflush(stdout);
3508 }
3509 else {
3510 send_x10state_command(ST_SECFLAGS, (unsigned char)sflags);
3511 }
3512
3513 break;
3514
3515 case 47 : /* Null direct command (does nothing) */
3516 case 44 : /* Null internal precommand (does nothing) */
3517 break;
3518
3519 case 7 : /* Extended Code commands */
3520 sendmap = (aflags & A_PLUS) ? bitmap : 0 ;
3521 if ( sendmap == 0 && configp->force_addr == YES )
3522 sendmap = 0xFFFF;
3523
3524 hcode = hc2code(hc);
3525
3526 if ( flags & F_ARB )
3527 switchcode = 0xff;
3528 else
3529 switchcode = subcode;
3530
3531 switch ( switchcode ) {
3532 case 0x30 : /* Include in group[.subgroup] at current level */
3533 if ( bitmap == 0 ) {
3534 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3535 store_error_message(errmsg);
3536 return 1;
3537 }
3538
3539 /* Get group */
3540 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3541 /* Fake a dummy parameter for scene verification */
3542 group = 0;
3543 subgroup = 0;
3544 }
3545 else {
3546 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3547 sprintf(errmsg, "Command '%s': %s", label, error_message());
3548 store_error_message(errmsg);
3549 return 1;
3550 }
3551 }
3552
3553 xdata = group << 6;
3554
3555 if ( subgroup & 0x80u ) {
3556 xdata |= (subgroup & 0x0fu) | 0x20u;
3557 }
3558 break;
3559
3560 case 0x32 : /* Include in group at level */
3561 if ( bitmap == 0 ) {
3562 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3563 store_error_message(errmsg);
3564 return 1;
3565 }
3566
3567 /* Get group */
3568 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3569 /* Fake a dummy parameter for scene verification */
3570 group = 0;
3571 subgroup = 0;
3572 }
3573 else {
3574 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3575 sprintf(errmsg, "Command '%s': %s", label, error_message());
3576 store_error_message(errmsg);
3577 return 1;
3578 }
3579 }
3580 if ( subgroup & 0x80u ) {
3581 sprintf(errmsg,
3582 "Command '%s': Cannot specify level with group.subgroup", label);
3583 store_error_message(errmsg);
3584 return 1;
3585 }
3586
3587 /* Get level 0-63 */
3588 if ( strchr(argv[3], '$') && mode == CMD_VERIFY ) {
3589 /* Fake a dummy parameter for scene verification */
3590 dimval = 10;
3591 sp = " ";
3592 }
3593 else {
3594 dimval = (int)strtol(argv[3], &sp, 10);
3595 }
3596
3597 if ( !strchr(" \t\n", *sp) || dimval < 0 || dimval > 63 ) {
3598 sprintf(errmsg,
3599 "Command '%s': Extended Preset level outside range 0-63", label);
3600 store_error_message(errmsg);
3601 return 1;
3602 }
3603 xdata = (group << 6) | dimval;
3604 break;
3605
3606 case 0x31 : /* Extended Preset Dim */
3607 if ( bitmap == 0 ) {
3608 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3609 store_error_message(errmsg);
3610 return 1;
3611 }
3612
3613 /* Get dim level */
3614 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3615 /* Fake a dummy parameter for scene verification */
3616 xdata = 10;
3617 sp = " ";
3618 }
3619 else {
3620 xdata = (int)strtol(argv[2], &sp, 10);
3621 }
3622
3623 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 63 ) {
3624 sprintf(errmsg,
3625 "Command '%s': Extended Preset level outside range 0-63", label);
3626 store_error_message(errmsg);
3627 return 1;
3628 }
3629
3630 /* Get ramp rate */
3631 ramp = 0;
3632 if ( nargs == 3 ) {
3633 if ( strchr(argv[3], '$') && mode == CMD_VERIFY ) {
3634 /* Fake a dummy parameter for scene verification */
3635 ramp = 0;
3636 sp = " ";
3637 }
3638 else {
3639 ramp = (int)strtol(argv[3], &sp, 10);
3640 }
3641 }
3642 if ( !strchr(" \t\n", *sp) || ramp < 0 || ramp > 3 ) {
3643 sprintf(errmsg,
3644 "Command '%s': Extended Preset ramp rate outside range 0-3", label);
3645 store_error_message(errmsg);
3646 return 1;
3647 }
3648 xdata |= ramp << 6;
3649
3650 break;
3651
3652 case 0x35 : /* Remove from extended group(s) */
3653 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3654 xdata = 0x01;
3655 }
3656 else if ( (xdata = (int)list2linmap(argv[2], 0, 3)) == 0 ) {
3657 sprintf(errmsg, "Command '%s': Group number %s", label, error_message());
3658 store_error_message(errmsg);
3659 return 1;
3660 }
3661
3662 if ( subsubcode == 0 ) {
3663 if ( bitmap == 0 ) {
3664 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3665 store_error_message(errmsg);
3666 return 1;
3667 }
3668 }
3669 else {
3670 xdata |= 0xf0;
3671 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) &&
3672 configp->force_addr == NO) {
3673 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3674 store_error_message(errmsg);
3675 *errmsg = '\0';
3676 bitmap = 1;
3677 }
3678 else if ( bitmap == 0 ) {
3679 bitmap = 1;
3680 }
3681 }
3682
3683 if ( aflags & A_PLUS )
3684 sendmap = bitmap;
3685
3686 break;
3687
3688 case 0x36 : /* Execute group function */
3689 case 0x3c : /* Extended Group Dim/Bright */
3690 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) ) {
3691 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3692 store_error_message(errmsg);
3693 bitmap = 1;
3694 }
3695 else if ( bitmap == 0 )
3696 bitmap = 1;
3697
3698 if ( aflags & A_PLUS )
3699 sendmap = bitmap;
3700
3701 /* Get group */
3702 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3703 /* Fake a dummy parameter for scene verification */
3704 group = 0;
3705 subgroup = 0;
3706 sp = " ";
3707 }
3708 else {
3709 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3710 sprintf(errmsg, "Command '%s': %s", label, error_message());
3711 store_error_message(errmsg);
3712 return 1;
3713 }
3714 }
3715
3716 xdata = (group << 6) | (subsubcode << 4);
3717 if ( subgroup & 0x80u ) {
3718 xdata |= 0x20u | (subgroup & 0x0f);
3719 }
3720 break;
3721
3722 case 0x3b : /* Set module auto status_ack mode */
3723 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) ) {
3724 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3725 store_error_message(errmsg);
3726 bitmap = 1;
3727 }
3728 else if ( bitmap == 0 )
3729 bitmap = 1;
3730
3731 if ( aflags & A_PLUS )
3732 sendmap = bitmap;
3733
3734 /* Get mode (0-3) */
3735 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3736 /* Fake a dummy parameter for scene verification */
3737 xdata = 3;
3738 sp = " ";
3739 }
3740 else {
3741 xdata = (int)strtol(argv[2], &sp, 10);
3742 }
3743
3744 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 3 ) {
3745 sprintf(errmsg,
3746 "Command '%s': Extended Config Mode outside range 0-3", label);
3747 store_error_message(errmsg);
3748 return 1;
3749 }
3750 break;
3751
3752 case 0xfe : /* Extended ON ( = Extended Preset Dim level 63) */
3753 if ( bitmap == 0 ) {
3754 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3755 store_error_message(errmsg);
3756 return 1;
3757 }
3758 subcode = 0x31;
3759 xdata = 63;
3760 break;
3761
3762 case 0xfd : /* Extended OFF ( = Extended Preset Dim level 0) */
3763 if ( bitmap == 0 ) {
3764 sprintf(errmsg, "command '%s': No Unitcode specified", label);
3765 store_error_message(errmsg);
3766 return 1;
3767 }
3768 subcode = 0x31;
3769 xdata = 0;
3770 break;
3771
3772 case 0x33 : /* Extended All On */
3773 case 0x34 : /* Extended All Off */
3774 case 0x04 : /* Shutter Open All */
3775 case 0x0B : /* Shutter Close All */
3776
3777 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) ) {
3778 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3779 store_error_message(errmsg);
3780 bitmap = 1;
3781 }
3782 else if ( bitmap == 0 )
3783 bitmap = 1;
3784
3785 if ( aflags & A_PLUS )
3786 sendmap = bitmap;
3787
3788 xdata = 0;
3789 break;
3790
3791 case 0x37 : /* Extended Status Requests */
3792 if ( subsubcode == 0 ) {
3793 /* Extended Output Status Req */
3794 if ( bitmap == 0 ) {
3795 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3796 store_error_message(errmsg);
3797 return 1;
3798 }
3799 xdata = 0;
3800 }
3801 else if ( subsubcode == 1 ) {
3802 /* Extended Power Up */
3803 if ( bitmap == 0 ) {
3804 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3805 store_error_message(errmsg);
3806 return 1;
3807 }
3808 xdata = 0x10;
3809 }
3810 else {
3811 /* Group Status Req */
3812 /* Get group */
3813 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3814 /* Fake a dummy parameter for scene verification */
3815 group = 0;
3816 subgroup = 0;
3817 }
3818 else {
3819 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3820 sprintf(errmsg, "Command '%s': %s", label, error_message());
3821 store_error_message(errmsg);
3822 return 1;
3823 }
3824 subsubcode = (subgroup & 0x80u) ? 3 : 2;
3825 }
3826 xdata = (group << 6) | (subsubcode << 4) | (subgroup & 0x0fu);
3827 }
3828 break;
3829
3830
3831 case 0x01 : /* Shutter open, observe limit */
3832 case 0x03 : /* Shutter open, disregard limit */
3833 case 0x02 : /* Shutter set limit */
3834 if ( bitmap == 0 ) {
3835 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3836 store_error_message(errmsg);
3837 return 1;
3838 }
3839 /* Get dim level */
3840 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3841 /* Fake a dummy parameter for scene verification */
3842 xdata = 10;
3843 sp = " ";
3844 }
3845 else {
3846 xdata = (int)strtol(argv[2], &sp, 10);
3847 }
3848
3849 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 25 ) {
3850 sprintf(errmsg,
3851 "Command '%s': Shutter level outside range 0-25", label);
3852 store_error_message(errmsg);
3853 return 1;
3854 }
3855 break;
3856
3857
3858 case 0xff : /* Arbitrary Extended Function */
3859 if ( bitmap == 0 ) {
3860 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3861 store_error_message(errmsg);
3862 return 1;
3863 }
3864 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3865 /* Fake a dummy parameter for scene verification */
3866 xdata = 10;
3867 sp = " ";
3868 }
3869 else {
3870 xdata = (int)strtol(argv[2], &sp, 16);
3871 }
3872 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
3873 sprintf(errmsg,
3874 "Command '%s': Data '%s' outside range 0-FF", label, argv[2]);
3875 store_error_message(errmsg);
3876 return 1;
3877 }
3878 break ;
3879
3880 default :
3881 sprintf(errmsg, "Command '%s': Not implemented", label);
3882 store_error_message(errmsg);
3883 return 1;
3884 }
3885
3886 if ( mode == CMD_VERIFY )
3887 break;
3888
3889 /* Header for extended code */
3890 cmdbuf[0] = 0x07;
3891
3892 /* House code and extended code function */
3893 cmdbuf[1] = (hcode << 4) | 0x07 ;
3894
3895 /* Unit code */
3896 /* cmdbuf[2] = unit code is filled in below */
3897
3898 /* "data" value */
3899 cmdbuf[3] = (unsigned char)(xdata & 0xff);
3900
3901 /* Extended Type/command */
3902 cmdbuf[4] = (unsigned char)(subcode & 0xff);
3903
3904
3905 mask = 1;
3906 for ( j = 0; j < 16; j++ ) {
3907 if ( bitmap & mask ) {
3908 cmdbuf[2] = j;
3909
3910 /* Kluge "fix" for checksum 5A problem. */
3911 /* CM11A seems to disregard the dim field */
3912 /* in the header byte. */
3913 if ( checksum(cmdbuf, 5) == 0x5A && configp->fix_5a == YES )
3914 cmdbuf[0] = 0x0F;
3915
3916 if ( send_address(hcode, sendmap & mask, syncmode) != 0 ||
3917 send_command(cmdbuf, 5, flags & F_STA, syncmode) != 0 )
3918 return 1;
3919 }
3920 mask = mask << 1;
3921 }
3922
3923 break;
3924
3925 #ifdef HASCM17A
3926 case 34 : /* CM17A "Firecracker" RF commands */
3927 noswitch = 0;
3928
3929 if ( aflags & A_PLUS && !(aflags & A_DUMMY) ) {
3930 sprintf(errmsg,
3931 "Command '%s': '+' prefix invalid for RF commands.", label);
3932 store_error_message(errmsg);
3933 return 1;
3934 }
3935 if ( flags & F_NOA ) {
3936 sprintf(errmsg, "'function' is invalid for RF commands.");
3937 store_error_message(errmsg);
3938 return 1;
3939 }
3940 if ( flags & F_FFM )
3941 rfmode = RF_FAST;
3942
3943 switch ( subcode ) {
3944 case 0 : /* RF All Units Off */
3945 case 1 : /* RF All Lights On */
3946 case 6 : /* RF All Lights Off */
3947 if ( bitmap != 0 && !(aflags & A_MINUS) && !(aflags & A_PLUS)) {
3948 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3949 store_error_message(errmsg);
3950 }
3951
3952 if ( mode == CMD_VERIFY )
3953 return 0;
3954
3955 if ( configp->rf_noswitch == YES )
3956 noswitch = rf_nosw_code[subcode];
3957
3958 bursts = configp->rf_bursts[subcode];
3959
3960 rfhcode = rev_low_nybble(hc2code(hc));
3961
3962 rfword = (rfhcode << 12) | rf_func_code[subcode] | noswitch;
3963 display_rf_xmit(subcode, rfword, bursts);
3964 if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
3965 sprintf(errmsg,
3966 "Command '%s': Unable to write to CM17A device.", label);
3967 store_error_message(errmsg);
3968 return 1;
3969 }
3970 rf_post_delay();
3971
3972 break;
3973
3974 case 2 : /* RF On */
3975 case 3 : /* RF Off */
3976 if ( aflags & A_MINUS ) {
3977 sprintf(errmsg,
3978 "Command '%s': '-' prefix invalid for this command.\n", label);
3979 store_error_message(errmsg);
3980 return 1;
3981 }
3982
3983 if ( bitmap == 0 ) {
3984 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3985 store_error_message(errmsg);
3986 return 1;
3987 }
3988
3989 if ( mode == CMD_VERIFY )
3990 return 0;
3991
3992 if ( configp->rf_noswitch == YES )
3993 noswitch = rf_nosw_code[subcode];
3994
3995 bursts = configp->rf_bursts[subcode];
3996
3997 rfhcode = rev_low_nybble(hc2code(hc));
3998
3999 mask = 1;
4000 for ( j = 0; j < 16; j++ ) {
4001 if ( bitmap & mask ) {
4002 nsw = (bitmap & 0x00c0) ? noswitch : 0; /* Units 1 or 9 */
4003 rfword = (rfhcode << 12) | rf_unit_code[j] | rf_func_code[subcode] | nsw;
4004 display_rf_xmit(subcode, rfword, bursts);
4005 if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
4006 sprintf(errmsg,
4007 "Command '%s': Unable to write to CM17A device.", label);
4008 store_error_message(errmsg);
4009 return 1;
4010 }
4011 rf_post_delay();
4012 }
4013 mask = mask << 1;
4014 }
4015
4016 break;
4017
4018 case 7 : /* RF Dim from full bright (turn Off before dimming) */
4019
4020 if ( aflags & A_MINUS ) {
4021 sprintf(errmsg,
4022 "Command '%s': '-' prefix invalid for this command", label);
4023 store_error_message(errmsg);
4024 return 1;
4025 }
4026 if ( bitmap == 0 ) {
4027 sprintf(errmsg,
4028 "Command '%s': No Unitcode specified", label);
4029 store_error_message(errmsg);
4030 return 1;
4031 }
4032
4033 case 4 : /* RF Dim */
4034 case 5 : /* RF Bright */
4035
4036 if ( (aflags & A_MINUS) )
4037 bitmap = 0;
4038
4039 sp = " ";
4040 if ( strchr(argv[2], '$') && mode == CMD_VERIFY )
4041 bursts = 1;
4042 else
4043 bursts = strtol(argv[2], &sp, 10);
4044
4045 if ( !strchr(" \t\n", *sp) || bursts < 1 || bursts > 255 ) {
4046 sprintf(errmsg, "Command '%s': Invalid argument '%s'", label, argv[2]);
4047 store_error_message(errmsg);
4048 return 1;
4049 }
4050
4051 if ( mode == CMD_VERIFY )
4052 break;
4053
4054 if ( subcode == 7 ) {
4055 subcode = 4;
4056 precode = 3; /* Turn Off before dimming */
4057 }
4058 else
4059 precode = 2; /* Turn On before dimming */
4060
4061 if ( configp->rf_noswitch == YES )
4062 noswitch = rf_nosw_code[precode];
4063
4064 burstgroup = min(bursts, (int)configp->rf_bursts[subcode]);
4065
4066 if ( rfmode == RF_FAST )
4067 burstgroup = bursts;
4068
4069 rfhcode = rev_low_nybble(hc2code(hc));
4070
4071 if ( bitmap == 0 ) {
4072 /* Transmit dims/brights only */
4073 rfword = (rfhcode << 12) | rf_func_code[subcode];
4074 display_rf_xmit(subcode, rfword, bursts);
4075 while ( bursts > 0 ) {
4076 if ( write_cm17a(rfword, min(bursts, burstgroup), rfmode) != 0 ) {
4077 sprintf(errmsg,
4078 "Command '%s': Unable to write to CM17A device.", label);
4079 store_error_message(errmsg);
4080 return 1;
4081 }
4082 rf_post_delay();
4083 bursts -= burstgroup;
4084 }
4085 }
4086 else {
4087 /* Precede dims/brights with an On (or Off) command to set the address. */
4088 mask = 1;
4089 savebursts = bursts;
4090 for ( j = 0; j < 16; j++ ) {
4091 if ( bitmap & mask ) {
4092 nsw = (bitmap & 0x00c0) ? noswitch : 0; /* Units 1 or 9 */
4093 rfonword = (rfhcode << 12) | rf_unit_code[j] | rf_func_code[precode] | nsw;
4094 display_rf_xmit(precode, rfonword, configp->rf_bursts[precode]);
4095 rfword = (rfhcode << 12) | rf_func_code[subcode];
4096 display_rf_xmit(subcode, rfword, bursts);
4097 if ( write_cm17a(rfonword, configp->rf_bursts[precode], RF_SLOW) != 0 ) {
4098 sprintf(errmsg,
4099 "Command '%s': Unable to write to CM17A device.", label);
4100 store_error_message(errmsg);
4101 return 1;
4102 }
4103 rf_post_delay();
4104
4105 while ( bursts > 0 ) {
4106 if ( write_cm17a(rfword, min(bursts, burstgroup), rfmode) != 0 ) {
4107 sprintf(errmsg,
4108 "Command '%s': Unable to write to CM17A device.", label);
4109 store_error_message(errmsg);
4110 return 1;
4111 }
4112 rf_post_delay();
4113 bursts -= burstgroup;
4114 }
4115 }
4116 mask = mask << 1;
4117 bursts = savebursts;
4118 }
4119 }
4120
4121 break;
4122
4123 case 9 : /* RF Arbitary 2-byte code */
4124 byte1 = byte2 = 0;
4125 sp = " ";
4126 if ( strchr(argv[1], '$') && mode == CMD_VERIFY )
4127 byte1 = 0x10;
4128 else
4129 byte1 = strtol(argv[1], &sp, 16);
4130 if ( !strchr(" \t\n", *sp) || byte1 < 0 || byte1 > 0xff ) {
4131 sprintf(errmsg,
4132 "Command '%s': Invalid argument '%s'", label, argv[1]);
4133 store_error_message(errmsg);
4134 return 1;
4135 }
4136
4137 sp = " ";
4138 if ( strchr(argv[2], '$') && mode == CMD_VERIFY )
4139 byte2 = 0x10;
4140 else
4141 byte2 = strtol(argv[2], &sp, 16);
4142 if ( !strchr(" \t\n", *sp) || byte2 < 0 || byte2 > 0xff ) {
4143 sprintf(errmsg,
4144 "Command '%s': Invalid argument '%s'", label, argv[2]);
4145 store_error_message(errmsg);
4146 return 1;
4147 }
4148
4149 if ( strchr(argv[3], '$') && mode == CMD_VERIFY )
4150 bursts = 1;
4151 else
4152 bursts = strtol(argv[3], &sp, 10);
4153
4154 if ( !strchr(" \t\n", *sp) || bursts < 1 || bursts > 255 ) {
4155 sprintf(errmsg,
4156 "Command '%s': Invalid <count> parameter", label);
4157 store_error_message(errmsg);
4158 return 1;
4159 }
4160
4161 if ( mode == CMD_VERIFY )
4162 return 0;
4163
4164 burstgroup = min(bursts, (int)configp->rf_bursts[subcode]);
4165 if ( rfmode == RF_FAST )
4166 burstgroup = bursts;
4167
4168 rfword = byte1 << 8 | byte2;
4169 display_rf_xmit(subcode, rfword, bursts);
4170
4171 while ( bursts > 0 ) {
4172 if ( write_cm17a(rfword, min(bursts, burstgroup), rfmode) != 0 ) {
4173 sprintf(errmsg,
4174 "Command '%s': Unable to write to CM17A device.", label);
4175 store_error_message(errmsg);
4176 return 1;
4177 }
4178 rf_farb_delay();
4179 bursts -= burstgroup;
4180 }
4181
4182 break;
4183
4184 case 8 : /* Reset CM17A */
4185
4186 if ( mode == CMD_VERIFY )
4187 return 0;
4188
4189 if ( reset_cm17a() != 0 ) {
4190 sprintf(errmsg,
4191 "Command '%s': Unable to reset CM17A device.", label);
4192 store_error_message(errmsg);
4193 return 1;
4194 }
4195
4196 break;
4197
4198 case 10 : /* farw - Multiple arbitrary 16 bit rfwords */
4199
4200 /* First check that each rfword is in range 0000-FFFF */
4201 for ( k = 1; k < argc; k++ ) {
4202 if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
4203 /* Fake a dummy value for scene verification */
4204 lxdata = 10;
4205 sp = " ";
4206 }
4207 else {
4208 lxdata = strtol(argv[k], &sp, 16);
4209 }
4210 if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4211 sprintf(errmsg,
4212 "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4213 store_error_message(errmsg);
4214 return 1;
4215 }
4216 }
4217
4218 if ( mode == CMD_VERIFY )
4219 break;
4220
4221 bursts = configp->rf_bursts[subcode];
4222 for ( k = 1; k < argc; k++ ) {
4223 lxdata = strtol(argv[k], &sp, 16);
4224 if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4225 sprintf(errmsg,
4226 "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4227 store_error_message(errmsg);
4228 return 1;
4229 }
4230 rfword = (unsigned int)lxdata;
4231 display_rf_xmit(10, rfword, bursts);
4232 if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
4233 sprintf(errmsg,
4234 "Command '%s': Unable to write to CM17A device.", label);
4235 store_error_message(errmsg);
4236 return 1;
4237 }
4238 rf_farw_delay();
4239 }
4240 break;
4241
4242 case 11 : /* flux - Multiple arbitrary 16 bit rfwords */
4243
4244 /* First two parameters are number of bursts and post-delay */
4245 /* in milliseconds. Both are decimal. */
4246 if ( strchr(argv[1], '&') && mode == CMD_VERIFY ) {
4247 bursts = 5;
4248 sp = " ";
4249 }
4250 else {
4251 bursts = strtol(argv[1], &sp, 10);
4252 }
4253 if ( !strchr(" \t\n\r", *sp) || bursts < 1 ) {
4254 sprintf(errmsg,
4255 "Command '%s': Invalid bursts number '%s'", label, argv[1]);
4256 store_error_message(errmsg);
4257 return 1;
4258 }
4259
4260 if ( strchr(argv[2], '&') && mode == CMD_VERIFY ) {
4261 flux_delay = 200;
4262 sp = " ";
4263 }
4264 else {
4265 flux_delay = strtol(argv[2], &sp, 10);
4266 }
4267 if ( !strchr(" \t\n\r", *sp) || flux_delay < 0 ) {
4268 sprintf(errmsg,
4269 "Command '%s': Invalid delay '%s'", label, argv[2]);
4270 store_error_message(errmsg);
4271 return 1;
4272 }
4273
4274 /* Subsequent parameters are 16-bit hex rfwords.*/
4275 /* Check that each rfword is in range 0000-FFFF */
4276 for ( k = 3; k < argc; k++ ) {
4277 if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
4278 /* Fake a dummy value for scene verification */
4279 lxdata = 10;
4280 sp = " ";
4281 }
4282 else {
4283 lxdata = strtol(argv[k], &sp, 16);
4284 }
4285 if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4286 sprintf(errmsg,
4287 "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4288 store_error_message(errmsg);
4289 return 1;
4290 }
4291 }
4292
4293 if ( mode == CMD_VERIFY )
4294 break;
4295
4296 for ( k = 3; k < argc; k++ ) {
4297 lxdata = strtol(argv[k], &sp, 16);
4298 if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4299 sprintf(errmsg,
4300 "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4301 store_error_message(errmsg);
4302 return 1;
4303 }
4304 rfword = (unsigned int)lxdata;
4305 display_rf_xmit(11, rfword, bursts);
4306 if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
4307 sprintf(errmsg,
4308 "Command '%s': Unable to write to CM17A device.", label);
4309 store_error_message(errmsg);
4310 return 1;
4311 }
4312 rf_flux_delay(flux_delay);
4313 }
4314 break;
4315
4316 default :
4317 break;
4318 }
4319 break;
4320 #endif /* End of HASCM17A */
4321
4322 default :
4323 sprintf(errmsg, "Command '%s': Not implemented", label);
4324 store_error_message(errmsg);
4325 return 1;
4326 }
4327
4328 return 0;
4329 }
4330
4331 /*---------------------------------------------------------------+
4332 | Generate the elements for an uploaded macro and pass back |
4333 | through the argument list. Return 0 if successful, otherwise |
4334 | write message to error handling routine and return 1. |
4335 +---------------------------------------------------------------*/
macro_command(int argc,char * argv[],int * nelem,int * nbytes,unsigned char * elements)4336 int macro_command( int argc, char *argv[],
4337 int *nelem, int *nbytes, unsigned char *elements )
4338 {
4339
4340 int j, k, icmd, nargs, radix, subcode, subsubcode;
4341 unsigned int mask, aflags, bitmap, sendmap;
4342 unsigned long flags;
4343 char *label, *sp;
4344 unsigned char *ep;
4345 char hc;
4346 char errmsg[128];
4347 int dimval, dimcode, delay, level;
4348 unsigned char group, subgroup;
4349 unsigned char cmdcode, hcode, switchv, switchcode;
4350 int minargs, maxargs;
4351 /* unsigned */ int xdata;
4352 static char reorder[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
4353 extern int parse_ext3_group ( char *, unsigned char *, unsigned char * );
4354
4355 clear_error_message();
4356
4357 flags = 0;
4358
4359 /* Handle a few special cases */
4360
4361 if ( strcmp(argv[0], "function") == 0 ) {
4362 /* Special case: "function" command */
4363 if ( argc < 2 ) {
4364 store_error_message("Command 'function': Too few parameters");
4365 return 1;
4366 }
4367 argv++;
4368 argc--;
4369 flags |= F_NOA;
4370 }
4371 else if ( strcmp(argv[0], "turn") == 0 ) {
4372 /* Special case: "turn" command */
4373 if ( argc < 3 ) {
4374 store_error_message("Command 'turn': Too few parameters");
4375 return 1;
4376 }
4377 flags |= F_TRN;
4378 argv++;
4379 argc--;
4380 /* Swap first two parameters */
4381 sp = argv[0];
4382 argv[0] = argv[1];
4383 argv[1] = sp;
4384 }
4385 nargs = argc - 1;
4386
4387 /* Identify the command */
4388 for ( icmd = 0; icmd < nx10cmds; icmd++ ) {
4389 if ( !strcmp(argv[0], x10command[icmd].label) )
4390 break;
4391 }
4392 if ( icmd == nx10cmds ) {
4393 sprintf(errmsg, "Invalid command '%s'", argv[0]);
4394 store_error_message(errmsg);
4395 return 1;
4396 }
4397
4398 label = x10command[icmd].label;
4399 minargs = x10command[icmd].minargs;
4400 maxargs = x10command[icmd].maxargs;
4401 flags |= x10command[icmd].flags;
4402 cmdcode = x10command[icmd].code;
4403 subcode = x10command[icmd].subcode;
4404 subsubcode = x10command[icmd].subsubcode;
4405 radix = (flags & F_ARB) ? 16 : 10;
4406
4407 /* Make sure this command is available for uploaded macros */
4408 if ( flags & F_NMA || flags & F_HLP ) {
4409 sprintf(errmsg, "'%s' command is invalid for uploaded macros", label);
4410 store_error_message(errmsg);
4411 return 1;
4412 }
4413
4414 if ( flags & F_NMAI ) {
4415 sprintf(errmsg, "Warning: '%s' command is ignored for uploaded macros", label);
4416 store_error_message(errmsg);
4417 *nelem = *nbytes = 0;
4418 return 0;
4419 }
4420
4421 if ( nargs < minargs ) {
4422 sprintf(errmsg, "Command '%s': Too few arguments", label);
4423 store_error_message(errmsg);
4424 return 1;
4425 }
4426 if ( maxargs != -1 && nargs > maxargs ) {
4427 cmd_usage(stderr, icmd );
4428 sprintf(errmsg, "Command '%s': Too many arguments", label);
4429 store_error_message(errmsg);
4430 return 1;
4431 }
4432
4433 /* Handle a few special cases */
4434
4435 if ( flags & F_ARB ) {
4436 /* If arbitrary extended code, get subcode and adjust argument count */
4437 subcode = (int)strtol(argv[1], &sp, radix);
4438 argv++;
4439 argc--;
4440 nargs--;
4441 if ( !strchr(" \t\n", *sp) || subcode < 0 || subcode > 0xff ) {
4442 sprintf(errmsg, "Command '%s': Extended code function outside range 00-0xff", label);
4443 store_error_message(errmsg);
4444 return 1;
4445 }
4446 }
4447
4448 /* Only commands with command code <= 6 or code == 26 are valid with "turn" */
4449 if ( flags & F_TRN && cmdcode > 6 && cmdcode != 26 ) {
4450 sprintf(errmsg,
4451 "Command '%s' cannot be used with the 'turn' command", label);
4452 store_error_message(errmsg);
4453 return 1;
4454 }
4455
4456 /* Default HC|Unit */
4457 hc = '_';
4458 bitmap = 0;
4459 aflags = A_VALID;
4460
4461 if ( !(flags & F_NUM) && nargs > 0 ) {
4462 /* parse it as an alias or HC|Units token. */
4463 aflags = parse_addr(argv[1], &hc, &bitmap);
4464 }
4465
4466 if ( !(aflags & A_VALID) ) {
4467 sprintf(errmsg, "Command '%s': ", label);
4468 add_error_prefix(errmsg);
4469 return 1;
4470 }
4471
4472 /* Make sure a positional parameter didn't slip through */
4473 if ( aflags & A_DUMMY ) {
4474 sprintf(errmsg,
4475 "Command '%s': Unresolved positional parameter '%s'",
4476 label, argv[1]);
4477 store_error_message(errmsg);
4478 return 1;
4479 }
4480
4481 if ( !(aflags & A_HCODE) && !(flags & F_NUM) && minargs > 0 ) {
4482 sprintf(errmsg, "Command '%s': Housecode required", label);
4483 store_error_message(errmsg);
4484 return 1;
4485 }
4486
4487 switchv = cmdcode;
4488
4489 switch ( switchv ) {
4490
4491 case 20 : /* Send address bytes only */
4492 case 21 : /* Function only */
4493 case 22 : /* Send arbitrary series of hex bytes */
4494 case 23 : /* Send (quoted) text message */
4495 case 25 : /* Pause */
4496 case 27 : /* Turn */
4497 case 28 : /* Temp_Req */
4498 case 99 : /* Legacy commands */
4499 /* We should never get here */
4500 sprintf(errmsg, "Internal error - Invalid macro command '%s'", label);
4501 store_error_message(errmsg);
4502 return 1;
4503
4504 case 8 : /* Hail */
4505 case 9 : /* Hail Ack */
4506 if ( !(aflags & A_HCODE) ) {
4507 hc = configp->housecode;
4508 }
4509 case 0 : /* All Units Off */
4510 case 1 : /* All Lights On */
4511 case 6 : /* All Lights Off */
4512 case 12 : /* Data Transfer */
4513 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4514 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4515 store_error_message(errmsg);
4516 *errmsg = '\0';
4517 bitmap = 0;
4518 }
4519 if ( bitmap == 0 && (aflags & A_PLUS || configp->force_addr == YES) )
4520 bitmap = 1;
4521
4522 case 2 : /* On */
4523 case 3 : /* Off */
4524 case 13 : /* Status On */
4525 case 14 : /* Status Off */
4526 if ( aflags & A_MINUS || flags & F_NOA )
4527 bitmap = 0;
4528 else if ( bitmap == 0 && !(flags & F_ALL) ) {
4529 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4530 store_error_message(errmsg);
4531 return 1;
4532 }
4533
4534 hcode = hc2code(hc);
4535
4536 elements[0] = (hcode << 4) | cmdcode;
4537 elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4538 elements[2] = (unsigned char)((bitmap & 0x00ff));
4539 *nelem = 1;
4540 *nbytes = 3;
4541
4542 break;
4543
4544 case 4 : /* Dim */
4545 case 5 : /* Bright */
4546 dimval = (int)strtol(argv[2], &sp, 10);
4547 if ( !strchr(" \t\n", *sp) ) {
4548 sprintf(errmsg,
4549 "Command '%s': Invalid Dim/Bright parameter '%s'", label, argv[2]);
4550 store_error_message(errmsg);
4551 return 1;
4552 }
4553 else if ( configp->restrict_dims == YES && (dimval < 1 || dimval > 22) ) {
4554 sprintf(errmsg,
4555 "Command '%s': Dim/Bright value outside range 1-22", label);
4556 store_error_message(errmsg);
4557 return 1;
4558 }
4559 else if ( configp->restrict_dims == NO && (dimval < 0 || dimval > 31) ) {
4560 sprintf(errmsg,
4561 "Command '%s': Dim/Bright value outside range 0-31", label);
4562 store_error_message(errmsg);
4563 return 1;
4564 }
4565
4566 if ( aflags & A_MINUS || flags & F_NOA )
4567 bitmap = 0;
4568 else if ( bitmap == 0 ) {
4569 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4570 store_error_message(errmsg);
4571 return 1;
4572 }
4573
4574 hcode = hc2code(hc);
4575
4576 if ( flags & F_ONN ) {
4577 /* On signal needed before bright/dim (WS467-1 redesign) */
4578 elements[0] = (hcode << 4) | 0x02;
4579 elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4580 elements[2] = (unsigned char)((bitmap & 0x00ff));
4581 elements[3] = (hcode << 4) | cmdcode;
4582 elements[4] = 0;
4583 elements[5] = 0;
4584 elements[6] = (unsigned char)(dimval | ((flags & F_BRI) ? 0x80 : 0));
4585 *nelem = 2;
4586 *nbytes = 7;
4587 }
4588 else {
4589 elements[0] = (hcode << 4) | cmdcode;
4590 elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4591 elements[2] = (unsigned char)((bitmap & 0x00ff));
4592 elements[3] = (unsigned char)(dimval | ((flags & F_BRI) ? 0x80 : 0));
4593 *nelem = 1;
4594 *nbytes = 4;
4595 }
4596
4597 break;
4598
4599 case 10 : /* Old style preset 1 */
4600 case 11 : /* Old style preset 2 */
4601
4602 if ( nargs == 2 )
4603 /* Limited preset for macros */
4604 dimval = (int)strtol(argv[2], &sp, 10);
4605 else
4606 /* preset level only */
4607 dimval = (int)strtol(argv[1], &sp, 10);
4608
4609 if ( !strchr(" \t\n", *sp) || dimval < 1 || dimval > 32 ) {
4610 sprintf(errmsg, "Command '%s': Level outside range 1-32", label);
4611 store_error_message(errmsg);
4612 return 1;
4613 }
4614
4615 if ( (dimcode = dimval) > 16 ) {
4616 cmdcode = 11;
4617 dimcode -= 16;
4618 }
4619 dimcode = rev_low_nybble(dimcode - 1);
4620
4621 if ( nargs == 2 && (hcode = hc2code(hc)) != dimcode ) {
4622 sprintf(errmsg, "Command '%s': Preset level %d not supported for housecode %c",
4623 label, dimval, hc);
4624 store_error_message(errmsg);
4625 return 1;
4626 }
4627
4628 if ( aflags & A_MINUS || flags & F_NOA )
4629 bitmap = 0;
4630 else if ( bitmap == 0 ) {
4631 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4632 store_error_message(errmsg);
4633 return 1;
4634 }
4635
4636 elements[0] = (dimcode << 4) | cmdcode;
4637 elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4638 elements[2] = (unsigned char)((bitmap & 0x00ff));
4639 *nelem = 1;
4640 *nbytes = 3;
4641
4642 break;
4643
4644 case 15 : /* Status Req */
4645 sendmap = bitmap;
4646 if ( aflags & A_MINUS || flags & F_NOA ) {
4647 bitmap = 1;
4648 sendmap = 0;
4649 }
4650 else if ( bitmap == 0 ) {
4651 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4652 store_error_message(errmsg);
4653 return 1;
4654 }
4655
4656 hcode = hc2code(hc);
4657
4658 ep = elements;
4659 *nelem = 0;
4660 *nbytes = 0;
4661
4662 /* Send each address/command pair individually to allow */
4663 /* associating a response to a particular unit code */
4664 mask = 1;
4665 for ( k = 0; k < 16; k++ ) {
4666 if ( bitmap & mask ) {
4667 *ep++ = (hcode << 4) | cmdcode;
4668 *ep++ = (unsigned char)((bitmap & 0xff00) >> 8);
4669 *ep++ = (unsigned char)((bitmap & 0x00ff));
4670 *nelem += 1;
4671 *nbytes += 3;
4672 }
4673 mask = mask << 1;
4674 }
4675 break;
4676
4677 case 24 : /* Send All_Units_Off to all Housecodes */
4678 bitmap = (configp->force_addr == YES) ? 1 : 0;
4679 ep = elements;
4680 for ( j = 0; j < 16; j++ ) {
4681 hcode = reorder[j]; /* Send housecodes in alphabetic order */
4682 *ep++ = (hcode << 4); /* cmdcode = 0 */
4683 *ep++ = (unsigned char)((bitmap & 0xff00) >> 8);
4684 *ep++ = (unsigned char)((bitmap & 0x00ff));
4685 }
4686 *nelem = 16;
4687 *nbytes = 48;
4688
4689 break;
4690
4691 case 26 : /* All Units On (same as 'on H1-16') */
4692 if ( bitmap != 0 && !(aflags & A_MINUS) ) {
4693 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4694 store_error_message(errmsg);
4695 }
4696 bitmap = 0xFFFF;
4697
4698 hcode = hc2code(hc);
4699
4700 elements[0] = (hcode << 4) | 2 ;
4701 elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4702 elements[2] = (unsigned char)((bitmap & 0x00ff));
4703 *nelem = 1;
4704 *nbytes = 3;
4705
4706 break;
4707
4708 case 32 : /* Delay in minutes */
4709 delay = (int)strtol(argv[1], &sp, 10);
4710 if ( !strchr(" \t\n", *sp) ) {
4711 sprintf(errmsg,
4712 "Command '%s': Invalid character '%c' in delay value", label, *sp);
4713 store_error_message(errmsg);
4714 return 1;
4715 }
4716 if ( delay < 0 || delay > 240 ) {
4717 sprintf(errmsg, "Command '%s': value %d outside range 0-240 minutes",
4718 label, delay);
4719 store_error_message(errmsg);
4720 return 1;
4721 }
4722
4723 sprintf(errmsg, "Command '%s': Not yet implemented for macros", label);
4724 store_error_message(errmsg);
4725 return 1;
4726
4727 case 7 : /* Extended Code commands */
4728 sendmap = (aflags & A_PLUS) ? bitmap : 0 ;
4729 if ( sendmap == 0 && configp->force_addr == YES )
4730 sendmap = 0xFFFF;
4731
4732 hcode = hc2code(hc);
4733 xdata = 0;
4734
4735 if ( flags & F_ARB )
4736 switchcode = 0xff;
4737 else
4738 switchcode = subcode;
4739
4740 switch ( switchcode ) {
4741 case 0x30 : /* Extended Group Include */
4742 if ( bitmap == 0 ) {
4743 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4744 store_error_message(errmsg);
4745 return 1;
4746 }
4747
4748 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4749 sprintf(errmsg,
4750 "Command '%s': %s", label, error_message());
4751 store_error_message(errmsg);
4752 return 1;
4753 }
4754
4755 xdata = (group << 6);
4756 if ( subgroup & 0x80u ) {
4757 xdata |= (subgroup & 0x0fu) | 0x20u;
4758 }
4759
4760 break;
4761
4762 case 0x31 : /* Extended Preset Dim */
4763 if ( bitmap == 0 ) {
4764 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4765 store_error_message(errmsg);
4766 return 1;
4767 }
4768
4769 /* Get dim level */
4770 xdata = (int)strtol(argv[2], &sp, 10);
4771 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 63 ) {
4772 sprintf(errmsg, "Command '%s': Extended Preset level outside range 0-63", label);
4773 store_error_message(errmsg);
4774 return 1;
4775 }
4776 break;
4777
4778 case 0x32 : /* Extended Group Include at Level*/
4779 if ( bitmap == 0 ) {
4780 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4781 store_error_message(errmsg);
4782 return 1;
4783 }
4784
4785 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4786 sprintf(errmsg,
4787 "Command '%s': %s", label, error_message());
4788 store_error_message(errmsg);
4789 return 1;
4790 }
4791
4792 xdata = (group << 6);
4793
4794 if ( subgroup & 0x80u ) {
4795 sprintf(errmsg,
4796 "Command '%s': Cannot specify group.subgroup with level", label);
4797 store_error_message(errmsg);
4798 return 1;
4799 }
4800
4801 level = (int)strtol(argv[3], &sp, 10);
4802 if ( !strchr(" \t\n", *sp) || level < 0 || level > 63 ) {
4803 sprintf(errmsg,
4804 "Command '%s': Level outside range 0-63", label);
4805 store_error_message(errmsg);
4806 return 1;
4807 }
4808 xdata |= (level & 0x3f);
4809
4810 break;
4811
4812 case 0x33 : /* Extended All On */
4813 case 0x34 : /* Extended All Off */
4814 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4815 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4816 store_error_message(errmsg);
4817 *errmsg = '\0';
4818 bitmap = 1;
4819 }
4820 else if ( bitmap == 0 )
4821 bitmap = 1;
4822
4823 if ( aflags & A_PLUS )
4824 sendmap = bitmap;
4825
4826 break;
4827
4828 case 0x35 : /* Remove from group(s) */
4829 if ( (xdata = (int)list2linmap(argv[2], 0, 3)) == 0 ) {
4830 sprintf(errmsg, "Command '%s': Group number %s", label, error_message());
4831 store_error_message(errmsg);
4832 return 1;
4833 }
4834
4835 if ( subsubcode == 0 ) {
4836 if ( bitmap == 0 ) {
4837 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4838 store_error_message(errmsg);
4839 return 1;
4840 }
4841 }
4842 else {
4843 xdata |= 0xf0;
4844 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) &&
4845 configp->force_addr == NO) {
4846 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4847 store_error_message(errmsg);
4848 *errmsg = '\0';
4849 bitmap = 1;
4850 }
4851 else if ( bitmap == 0 ) {
4852 bitmap = 1;
4853 }
4854 }
4855
4856 if ( aflags & A_PLUS )
4857 sendmap = bitmap;
4858
4859 break;
4860
4861 case 0x36 : /* Execute group functions */
4862 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4863 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4864 store_error_message(errmsg);
4865 *errmsg = '\0';
4866 bitmap = 1;
4867 }
4868 else if ( bitmap == 0 ) {
4869 bitmap = 1;
4870 }
4871
4872 if ( aflags & A_PLUS )
4873 sendmap = bitmap;
4874
4875 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4876 sprintf(errmsg,
4877 "Command '%s': %s", label, error_message());
4878 store_error_message(errmsg);
4879 return 1;
4880 }
4881
4882 xdata = group << 6;
4883
4884 if ( subgroup & 0x80u ) {
4885 xdata |= 0x20u | (subgroup & 0x0f);
4886 }
4887
4888 if ( subsubcode == 1 ) {
4889 xdata |= 0x10u;
4890 }
4891
4892 break;
4893
4894 case 0x37 : /* Extended Status Requests */
4895 if ( bitmap == 0 ) {
4896 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4897 store_error_message(errmsg);
4898 return 1;
4899 }
4900
4901 if ( subsubcode == 0 ) {
4902 /* Output status request */
4903 xdata = 0;
4904 }
4905 else if ( subsubcode == 1 ) {
4906 /* Powerup status request */
4907 xdata = 0x10;
4908 }
4909 else {
4910 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4911 sprintf(errmsg,
4912 "Command '%s': %s", label, error_message());
4913 store_error_message(errmsg);
4914 return 1;
4915 }
4916
4917 xdata = (group << 6) | 0x20u;
4918
4919 if ( subgroup & 0x80u ) {
4920 xdata |= 0x10u | (subgroup & 0x0f);
4921 }
4922 }
4923
4924 break;
4925
4926 case 0x3b : /* Extended Configure Auto Status_Ack Mode */
4927 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4928 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4929 store_error_message(errmsg);
4930 *errmsg = '\0';
4931 bitmap = 1;
4932 }
4933 else if ( bitmap == 0 )
4934 bitmap = 1;
4935
4936 if ( aflags & A_PLUS )
4937 sendmap = bitmap;
4938
4939 /* Get mode */
4940 xdata = (int)strtol(argv[2], &sp, 10);
4941 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 3 ) {
4942 sprintf(errmsg, "Command '%s': Extended Config Mode outside range 0-3", label);
4943 store_error_message(errmsg);
4944 return 1;
4945 }
4946 break;
4947
4948 case 0x3c : /* Extended group bright/dim functions */
4949 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4950 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4951 store_error_message(errmsg);
4952 *errmsg = '\0';
4953 bitmap = 1;
4954 }
4955 else if ( bitmap == 0 ) {
4956 bitmap = 1;
4957 }
4958
4959 if ( aflags & A_PLUS )
4960 sendmap = bitmap;
4961
4962 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4963 sprintf(errmsg,
4964 "Command '%s': %s", label, error_message());
4965 store_error_message(errmsg);
4966 return 1;
4967 }
4968
4969 xdata = group << 6;
4970
4971 if ( subgroup & 0x80u ) {
4972 xdata |= 0x20u | (subgroup & 0x0f);
4973 }
4974
4975 if ( subsubcode == 1 ) {
4976 xdata |= 0x10u;
4977 }
4978
4979 break;
4980
4981 case 0xfd : /* Extended OFF ( = Extended Preset Dim level 0) */
4982 if ( bitmap == 0 ) {
4983 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4984 store_error_message(errmsg);
4985 return 1;
4986 }
4987 subcode = 0x31;
4988 xdata = 0;
4989 break;
4990
4991 case 0xfe : /* Extended ON ( = Extended Preset Dim level 63) */
4992 if ( bitmap == 0 ) {
4993 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4994 store_error_message(errmsg);
4995 return 1;
4996 }
4997 subcode = 0x31;
4998 xdata = 63;
4999 break;
5000
5001 case 0xff : /* Arbitrary Extended Function */
5002 if ( bitmap == 0 ) {
5003 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
5004 return 1;
5005 }
5006 xdata = (int)strtol(argv[2], &sp, 16);
5007 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
5008 sprintf(errmsg,
5009 "Command '%s': Data '%s' outside range 0-FF", label, argv[2]);
5010 store_error_message(errmsg);
5011 return 1;
5012 }
5013 break ;
5014
5015 case 0x01 : /* Shutter open, observe limit */
5016 case 0x03 : /* Shutter open, ignore limit */
5017 case 0x02 : /* Set shutter limit */
5018 if ( bitmap == 0 ) {
5019 sprintf(errmsg, "Command '%s': No Unitcode specified", label);
5020 store_error_message(errmsg);
5021 return 1;
5022 }
5023
5024 /* Get dim level */
5025 xdata = (int)strtol(argv[2], &sp, 10);
5026 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 25 ) {
5027 sprintf(errmsg, "Command '%s': Shutter level outside range 0-25", label);
5028 store_error_message(errmsg);
5029 return 1;
5030 }
5031 break;
5032
5033 case 0x04 : /* Shutter Open All */
5034 case 0x0B : /* Shutter Close All */
5035
5036 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
5037 sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
5038 store_error_message(errmsg);
5039 *errmsg = '\0';
5040 bitmap = 1;
5041 }
5042 else if ( bitmap == 0 )
5043 bitmap = 1;
5044
5045 if ( aflags & A_PLUS )
5046 sendmap = bitmap;
5047
5048 break;
5049
5050 default :
5051 sprintf(errmsg, "Command not implemented");
5052 store_error_message(errmsg);
5053 return 1;
5054 }
5055
5056 mask = 1;
5057 ep = elements;
5058 *nelem = 0;
5059 *nbytes = 0;
5060 for ( j = 0; j < 16; j++ ) {
5061 if ( bitmap & mask ) {
5062 *ep++ = (hcode << 4) | cmdcode;
5063 *ep++ = (unsigned char)(((sendmap & mask) & 0xff00) >> 8);
5064 *ep++ = (unsigned char)(((sendmap & mask) & 0x00ff));
5065 *ep++ = j;
5066 *ep++ = (unsigned char)(xdata & 0xff);
5067 *ep++ = (unsigned char)(subcode & 0xff);
5068 *nelem += 1;
5069 *nbytes += 6;
5070 }
5071 mask = mask << 1;
5072 }
5073 break;
5074
5075 default :
5076 sprintf(errmsg, "Command not implemented");
5077 store_error_message(errmsg);
5078 return 1;
5079 }
5080
5081 return 0;
5082 }
5083
5084
5085 /*---------------------------------------------------------------+
5086 | Return the maximum number of positional parameters in a |
5087 | scene, i.e., the highest integer 'nn' represented in a token |
5088 | a token of the form "$nn". Return -1 if the "nn" is missing, |
5089 | is less than 1, or is not an integer. |
5090 +---------------------------------------------------------------*/
max_parms(int tokc,char * tokv[])5091 int max_parms ( int tokc, char *tokv[] )
5092 {
5093 int j, parmno, count, maxparm = 0;
5094 int bracket;
5095 char *sp, *tp;
5096 char errmsg[128];
5097 int parmchk[1000];
5098
5099 for ( j = 0; j < 32; j++ )
5100 parmchk[j] = 0;
5101
5102 for ( j = 1; j < tokc; j++ ) {
5103 tp = tokv[j];
5104 while ((sp = strchr(tp, '$')) != NULL ) {
5105 sp++;
5106 bracket = (*sp == '{') ? 1 : 0 ;
5107 parmno = (int)strtol(sp + bracket, &sp, 10);
5108 if ( (bracket && *sp != '}') || parmno < 1 ) {
5109 sprintf(errmsg,
5110 "Command '%s': Invalid positional parameter in '%s'",
5111 tokv[0], tokv[j]);
5112 store_error_message(errmsg);
5113 return -1;
5114 }
5115 if ( parmno > configp->max_pparms ) {
5116 sprintf(errmsg, "Too many (%d) positional parameters - max %d",
5117 parmno, configp->max_pparms);
5118 store_error_message(errmsg);
5119 return -1;
5120 }
5121 parmchk[parmno] = 1;
5122 maxparm = max(maxparm, parmno);
5123 if ( bracket )
5124 sp++;
5125 tp = sp;
5126 }
5127 }
5128
5129 count = 0;
5130 for ( j = 1; j < 32; j++ )
5131 count += parmchk[j];
5132
5133 /* Warn the user if all positional parameters within the */
5134 /* range 1 through maxparm arene't used in the scene. */
5135 if ( count < maxparm ) {
5136 sprintf(errmsg,
5137 "Warning: Not all positional parameters specified are used");
5138 store_error_message(errmsg);
5139 }
5140 return maxparm;
5141 }
5142
5143
5144 /*---------------------------------------------------------------+
5145 | Verify that the syntax of commands in a scene is correct. |
5146 +---------------------------------------------------------------*/
verify_scene(char * body)5147 int verify_scene ( char *body )
5148 {
5149 int j;
5150 int cmdc, tokc, count;
5151 char **cmdv, **tokv;
5152 char *sp;
5153
5154 sp = strdup(body);
5155
5156 /* Separate scene into individual commands */
5157 tokenize(sp, ";", &cmdc, &cmdv);
5158 count = 0;
5159 for ( j = 0; j < cmdc; j++ ) {
5160 strtrim(cmdv[j]);
5161 if ( *cmdv[j] == '\0' )
5162 continue;
5163 count++;
5164 /* Separate commands into individual tokens */
5165 tokenize(cmdv[j], " \t", &tokc, &tokv);
5166 /* Verify the individual command */
5167 if ( direct_command(tokc, tokv, CMD_VERIFY) != 0 ) {
5168 free(tokv);
5169 free(cmdv);
5170 free(sp);
5171 return 1;
5172 }
5173 free(tokv);
5174 }
5175 free(cmdv);
5176 free(sp);
5177 if ( !count ) {
5178 store_error_message("Warning: Contains no commands.");
5179 }
5180 return 0;
5181 }
5182
5183
5184 /*---------------------------------------------------------------+
5185 | Replace the positional parameters in the members of list tokv |
5186 | with actual values. It is presumed that the number of |
5187 | members in 'actual' has already be verified to be sufficient. |
5188 | The new _members_ in tokvp must be freed after use. |
5189 +---------------------------------------------------------------*/
replace_dummy_parms(int tokc,char *** tokvp,char * actual[])5190 void replace_dummy_parms ( int tokc, char ***tokvp, char *actual[] )
5191 {
5192 int j, parmno, bracket;
5193 char *sp;
5194 char buffer1[128];
5195 char buffer2[128];
5196
5197 for ( j = 0; j < tokc; j++ ) {
5198 strncpy2(buffer1, (*tokvp)[j], sizeof(buffer1) - 1);
5199
5200 while ( (sp = strchr(buffer1, '$')) != NULL ) {
5201 strncpy2(buffer2, buffer1, (sp - buffer1));
5202 sp++;
5203 bracket = (*sp == '{') ? 1 : 0 ;
5204 parmno = (int)strtol(sp + bracket, &sp, 10);
5205 strncat(buffer2, actual[parmno], sizeof(buffer2) - 1 - strlen(buffer2));
5206 if ( bracket )
5207 sp++;
5208 strncat(buffer2, sp, sizeof(buffer2) - 1 - strlen(buffer2));
5209 strncpy2(buffer1, buffer2, sizeof(buffer1) - 1);
5210 }
5211 if ( ((*tokvp)[j] = strdup(buffer1)) == NULL ) {
5212 fprintf(stderr, "Unable to allocate memory in replace_dummy_parms()\n");
5213 exit(1);
5214 }
5215 }
5216
5217 return;
5218 }
5219
5220 /*---------------------------------------------------------------+
5221 | Message when 'heyu linewidth NN' is entered at the PC command |
5222 | prompt. This command is intended to be intercepted by the |
5223 | WEBCLI web interface and be an instruction to that interface. |
5224 +---------------------------------------------------------------*/
display_linewidth_msg(void)5225 int display_linewidth_msg ( void )
5226 {
5227 printf("Dummy command - intended for web interface.\n");
5228 return 0;
5229 }
5230
5231 /*---------------------------------------------------------------+
5232 | Handler for direct commands and scenes entered from the |
5233 | command line. |
5234 +---------------------------------------------------------------*/
c_command(int argc,char * argv[])5235 int c_command( int argc, char *argv[] )
5236 {
5237 int j, k, m, cmdc, tokc, retcode;
5238 char **cmdv, **tokv;
5239 char *sp;
5240 SCENE *scenep;
5241
5242 scenep = config.scenep;
5243
5244 /* Determine whether 2nd level command under "cmd". */
5245 /* If so, offset all arguments by 1. */
5246 if ( strcmp(argv[1], "cmd") == 0 ) {
5247 argv++;
5248 argc--;
5249 }
5250 if ( argc < 2 )
5251 return 1;
5252
5253 /* Special case: "help" command */
5254 if ( strcmp(argv[1], "help") == 0 ) {
5255 if ( argc < 3 )
5256 command_help("");
5257 else
5258 command_help(argv[2]);
5259 return 0;
5260 }
5261
5262 /* Special case: "syn" (display synonyms) command */
5263 if ( strcmp(argv[1], "syn") == 0 ) {
5264 display_syn();
5265 return 0;
5266 }
5267
5268 /* Special case: "linewidth" dummy command */
5269 if ( strcmp(argv[1], "linewidth") == 0 ) {
5270 display_linewidth_msg();
5271 return 0;
5272 }
5273
5274 /* If the argument contains one or more ';' characters, it's */
5275 /* tentatively a compound ("command-line macro") command. */
5276 #if 0
5277 if ( (sp = strstr(argv[1], "\\;")) != NULL ) {
5278 strncpy(sp, "; ", 2);
5279 printf("argv[1] = '%s'\n", argv[1]);
5280 }
5281 #endif
5282 if ( strchr(argv[1], ';') || strchr(argv[1], ' ') || strchr(argv[1], '\t') ) {
5283 sp = argv[1];
5284 while ( (sp = strstr(sp, "\\;")) != NULL ) {
5285 strncpy(sp, "; ", 2);
5286 sp++;
5287 }
5288 if ( verify_scene(argv[1]) != 0 ) {
5289 fprintf(stderr, "%s\n", error_message());
5290 clear_error_message();
5291 return 1;
5292 }
5293 /* Separate commands delimited by ';' */
5294 sp = strdup(argv[1]);
5295 tokenize(sp, ";", &cmdc, &cmdv);
5296 for ( k = 0; k < cmdc; k++ ) {
5297 strtrim(cmdv[k]);
5298 if ( *cmdv[k] == '\0' )
5299 continue;
5300
5301 tokenize(cmdv[k], " \t", &tokc, &tokv);
5302
5303 retcode = direct_command(tokc, tokv, CMD_RUN);
5304
5305 free(tokv);
5306
5307 if ( retcode != 0 ) {
5308 /* Display fatal message from error handler */
5309 fprintf(stderr, "%s\n", error_message());
5310 clear_error_message();
5311 free(cmdv);
5312 return retcode;
5313 }
5314 }
5315 free(cmdv);
5316 free(sp);
5317
5318 return 0;
5319 }
5320
5321
5322 /* See if it's a user-defined scene */
5323 if ( (j = lookup_scene(scenep, argv[1])) >= 0 ) {
5324
5325 if ( argc < (scenep[j].nparms + 2) ) {
5326 fprintf(stderr,
5327 "(Config %02d: %s '%s'): Too few parameters - %d required.\n",
5328 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5329 scenep[j].nparms);
5330 return 1;
5331 }
5332 else if ( argc > (scenep[j].nparms + 2) ) {
5333 fprintf(stderr,
5334 "(Config %02d: %s '%s'): Too many parameters - only %d accepted.\n",
5335 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5336 scenep[j].nparms);
5337 return 1;
5338 }
5339
5340 sp = strdup(scenep[j].body);
5341 /* Separate scene commands delimited by ';' */
5342 tokenize(sp, ";", &cmdc, &cmdv);
5343 for ( k = 0; k < cmdc; k++ ) {
5344 strtrim(cmdv[k]);
5345 if ( *cmdv[k] == '\0' )
5346 continue;
5347 tokenize(cmdv[k], " ", &tokc, &tokv);
5348
5349 /* Substitute for any positional parameters */
5350 /* This function allocates new memory for */
5351 /* each parameter since the replacement may */
5352 /* be longer than the string in argv, so */
5353 /* each must afterwarsds be freed. */
5354 replace_dummy_parms(tokc, &tokv, argv + 1);
5355
5356 retcode = direct_command(tokc, tokv, CMD_RUN);
5357
5358 /* Free each member of tokv, then tokv itself */
5359 for ( m = 0; m < tokc; m++ )
5360 free(tokv[m]);
5361 free(tokv);
5362
5363 if ( retcode != 0 ) {
5364 /* Display fatal message from error handler */
5365 fprintf(stderr, "(Config %02d: %s '%s'): %s\n",
5366 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5367 error_message());
5368 clear_error_message();
5369 free(cmdv);
5370 free(sp);
5371 return retcode;
5372 }
5373 else if ( *error_message() ) {
5374 /* Display warning from error handler */
5375 fprintf(stderr, "(Config %02d: %s '%s'): %s\n",
5376 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5377 error_message());
5378 }
5379 clear_error_message();
5380 }
5381 free(cmdv);
5382 free(sp);
5383
5384 return 0;
5385 }
5386
5387 /* It's an individual command. */
5388 /* Reduce the argument list so that the X10 command */
5389 /* itself becomes argv[0] */
5390
5391 retcode = direct_command(--argc, ++argv, CMD_RUN);
5392 if ( retcode != 0 ) {
5393 fprintf(stderr, "%s\n", error_message());
5394 clear_error_message();
5395 return retcode;
5396 }
5397 else if ( *error_message() ) {
5398 fprintf(stderr, "%s\n", error_message());
5399 clear_error_message();
5400 }
5401
5402 return 0;
5403 }
5404
5405
5406
5407 /*---------------------------------------------------------------------+
5408 | Display macros for final_report() |
5409 | Argument 'type' is either USED or NOTUSED. |
5410 +---------------------------------------------------------------------*/
display_macros(FILE * fd,unsigned char type,MACRO * macrop,unsigned char * elementp)5411 int display_macros ( FILE *fd, unsigned char type,
5412 MACRO *macrop, unsigned char *elementp )
5413 {
5414 int i, j, k, m, switchval, label_max, minargs;
5415 char delim, modflag;
5416 unsigned char cmdcode, subcode, subsubcode, hcode, subunit, bflag, dim;
5417 unsigned int bmap;
5418 unsigned long flags;
5419
5420 /* Find the maximum length of a macro label */
5421 label_max = 0;
5422 j = 0;
5423 while ( macrop[j].line_no > 0 ) {
5424 /* Skip null macro and those of the wrong type (used/unused) */
5425 if ( macrop[j].use != type || macrop[j].isnull ) {
5426 j++;
5427 continue;
5428 }
5429
5430 /* Skip derived macros which aren't used */
5431 if ( type == NOTUSED && (macrop[j].refer & DERIVED_MACRO) ) {
5432 j++;
5433 continue;
5434 }
5435
5436 label_max = max(label_max, (int)strlen(macrop[j].label) );
5437 j++;
5438 }
5439
5440 if ( label_max == 0 )
5441 return 0;
5442
5443 /* Now display the macros */
5444 j = 0;
5445 while ( macrop[j].line_no > 0 ) {
5446 if ( macrop[j].use != type || macrop[j].isnull ) {
5447 j++;
5448 continue;
5449 }
5450
5451 /* Skip derived macros which aren't used */
5452 if ( type == NOTUSED && (macrop[j].refer & DERIVED_MACRO) ) {
5453 j++;
5454 continue;
5455 }
5456
5457 modflag = (macrop[j].modflag == UNMODIFIED) ? ' ' : '*' ;
5458
5459 if ( type == USED && configp->display_offset == YES )
5460 (void) fprintf( fd, "%03x macro %-*s %3d %c", macrop[j].offset,
5461 label_max, macrop[j].label, macrop[j].delay, modflag);
5462 else
5463 (void) fprintf( fd, "macro %-*s %3d %c", label_max,
5464 macrop[j].label, macrop[j].delay, modflag);
5465
5466
5467 delim = ' ';
5468 i = macrop[j].element ;
5469 for ( k = 0; k < macrop[j].nelem; k++ ) {
5470 cmdcode = elementp[i] & 0x0f;
5471 hcode = code2hc((elementp[i] & 0xf0u) >> 4);
5472 bmap = elementp[i+1] << 8 | elementp[i+2];
5473 switchval = -1;
5474
5475 bflag = 0;
5476 if ( cmdcode == 4 || cmdcode == 5 ) {
5477 /* Dim or Bright command */
5478 /* Check both command code and brighten bit */
5479 bflag = (elementp[i+3] & 0x80) ? F_BRI : 0;
5480 for ( m = 0; m < nx10cmds; m++ ) {
5481 if ( x10command[m].code == cmdcode &&
5482 (x10command[m].flags & F_BRI) == bflag ) {
5483 switchval = m;
5484 break;
5485 }
5486 }
5487 }
5488 else if ( cmdcode == 10 || cmdcode == 11 ) {
5489 /* Preset command */
5490 if ( bmap == 0 ) {
5491 /* Check for preset_level only */
5492 for ( m = 0; m < nx10cmds; m++ ) {
5493 if ( x10command[m].code == cmdcode &&
5494 x10command[m].flags & F_NOA ) {
5495 switchval = m;
5496 break;
5497 }
5498 }
5499 }
5500 else {
5501 /* Check for mpreset */
5502 for ( m = 0; m < nx10cmds; m++ ) {
5503 if ( x10command[m].code == cmdcode &&
5504 x10command[m].flags & F_MAC ) {
5505 switchval = m;
5506 break;
5507 }
5508 }
5509 }
5510 }
5511 else {
5512 for ( m = 0; m < nx10cmds; m++ ) {
5513 if ( x10command[m].code == cmdcode ) {
5514 switchval = m;
5515 break;
5516 }
5517 }
5518 }
5519
5520 if ( switchval < 0 || switchval == nx10cmds ) {
5521 (void) fprintf(stderr, "display_macros(): Internal error 1\n");
5522
5523 break;
5524 }
5525
5526 switch ( cmdcode ) {
5527 case 0 : /* All Units Off */
5528 case 1 : /* All Lights On */
5529 case 6 : /* All Lights Off */
5530 case 8 : /* Hail Request */
5531 case 9 : /* Hail Ack */
5532 case 12 : /* Data Transfer */
5533 case 13 : /* Status On */
5534 case 14 : /* Status Off */
5535 /* Bitmap is normally zero for these commands */
5536 if ( bmap ) {
5537 /* Display the '+' prefixed HC and unit */
5538 fprintf( fd, "%c %s +%c%s",
5539 delim, x10command[switchval].label, hcode,
5540 bmap2units(bmap) );
5541 }
5542 else {
5543 /* Just the HC */
5544 fprintf( fd, "%c %s %c",
5545 delim, x10command[switchval].label, hcode);
5546 }
5547
5548 i += x10command[switchval].length;
5549 break;
5550
5551 case 2 : /* On */
5552 case 3 : /* Off */
5553 case 15 : /* Status Request */
5554 if ( bmap ) {
5555 fprintf( fd, "%c %s %c%s",
5556 delim, x10command[switchval].label, hcode,
5557 bmap2units(bmap) );
5558 }
5559 else {
5560 fprintf( fd, "%c %s -%c",
5561 delim, x10command[switchval].label, hcode);
5562 }
5563
5564 i += x10command[switchval].length;
5565 break;
5566
5567 case 4 : /* Dim or Dimb */
5568 case 5 : /* Bright or Brightb */
5569 if ( bmap ) {
5570 fprintf( fd, "%c %s %c%s %d",
5571 delim, x10command[switchval].label, hcode,
5572 bmap2units(bmap), elementp[i+3] & 0x1f );
5573 }
5574 else {
5575 fprintf( fd, "%c %s -%c %d",
5576 delim, x10command[switchval].label, hcode,
5577 elementp[i+3] & 0x1f );
5578 }
5579
5580 i += x10command[switchval].length;
5581
5582 break;
5583
5584 case 10 : /* Preset Dim (1) - mpreset or level only */
5585 case 11 : /* Preset Dim (2) - mpreset or level only */
5586 dim = rev_low_nybble((elementp[i] & 0xf0u) >> 4) + 1;
5587 dim += (cmdcode == 11) ? 16 : 0;
5588 if ( bmap == 0 ) {
5589 fprintf( fd, "%c %s %d",
5590 delim, x10command[switchval].label, dim);
5591 }
5592 else {
5593 fprintf( fd, "%c %s %c%s %d",
5594 delim, x10command[switchval].label,
5595 hcode, bmap2units(bmap), dim );
5596 }
5597
5598 i += x10command[switchval].length;
5599 break;
5600
5601 case 7 : /* Extended Code */
5602 subcode = elementp[i+5];
5603 if ( subcode == 0x37 || subcode == 0x36 || subcode == 0x35 || subcode == 0x3c ) {
5604 subsubcode = (elementp[i+4] & 0x30) >> 4;
5605 for ( m = 0; m < nx10cmds; m++ ) {
5606 if ( x10command[m].code == cmdcode &&
5607 x10command[m].subcode == subcode &&
5608 x10command[m].subsubcode == subsubcode )
5609 break;
5610 }
5611 }
5612 else {
5613 for ( m = 0; m < nx10cmds; m++ ) {
5614 if ( x10command[m].code == cmdcode &&
5615 x10command[m].subcode == subcode )
5616 break;
5617 }
5618 }
5619
5620 /* If not standard command, locate entry for arbitrary code */
5621 if ( m >= nx10cmds ) {
5622 for ( m = 0; m < nx10cmds; m++ ) {
5623 if ( !(strcmp(x10command[m].label, XARBNAME)) )
5624 break;
5625 }
5626 }
5627
5628 flags = x10command[m].flags;
5629 minargs = x10command[m].minargs;
5630 subsubcode = x10command[m].subsubcode;
5631
5632 if ( flags & F_ARB )
5633 fprintf( fd, "%c %s %02X ", delim, x10command[m].label, subcode);
5634 else
5635 fprintf( fd, "%c %s ", delim, x10command[m].label);
5636
5637 /* Display the HC, prefixed with a '+' if bitmap is not 0 */
5638 if ( bmap )
5639 fprintf(fd, "+%c", hcode);
5640 else
5641 fprintf( fd, "%c", hcode);
5642
5643 /* Display the unit, if any */
5644 if ( !(flags & F_ALL) || flags & F_FAD || flags & F_ARB ) {
5645 fprintf( fd, "%d", code2unit(elementp[i+3] & 0x0f) );
5646 subunit = (elementp[i+3] & 0xf0u) >> 4;
5647 if ( subunit > 0 )
5648 fprintf( fd, "(%02x)", subunit );
5649 }
5650 else if ( flags & F_ALL && bmap ) {
5651 fprintf( fd, "%s", bmap2units(bmap) );
5652 }
5653
5654 /* Display the data byte info if required */
5655 if ( subcode == 0x30u || subcode == 0x36u || subcode == 0x3cu ) {
5656 if ( elementp[i+4] & 0x20u )
5657 fprintf(fd, " %d.%-2d", elementp[i+4] >> 6, (elementp[i+4] & 0x0fu) + 1);
5658 else
5659 fprintf(fd, " %d", elementp[i+4] >> 6);
5660 }
5661 else if ( subcode == 0x35u ) {
5662 fprintf( fd, " %s", linmap2list(elementp[i+4] & 0x0f));
5663 }
5664 else if ( subcode == 0x37u && (elementp[i+4] & 0x20u) ) {
5665 if ( elementp[i+4] & 0x10u )
5666 fprintf(fd, " %d.%-2d", elementp[i+4] >> 6, (elementp[i+4] & 0x0fu) + 1);
5667 else
5668 fprintf(fd, " %d", elementp[i+4] >> 6);
5669 }
5670 else if ( flags & F_GRP ) {
5671 if ( minargs > 1 )
5672 fprintf( fd, " %d", elementp[i+4] >> 6);
5673 if ( minargs > 2 )
5674 fprintf( fd, " %02d", elementp[i+4] & 0x3f);
5675 }
5676 else if ( flags & F_DED )
5677 fprintf( fd, " %02d", elementp[i+4]);
5678 else if ( flags & F_ARB )
5679 fprintf( fd, " %02X", elementp[i+4]);
5680
5681 i += x10command[m].length;
5682 break;
5683
5684 default :
5685 fprintf(stderr, "display_macros(): Internal error 2.\n");
5686 break;
5687 }
5688
5689 delim = ';';
5690 }
5691 (void) fprintf( fd, "\n" );
5692 j++ ;
5693 }
5694
5695 return 1;
5696 }
5697
5698 /*---------------------------------------------------------------------+
5699 | Display error message and return 1 if configured for a CM10A |
5700 | interface. Otherwise just return 0. |
5701 +---------------------------------------------------------------------*/
invalidate_for_cm10a(void)5702 int invalidate_for_cm10a ( void )
5703 {
5704 if ( configp->device_type & DEV_CM10A ) {
5705 fprintf(stderr, "Command is invalid for CM10A interface.\n");
5706 return 1;
5707 }
5708 return 0;
5709 }
5710
5711 /*---------------------------------------------------------------------+
5712 | Instruct daemons to re-read the config file. |
5713 +---------------------------------------------------------------------*/
c_restart_old(int argc,char * argv[])5714 int c_restart_old ( int argc, char *argv[] )
5715 {
5716 send_x10state_command(ST_RESTART, 0);
5717 return 0;
5718 }
5719
5720
5721 /*---------------------------------------------------------------------+
5722 | Instruct daemons to re-read the config file. |
5723 +---------------------------------------------------------------------*/
c_restart(int argc,char * argv[])5724 int c_restart ( int argc, char *argv[] )
5725 {
5726 FILE *fp;
5727 char message[127];
5728 char restart_r[PATH_LEN + 1];
5729 char restart_a[PATH_LEN + 1];
5730 int rflag = 1, aflag = 0;
5731 int count = 100;
5732 struct stat rstat, astat;
5733
5734 char *ignoretp;
5735
5736 strncpy2(restart_r, pathspec(RESTART_RELAY_FILE), PATH_LEN);
5737
5738 if ( config.ttyaux[0] != '\0' /* check_for_aux() == 0 */ ) {
5739 strncpy2(restart_a, pathspec(RESTART_AUX_FILE), PATH_LEN);
5740 aflag = 1;
5741 }
5742
5743 if ( !(fp = fopen(restart_r, "w")) ) {
5744 fprintf(stderr, "Unable to restart heyu_relay.\n");
5745 return 1;
5746 }
5747 fclose(fp);
5748
5749 if ( aflag ) {
5750 if ( !(fp = fopen(restart_a, "w")) ) {
5751 fprintf(stderr, "Unable to restart heyu_aux.\n");
5752 unlink(restart_r);
5753 return 1;
5754 }
5755 fclose(fp);
5756 }
5757
5758 while ( count-- && (rflag || aflag) ) {
5759 millisleep(100);
5760 rflag = (rflag && !stat(restart_r, &rstat)) ? 1 : 0;
5761 aflag = (aflag && !stat(restart_a, &astat)) ? 1 : 0;
5762 }
5763
5764 if ( count == 0 ) {
5765 if ( rflag ) {
5766 fp = fopen(restart_r, "r");
5767 ignoretp = fgets(message, sizeof(message), fp);
5768 fprintf(stderr, "%s", message);
5769 fclose(fp);
5770 fprintf(stderr, "Restart heyu_relay failed.\n");
5771 unlink(restart_r);
5772 }
5773 if ( aflag ) {
5774 fp = fopen(restart_a, "r");
5775 ignoretp = fgets(message, sizeof(message), fp);
5776 fprintf(stderr, "%s", message);
5777 fclose(fp);
5778 fprintf(stderr, "Restart heyu_aux failed.\n");
5779 unlink(restart_a);
5780 }
5781 return 1;
5782 }
5783
5784 return 0;
5785 }
5786
5787 /*---------------------------------------------------------------------+
5788 | Global script enable/disable |
5789 +---------------------------------------------------------------------*/
c_script_ctrl(int argc,char * argv[])5790 int c_script_ctrl ( int argc, char *argv[] )
5791 {
5792 extern int check_for_engine ( void );
5793 int funct;
5794
5795 if ( argc != 3 ||
5796 (funct = (strcmp(strlower(argv[2]), "disable") == 0) ? NO :
5797 (strcmp(argv[2], "enable") == 0) ? YES : -1) < 0 ) {
5798 fprintf(stderr, "Usage: %s %s disable|enable\n", argv[0], argv[1]);
5799 return 1;
5800 }
5801
5802 if ( check_for_engine() != 0 ) {
5803 fprintf(stderr, "Heyu state engine is not running.\n");
5804 return 1;
5805 }
5806
5807 send_x10state_command(ST_LAUNCH, (unsigned char)funct);
5808 return 0;
5809 }
5810
5811
5812 /*---------------------------------------------------------------------+
5813 | Test powerfail script |
5814 +---------------------------------------------------------------------*/
c_powerfailtest(int argc,char * argv[])5815 int c_powerfailtest ( int argc, char *argv[] )
5816 {
5817 extern int check_for_engine ( void );
5818 unsigned char bootflag = R_NOTATSTART;
5819
5820 if ( argc < 3 ) {
5821 fprintf(stderr, "Parameter 'boot' or 'notboot' required.\n");
5822 return 1;
5823 }
5824
5825 strlower(argv[2]);
5826 if ( strcmp(argv[2], "boot") == 0 )
5827 bootflag = R_ATSTART;
5828 else if ( strcmp(argv[2], "notboot") == 0 )
5829 bootflag = R_NOTATSTART;
5830 else {
5831 fprintf(stderr, "Parameter must be 'boot' or 'notboot'\n");
5832 return 1;
5833 }
5834
5835 if ( check_for_engine() != 0 ) {
5836 fprintf(stderr, "State engine is not running.\n");
5837 return 1;
5838 }
5839
5840 send_x10state_command(ST_PFAIL, bootflag);
5841
5842 return 0;
5843 }
5844
5845 /*---------------------------------------------------------------------+
5846 | Get security flags for home, away, min, max. |
5847 +---------------------------------------------------------------------*/
get_armed_parms(int argc,char * argv[],unsigned char * sflags,unsigned char mode)5848 int get_armed_parms ( int argc, char *argv[], unsigned char *sflags, unsigned char mode )
5849 {
5850 int j;
5851 unsigned long lflags;
5852 char msg[80];
5853
5854 #if 0
5855 if ( check_for_engine() != 0 ) {
5856 store_error_message("Engine is not running.");
5857 return 1;
5858 }
5859 #endif
5860
5861 lflags = GLOBSEC_ARMED;
5862 *sflags = (lflags >> GLOBSEC_SHIFT);
5863
5864 for ( j = 1; j < argc; j++ ) {
5865 if ( strchr(argv[j], '$') && mode == CMD_VERIFY )
5866 continue;
5867 strlower(argv[j]);
5868 if ( strcmp(argv[j], "home") == 0 )
5869 lflags |= GLOBSEC_HOME;
5870 else if ( strcmp(argv[j], "away") == 0 )
5871 lflags &= ~GLOBSEC_HOME;
5872 else if ( strcmp(argv[j], "min") == 0 ) {
5873 lflags |= GLOBSEC_ARMED;
5874 lflags &= ~GLOBSEC_PENDING;
5875 }
5876 else if ( strcmp(argv[j], "max") == 0 ) {
5877 lflags &= ~GLOBSEC_ARMED;
5878 lflags |= GLOBSEC_PENDING;
5879 }
5880 else {
5881 sprintf(msg, "Invalid arm parameter '%s'\n", argv[j]);
5882 store_error_message(msg);
5883 return 1;
5884 }
5885 }
5886
5887 *sflags = (lflags >> GLOBSEC_SHIFT);
5888
5889 return 0;
5890 }
5891
5892 /*---------------------------------------------------------------------+
5893 | Set the system flags for Armed or Disarmed - LEGACY |
5894 +---------------------------------------------------------------------*/
c_arm(int argc,char * argv[])5895 int c_arm ( int argc, char *argv[] )
5896 {
5897 int j;
5898 unsigned long sflags;
5899
5900 if ( argc < 2 ) {
5901 fprintf(stderr, "Too few parameters.\n");
5902 return 1;
5903 }
5904
5905 if ( check_for_engine() != 0 ) {
5906 fprintf(stderr, "Engine is not running.\n");
5907 return 1;
5908 }
5909
5910
5911 if ( strcmp(argv[1], "_disarm") == 0 ) {
5912 send_x10state_command(ST_SECFLAGS, 0);
5913 return 0;
5914 }
5915 else if ( strcmp(argv[1], "_arm") != 0 ) {
5916 fprintf(stderr, "Invalid command '%s'\n", argv[1]);
5917 return 1;
5918 }
5919
5920 sflags = GLOBSEC_ARMED;
5921
5922 for ( j = 2; j < argc; j++ ) {
5923 strlower(argv[j]);
5924 if ( strcmp(argv[j], "home") == 0 )
5925 sflags |= GLOBSEC_HOME;
5926 else if ( strcmp(argv[j], "away") == 0 )
5927 continue;
5928 else if ( strcmp(argv[j], "min") == 0 ) {
5929 sflags |= GLOBSEC_ARMED;
5930 sflags &= ~GLOBSEC_PENDING;
5931 }
5932 else if ( strcmp(argv[j], "max") == 0 ) {
5933 sflags &= ~GLOBSEC_ARMED;
5934 sflags |= GLOBSEC_PENDING;
5935 }
5936 else {
5937 fprintf(stderr, "Invalid arm parameter '%s'\n", argv[j]);
5938 return 1;
5939 }
5940 }
5941
5942 sflags = sflags >> GLOBSEC_SHIFT;
5943
5944 send_x10state_command(ST_SECFLAGS, (unsigned char)sflags);
5945
5946 return 0;
5947 }
5948
5949 /*---------------------------------------------------------------------+
5950 | Read ext3 code group as either absolute, e.g., "0", or relative, |
5951 | e.g. "0.1"). Subgroups are entered and displayed as numbered 1-16 |
5952 | but converted to 0x80-0x8f to distinguish internal subgroup 0 from |
5953 | absolute group 0 |
5954 +---------------------------------------------------------------------*/
parse_ext3_group(char * token,unsigned char * group,unsigned char * subgroup)5955 int parse_ext3_group ( char *token, unsigned char *group, unsigned char *subgroup )
5956 {
5957 char errmsg[80];
5958 char *sp, *tp;
5959 int igroup;
5960
5961 igroup = (int)strtol(token, &sp, 10);
5962
5963 if ( strchr(" ./t/n", *sp) == NULL || igroup < 0 || igroup > 3 ) {
5964 sprintf(errmsg, "Invalid group '%s'", token);
5965 store_error_message(errmsg);
5966 return 1;
5967 }
5968 else if ( *sp == '.' ) {
5969 *group = (unsigned char)igroup;
5970 tp = sp + 1;
5971 igroup = (int)strtol(tp, &sp, 10);
5972 if ( strchr(" /t/n", *sp) == NULL || igroup < 0 || igroup > 16 ) {
5973 sprintf(errmsg, "Invalid subgroup '%s'", tp);
5974 store_error_message(errmsg);
5975 return 1;
5976 }
5977 else if ( igroup == 0 ) {
5978 *subgroup = 0;
5979 }
5980 else {
5981 *subgroup = (unsigned char)(igroup - 1) | 0x80;
5982 }
5983 }
5984 else {
5985 *group = (unsigned char)igroup;
5986 *subgroup = 0;
5987 }
5988
5989 return 0;
5990 }
5991
5992
restore_groups(unsigned char hcode)5993 int restore_groups ( unsigned char hcode )
5994 {
5995
5996 int group, lastgroup;
5997 int tokc;
5998 char **tokv = NULL;
5999 unsigned char ucode, xdata, gflag, grflag;
6000 int unit, xgrc;
6001 unsigned int bitmap;
6002 char hc;
6003 char cmdline[80];
6004 unsigned char grpmember[16], grpaddr[16], grplevel[16][4], xconfigmode;
6005
6006 extern unsigned int modmask[][16];
6007 extern int fetch_x10state ( void );
6008 extern int copy_xmodule_info ( unsigned char, unsigned char [], unsigned char [],
6009 unsigned char [][4], unsigned char * );
6010
6011 if ( fetch_x10state() != 0 )
6012 return 1;
6013
6014 hc = code2hc(hcode);
6015
6016 /* Copy xmodule info from x10state table to temp arrays */
6017 copy_xmodule_info(hcode, grpmember, grpaddr, grplevel, &xconfigmode);
6018
6019 /* Determine if groups and group reference is used for any unit */
6020 grflag = gflag = 0;
6021 for ( ucode = 0; ucode < 16; ucode++ ) {
6022 if ( grpmember[ucode] & 0x0fu ) {
6023 gflag = 1;
6024 if ( grpaddr[ucode] & 0x80u ) {
6025 grflag = 1;
6026 break;
6027 }
6028 }
6029 }
6030
6031 if ( gflag == 0 ) {
6032 /* No groups - restore xconfig mode if required and return */
6033 if ( modmask[Ext3StatusMask][hcode] & 0xffff ) {
6034 sprintf(cmdline, "%s 3b %c1 %02x", XARBNAME, hc, xconfigmode);
6035 tokenize(cmdline, " ", &tokc, &tokv);
6036 direct_command(tokc, tokv, CMD_RUN);
6037 free(tokv);
6038 }
6039 return 0;
6040 }
6041
6042 /* Remove all units from all groups via xfunc 35 */
6043 sprintf(cmdline, "%s 35 %c1 ff", XARBNAME, hc);
6044 tokenize(cmdline, " ", &tokc, &tokv);
6045 direct_command(tokc, tokv, CMD_RUN);
6046 free(tokv);
6047
6048 if ( grflag && xconfigmode > 0 ) {
6049 /* Disable extended status ack */
6050 sprintf(cmdline, "%s 3b %c1 00", XARBNAME, hc);
6051 tokenize(cmdline, " ", &tokc, &tokv);
6052 direct_command(tokc, tokv, CMD_RUN);
6053 free(tokv);
6054 }
6055
6056 for ( ucode = 0; ucode < 16; ucode++ ) {
6057 if ( (grpmember[ucode] & 0x0fu) == 0 )
6058 continue;
6059
6060 unit = code2unit(ucode);
6061 bitmap = 1 << ucode;
6062
6063 xgrc = (modmask[Ext3GrpRelT1Mask][hcode] & bitmap) ? 1 :
6064 (modmask[Ext3GrpRelT2Mask][hcode] & bitmap) ? 2 :
6065 (modmask[Ext3GrpRelT3Mask][hcode] & bitmap) ? 3 :
6066 (modmask[Ext3GrpRelT4Mask][hcode] & bitmap) ? 4 : 0;
6067
6068 lastgroup = 4;
6069 for ( group = 0; group < 4; group++ ) {
6070 if ( (grpmember[ucode] & (1 << group)) == 0 )
6071 continue;
6072
6073 if ( grpaddr[ucode] & 0x80u ) {
6074 /* Relative to group reference */
6075 /* Preset module to level with xfunc 31 */
6076 xdata = grplevel[ucode][group] & 0x3fu;
6077 sprintf(cmdline, "%s 31 %c%d %02x", XARBNAME, hc, unit, xdata);
6078 tokenize(cmdline, " ", &tokc, &tokv);
6079 if ( direct_command(tokc, tokv, CMD_RUN) != 0 )
6080 fprintf(stderr, "%s\n", error_message());
6081 free(tokv);
6082 /* Add to group with reference with xfunc 30 */
6083 xdata = (group << 6) | 0x20 | (grpaddr[ucode] & 0x0fu);
6084 sprintf(cmdline, "%s 30 %c%d %02x", XARBNAME, hc, unit, xdata);
6085 tokenize(cmdline, " ", &tokc, &tokv);
6086 if ( direct_command(tokc, tokv, CMD_RUN) != 0 )
6087 fprintf(stderr, "%s\n", error_message());
6088 free(tokv);
6089 }
6090 else {
6091 /* Set Absolute group with xfunc 32 */
6092 xdata = (group << 6) | (grplevel[ucode][group] & 0x3fu);
6093 sprintf(cmdline, "%s 32 %c%d %02x", XARBNAME, hc, unit, xdata);
6094 tokenize(cmdline, " ", &tokc, &tokv);
6095 if ( direct_command(tokc, tokv, CMD_RUN) != 0 )
6096 fprintf(stderr, "%s\n", error_message());
6097 free(tokv);
6098 }
6099 }
6100 }
6101
6102 /* Restore xstatus if required */
6103 if ( modmask[Ext3StatusMask][hcode] & 0xffff ) {
6104 sprintf(cmdline, "%s 3b %c1 %02x", XARBNAME, hc, xconfigmode);
6105 tokenize(cmdline, " ", &tokc, &tokv);
6106 direct_command(tokc, tokv, CMD_RUN);
6107 free(tokv);
6108 }
6109
6110 return 0;
6111 }
6112
c_restore_groups(int argc,char * argv[])6113 int c_restore_groups ( int argc, char *argv[] )
6114 {
6115 char hc;
6116
6117 for ( hc = 'A'; hc <= 'P'; hc++ ) {
6118 if ( restore_groups(hc2code(hc)) != 0 ) {
6119 fprintf(stderr, "restore_groups failed for housecode %c\n", hc);
6120 return 1;
6121 }
6122 }
6123
6124 return 0;
6125 }
6126
6127
6128 #if 0
6129 void display_help ( char *helplist[][3] )
6130 {
6131 int j;
6132 int szlbl, szarg, sztot;
6133 char buffer[128];
6134
6135 /* Get max string lengths for formatting output */
6136 szlbl = szarg = sztot = 0;
6137 j = 0;
6138 while ( helplist[j][0] != NULL ) {
6139 szlbl = max(szlbl, (int)strlen(helplist[j][0]));
6140 szarg = max(szarg, (int)strlen(helplist[j][1]));
6141 sztot = max(sztot, ((int)strlen(helplist[j][0]) +
6142 (int)strlen(helplist[j][1])));
6143 j++;
6144 }
6145
6146 j = 0;
6147 while ( helplist[j][0] != NULL ) {
6148 #ifdef COMPACT_FORMAT
6149 /* Compact format */
6150 sprintf(buffer, "%s %s", helplist[j][0], helplist[j][1]);
6151 printf(" heyu %-*s %s\n", sztot + 2, buffer, helplist[j][2]);
6152 #else
6153 printf(" heyu %-*s %-*s %s\n", szlbl, helplist[j][0],
6154 szarg, helplist[j][1], helplist[j][2]);
6155 #endif /* End ifdef */
6156 j++;
6157 }
6158 return;
6159 }
6160 #endif
6161
6162
6163 #if 0
6164 void display_dir_help ( unsigned long flags, unsigned long notflags )
6165 {
6166 int j;
6167 int szlbl, szarg, sztot;
6168 char buffer[128];
6169 struct cmd_list *one = NULL, *two;
6170
6171 /* Direct commands */
6172 /* Get max string lengths for formatting output */
6173 szlbl = szarg = sztot = 0;
6174 for ( j = 0; j < nx10cmds; j++ ) {
6175 if ( x10command[j].flags & F_HID ||
6176 x10command[j].flags & notflags ||
6177 !(x10command[j].flags & flags) ) {
6178 continue;
6179 }
6180 sztot = max(sztot, ((int)strlen(x10command[j].label) +
6181 (int)strlen(helparg[x10command[j].argtype])) );
6182 }
6183 for ( j = 0; j < nx10cmds; j++ ) {
6184 two = &x10command[j];
6185 if ( cmp_commands(one, two) ) {
6186 sprintf(buffer, "%s %s", x10command[j].label, helparg[x10command[j].argtype]);
6187 printf(" heyu %-*s %s%s\n", sztot + 2, buffer,
6188 helpdirect[x10command[j].help],
6189 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
6190 one = two;
6191 }
6192 }
6193 return;
6194 }
6195 #endif
6196
6197 #if 0
6198 int c_logtail_new ( int argc, char *argv[] )
6199 {
6200 int lines;
6201 char cmdline[PATH_LEN + PATH_LEN + 20];
6202 char *sp;
6203
6204 get_configuration(CONFIG_INIT);
6205
6206 if ( strcmp(configp->logfile, "/dev/null") == 0 ) {
6207 fprintf(stderr, "LOG_DIR not defined in config file.\n");
6208 return 1;
6209 }
6210
6211 if ( *configp->tailpath == '\0' ) {
6212 strcpy(cmdline, "tail");
6213 }
6214 else {
6215 strncpy2(cmdline, configp->tailpath, PATH_LEN);
6216 }
6217
6218 if ( argc == 3 ) {
6219 if ( strcmp(argv[2], "-f") == 0 ) {
6220 sprintf(cmdline + strlen(cmdline), " -f %s", configp->logfile);
6221 system(cmdline);
6222 for (;;) {
6223 sleep(1);
6224 }
6225 }
6226
6227 lines = (int)strtol(argv[2], &sp, 10);
6228 if ( strchr(" \t\r\n", *sp) == NULL ) {
6229 fprintf(stderr, "Invalid 'tail' lines '%s'\n", argv[2]);
6230 return 1;
6231 }
6232 lines = (lines < 0) ? -lines : lines;
6233 sprintf(cmdline + strlen(cmdline), " -%d", lines);
6234 }
6235
6236 sprintf(cmdline + strlen(cmdline), " %s", configp->logfile);
6237
6238 return system(cmdline);
6239
6240 }
6241 #endif
6242
c_logtail(int argc,char * argv[])6243 int c_logtail ( int argc, char *argv[] )
6244 {
6245 int lines;
6246 char cmdline[PATH_LEN + PATH_LEN + 20];
6247 char *sp;
6248
6249 get_configuration(CONFIG_INIT);
6250
6251 if ( strcmp(configp->logfile, "/dev/null") == 0 ) {
6252 fprintf(stderr, "LOG_DIR not defined in config file.\n");
6253 return 1;
6254 }
6255
6256 if ( *configp->tailpath == '\0' ) {
6257 strcpy(cmdline, "tail");
6258 }
6259 else {
6260 strcpy(cmdline, configp->tailpath);
6261 }
6262
6263 if ( argc == 3 ) {
6264 lines = (int)strtol(argv[2], &sp, 10);
6265 if ( strchr(" \t\r\n", *sp) == NULL ) {
6266 fprintf(stderr, "Invalid 'tail' lines '%s'\n", argv[2]);
6267 return 1;
6268 }
6269 lines = (lines < 0) ? -lines : lines;
6270 sprintf(cmdline + strlen(cmdline), " -%d", lines);
6271 }
6272
6273 sprintf(cmdline + strlen(cmdline), " %s", configp->logfile);
6274
6275 return system(cmdline);
6276
6277 }
6278
6279
6280