/*----------------------------------------------------------------------------+ | | | State and Script functions for HEYU | | Copyright 2004-2007 Charles W. Sullivan | | All Rights Reserved | | | | | | This software is licensed free of charge for non-commercial distribution | | and for personal and internal business use only. Inclusion of this | | software or any part thereof in a commercial product is prohibited | | without the prior written permission of the author. You may copy, use, | | and distribute this software subject to the following restrictions: | | | | 1) You may not charge money for it. | | 2) You may not remove or alter this license, copyright notice, or the | | included disclaimers. | | 3) You may not claim you wrote it. | | 4) If you make improvements (or other changes), you are requested | | to send them to the Heyu maintainer so there's a focal point for | | distributing improved versions. | | | | As used herein, HEYU is a trademark of Daniel B. Suthers. | | X10, CM11A, and ActiveHome are trademarks of X-10 (USA) Inc. | | The author is not affiliated with either entity. | | | | Charles W. Sullivan | | Co-author and Maintainer | | Greensboro, North Carolina | | Email ID: cwsulliv01 | | Email domain: -at- heyu -dot- org | | | | Disclaimers: | | THERE IS NO ASSURANCE THAT THIS SOFTWARE IS FREE OF DEFECTS AND IT MUST | | NOT BE USED IN ANY SITUATION WHERE THERE IS ANY CHANCE THAT ITS | | PERFORMANCE OR FAILURE TO PERFORM AS EXPECTED COULD RESULT IN LOSS OF | | LIFE, INJURY TO PERSONS OR PROPERTY, FINANCIAL LOSS, OR LEGAL LIABILITY. | | | | TO THE EXTENT ALLOWED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED "AS IS",| | WITH NO EXPRESS OR IMPLIED WARRANTY, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.| | | | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL THE AUTHOR BE LIABLE | | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL | | DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SOFTWARE EVEN IF | | THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | | | +----------------------------------------------------------------------------*/ #include #include #include #include #if defined(SYSV) || defined(FREEBSD) || defined(OPENBSD) #include #else #include #endif #include #include #include #include #ifdef POSIX #define EXEC_POSIX #endif #ifdef EXEC_POSIX #include #endif #include #include #include "x10.h" #include "process.h" #include "sun.h" #ifdef pid_t #define PID_T pid_t #else #define PID_T long #endif extern CONFIG config; extern CONFIG *configp; extern char *funclabel[]; extern int tty, sptty, verbose; extern int i_am_monitor, i_am_state, i_am_relay; extern int heyu_parent; extern FILE *fdsout, *fdserr; extern char *heyu_tzname[2]; extern char *datstrf( void ); extern char heyu_script[]; extern char statefile[]; extern char enginelockfile[]; extern char heyu_config[PATH_LEN + 1]; #define PROMSIZE 1024 static unsigned char image[PROMSIZE + 10]; extern unsigned int modmask[NumModMasks][16]; extern unsigned char maxdimlevel[16][16]; extern unsigned char ondimlevel[16][16]; extern char *wday_name[7]; extern char *month_name[12]; #define NHCODES 16 #define SpecPLC 0x01 /* Specific PLC */ #define GenPLC 0x02 /* Generic PLC */ #define SpecSec 0x04 /* Specific Security */ #define AnyFlag 0x80 /* All of any above group */ #define AnyPLC (AnyFlag | SpecPLC) #define AnySec (AnyFlag | SpecSec) #define AnyFunc (AnyFlag | SpecPLC | SpecSec) /* General commands which can launch a script */ /* (Maximum of 32 distinct xxxTrig entries in */ /* this table, to fit into an unsigned long */ /* integer bitmap.) */ static struct trig_type_st { char *label; unsigned char flags; int signal; } trig_type[] = { {"anyplc", AnyPLC, 0}, {"anysec", AnySec, 0}, {"anyfunc", AnyFunc, 0}, {"on", SpecPLC, OnTrig}, {"off", SpecPLC, OffTrig}, {"dim", SpecPLC, DimTrig}, {"bright", SpecPLC, BriTrig}, {"lightson", SpecPLC, LightsOnTrig}, {"lightsoff", SpecPLC, LightsOffTrig}, {"allon", SpecPLC, AllOnTrig}, {"alloff", SpecPLC, AllOffTrig}, {"status_on", SpecPLC, StatusOnTrig}, {"status_off", SpecPLC, StatusOffTrig}, {"status", SpecPLC, StatusReqTrig}, {"preset", SpecPLC, PresetTrig}, {"extended", SpecPLC, ExtendedTrig}, {"xpup", SpecPLC, ExtPowerUpTrig}, {"xpowerup", SpecPLC, ExtPowerUpTrig}, {"hail", SpecPLC, HailReqTrig}, {"hail_ack", SpecPLC, HailAckTrig}, {"data_xfer", SpecPLC, DataXferTrig}, {"vdata", SpecPLC, VdataTrig}, {"gon", GenPLC, OnTrig}, {"goff", GenPLC, OffTrig}, {"gdim", GenPLC, DimTrig}, {"panic", SpecSec, PanicTrig}, {"arm", SpecSec, ArmTrig}, {"disarm", SpecSec, DisarmTrig}, {"alert", SpecSec, AlertTrig}, {"clear", SpecSec, ClearTrig}, {"slightson", SpecSec, SecLightsOnTrig}, {"slightsoff", SpecSec, SecLightsOffTrig}, }; #define NTRIGTYPES (sizeof(trig_type)/sizeof(struct trig_type_st)) /* Functions included in the 'generic' group */ #define GENFUNCMAP ((1 << OnTrig) | (1 << OffTrig) | (1 << DimTrig)) void arm_delay_complete ( int ); void timer_timeout ( int ); enum { ArmDelayTimer, Timeout01, Timeout02, Timeout03, Timeout04, Timeout05, Timeout06, Timeout07, Timeout08, Timeout09, Timeout10, Timeout11, Timeout12, Timeout13, Timeout14, Timeout15, Timeout16, NumCountdownTimers }; typedef struct { long count; void (*elapsed_func)(int); } COUNTDOWN; COUNTDOWN countdown_timer[NumCountdownTimers] = { {0, arm_delay_complete}, {0, timer_timeout}, /* User timer 1 */ {0, timer_timeout}, /* User timer 2 */ {0, timer_timeout}, /* User timer 3 */ {0, timer_timeout}, /* User timer 4 */ {0, timer_timeout}, /* User timer 5 */ {0, timer_timeout}, /* User timer 6 */ {0, timer_timeout}, /* User timer 7 */ {0, timer_timeout}, /* User timer 8 */ {0, timer_timeout}, /* User timer 9 */ {0, timer_timeout}, /* User timer 10 */ {0, timer_timeout}, /* User timer 11 */ {0, timer_timeout}, /* User timer 12 */ {0, timer_timeout}, /* User timer 13 */ {0, timer_timeout}, /* User timer 14 */ {0, timer_timeout}, /* User timer 15 */ {0, timer_timeout}, /* User timer 16 */ }; /* list of security sensor alias indexes */ int sensor_alias_indexes[MAX_SEC_SENSORS]; int num_sec_sensors = 0; enum {OnState, DimState, ChgState, LightsOnState, NumStates}; typedef struct { unsigned char reset; unsigned char lastunit; unsigned char lastcmd; unsigned int addressed; /* X10 bitmap */ unsigned int exclusive; /* Exclusive bitmap */ unsigned int sticky; /* Stickey address bitmap */ unsigned int vaddress; /* Virtual address bitmap */ unsigned char xconfigmode; /* Mode for xconfig */ unsigned int state[NumStates]; /* 0 = OnState 1 = DimState 2 = ChgState 3 = LightsOnState */ unsigned int launched; /* Launched script bitmap */ unsigned int statusflags; /* Status pending flags */ unsigned int vflags[16]; /* Virtual data flags */ unsigned char dimlevel[16]; /* Current dim level */ unsigned char memlevel[16]; /* "memory" level - preset modules */ unsigned char vident[16]; /* ident - virtual modules */ time_t timestamp[16]; /* time of last update */ unsigned char grpmember[16]; /* bits 0-3 are bitmapped ext3 group memberships */ unsigned char grpaddr[16]; /* abs or relative group address */ unsigned char grplevel[16][4]; /* ext3 levels for groups 0-3 */ } x10hcode_t; static struct { time_t filestamp; /* Time of last file write */ unsigned long longvdata; unsigned char lastvtype; unsigned char lasthc; unsigned int lastaddr; unsigned long timer_count[NumCountdownTimers]; unsigned long flags; /* State and Security flags */ unsigned char dawndusk_enable; /* Set when config has Lat and Long.*/ int isdark_offset; time_t utc0_dawn; time_t utc0_dusk; time_t utc0_tomorrow; x10hcode_t x10hcode[NHCODES]; } x10global; x10hcode_t *x10state = x10global.x10hcode; /*----------------------------------------------------------------------------+ | Converts programmed dim level (1-22) to dims (0-210). | | This is just an approximation as the number of dims actually transmitted | | by the interface varies, apparently at random, but actually depending on | | whether beginning on rising or falling zero crossing of AC waveform. | +----------------------------------------------------------------------------*/ unsigned char level2dims ( unsigned char level, char **prefix, char **suffix ) { unsigned int dims, base; /* The value of 'base' appears to vary randomly, being either 2 or 3 */ /* with approximately equal likelyhood. The value 4 has occasionally */ /* been observed. We use 2 here. */ base = 2; dims = (level > 0) ? base + 11 * (level - 1) : base + 11 ; if ( dims > 210 ) { *prefix = ""; *suffix = "+"; } else { *prefix = "~"; *suffix = ""; } dims = min(210, dims); return (unsigned char)dims; } /*----------------------------------------------------------------------------+ | Converts dims level (0-210) to (int) percent of full ON voltage. | | Linearized from measured data on an X10 LM465 (1-way) Lamp Module | | controlling a 100 Watt lamp. | +----------------------------------------------------------------------------*/ int dims2pct ( unsigned char level ) { long int pct; pct = (level > 32u) ? 90 * (level - 33u) / 177 + 10 : (level > 1u) ? 8 * (level - 2u) / 31 + 2 : 0 ; return (int)pct; } /*----------------------------------------------------------------------------+ | Converts type 0 (shutter) level (0-25) to (int) percent of full Open. | | This is just a linear translation. | +----------------------------------------------------------------------------*/ int ext0level2pct ( unsigned int level ) { return 100 * (int)level /25; } #define MEASURED_DATA #ifdef MEASURED_DATA /*----------------------------------------------------------------------------+ | Converts old-style preset level (zero-based, 0-31) to (int) percent of | | full ON voltage. | | This is based on measurement of output voltage for a LampLinc 2000STW | | (2-way) dimmer module controlling a 100 Watt lamp. | +----------------------------------------------------------------------------*/ int presetlevel2pct ( unsigned char level ) { static unsigned char table[32] = { 0, 18, 21, 23, 27, 28, 31, 34, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 67, 70, 73, 76, 79, 82, 85, 87, 90, 92, 95, 97, 99, 100 }; return (int)table[min(31u, level)]; } /*----------------------------------------------------------------------------+ | Converts extended preset level (0-63) to (int) percent of full ON voltage. | | (This is based on measurement of output voltage for a LM14A module | | controlling a 100 Watt lamp.) | +----------------------------------------------------------------------------*/ int ext3level2pct ( unsigned char level ) { static unsigned char table[64] = { 0, 12, 13, 15, 15, 17, 18, 20, 21, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, 46, 48, 50, 51, 53, 54, 56, 58, 60, 61, 63, 64, 66, 67, 69, 71, 73, 74, 76, 77, 79, 80, 82, 83, 85, 86, 88, 88, 90, 91, 92, 93, 95, 96, 97, 98, 99, 100, 100 }; return (int)table[min(63u, level)]; } #else /*----------------------------------------------------------------------------+ | Converts (old) preset level (0-31) to (int) percent of full ON voltage. | | This is just a linear translation. | +----------------------------------------------------------------------------*/ int presetlevel2pct ( unsigned char level ) { return 100 * (int)level / 31; } /*----------------------------------------------------------------------------+ | Converts extended preset level (0-63) to (int) percent of full ON voltage. | | (This is linearized from actual data - max deviation about 4% of full On). | +----------------------------------------------------------------------------*/ int ext3level2pct ( unsigned char level ) { level = min(62, level); return (level > 0) ? 12 + 88 * ((int)level - 1) / 61 : 0; } #endif /* Measured vs Linearized data */ /*----------------------------------------------------------------------------+ | Return the name of the function for the argument trigger signal | +----------------------------------------------------------------------------*/ char *lookup_function ( int signal ) { int j; for ( j = 0; j < (int)NTRIGTYPES; j++ ) { if ( trig_type[j].signal == signal ) return trig_type[j].label; } return ""; } /*----------------------------------------------------------------------------+ | Initialize the state structure elements for (X10 encoded) hcode | +----------------------------------------------------------------------------*/ void x10state_init ( unsigned char hcode ) { int ucode, group; x10state[hcode].lastunit = 0; x10state[hcode].lastcmd = 0xff; x10state[hcode].reset = 0; x10state[hcode].addressed = 0; x10state[hcode].sticky = 0; x10state[hcode].exclusive = 0; x10state[hcode].vaddress = 0; x10state[hcode].xconfigmode = 0; x10state[hcode].state[OnState] = 0; x10state[hcode].state[DimState] = 0; x10state[hcode].state[ChgState] = 0; x10state[hcode].state[LightsOnState] = 0; x10state[hcode].launched = 0; x10state[hcode].statusflags = 0; for ( ucode = 0; ucode < 16; ucode++ ) { x10state[hcode].dimlevel[ucode] = 0; x10state[hcode].memlevel[ucode] = maxdimlevel[hcode][ucode]; x10state[hcode].vident[ucode] = 0; x10state[hcode].timestamp[ucode] = 0; /* Init timestamp */ x10state[hcode].vflags[ucode] = 0; x10state[hcode].grpmember[ucode] = 0; for ( group = 0; group < 4; group++ ) { x10state[hcode].grpaddr[ucode] = 0; x10state[hcode].grplevel[ucode][group] = 0; } } return; } /*----------------------------------------------------------------------------+ | Initialize the state structure for all housecodes and global data except | | security flags and countdown timer counts. | +----------------------------------------------------------------------------*/ void x10state_init_all ( void ) { int hcode, j; for ( hcode = 0; hcode < 16; hcode++ ) x10state_init(hcode); x10global.longvdata = 0; x10global.lastvtype = 0; x10global.lasthc = 0xff; x10global.lastaddr = 0; x10global.flags &= (GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING | GLOBSEC_TAMPER); update_global_nightdark_flags(time(NULL)); for ( j = 0; j < NumCountdownTimers; j++ ) x10global.timer_count[j] = countdown_timer[j].count; write_x10state_file(); return; } /*----------------------------------------------------------------------------+ | Initialize the cumulative address table for all housecodes. | +----------------------------------------------------------------------------*/ void x10state_init_others ( void ) { int hcode; for ( hcode = 0; hcode < 16; hcode++ ) x10state[hcode].sticky = 0; return; } /*----------------------------------------------------------------------------+ | Set the security flags represented by the argument (which has been shifted | | to fit into the size of the argument). | | With this version, an Armed or Pending state cannot be changed unless | | first Disarmed. | +----------------------------------------------------------------------------*/ int set_globsec_flags_strict ( unsigned char sflags ) { unsigned long lflags; int retcode = 1; lflags = ((unsigned long)sflags << GLOBSEC_SHIFT) & (GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING); if ( lflags == 0 ) { /* Disarm */ x10global.flags &= ~(GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING); set_timer_countdown(ArmDelayTimer, 0); x10global.timer_count[ArmDelayTimer] = 0; retcode = 0; } else if ( !(x10global.flags & (GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING)) ) { /* Arm */ x10global.flags |= lflags; if ( x10global.flags & GLOBSEC_PENDING ) { set_timer_countdown(ArmDelayTimer, config.arm_max_delay); x10global.timer_count[ArmDelayTimer] = config.arm_max_delay; } retcode = 0; } return retcode; } /*----------------------------------------------------------------------------+ | Set the security flags represented by the argument (which has been shifted | | to fit into the size of the argument). | | Allowed changes: | | Disarmed --> Armed or Pending | | Armed --> Disarmed | | Pending --> Armed or Disarmed | | Home <--> Away | +----------------------------------------------------------------------------*/ int set_globsec_flags_medium ( unsigned char sflags ) { unsigned long lflags, gflags; lflags = ((unsigned long)sflags << GLOBSEC_SHIFT) & (GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING); gflags = x10global.flags & (GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING); if ( lflags == gflags ) { return 0; } if ( lflags == 0 ) { /* Disarm */ x10global.flags &= ~(GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING); set_timer_countdown(ArmDelayTimer, 0); x10global.timer_count[ArmDelayTimer] = 0; return 0; } if ( gflags == 0 ) { /* Arm or Pending */ x10global.flags |= lflags; if ( x10global.flags & GLOBSEC_PENDING ) { set_timer_countdown(ArmDelayTimer, config.arm_max_delay); x10global.timer_count[ArmDelayTimer] = config.arm_max_delay; } return 0; } if ( lflags & GLOBSEC_ARMED ) { /* Arm */ x10global.flags &= ~GLOBSEC_PENDING; x10global.flags |= GLOBSEC_ARMED; set_timer_countdown(ArmDelayTimer, 0); x10global.timer_count[ArmDelayTimer] = 0; if ( (lflags & GLOBSEC_HOME) != (gflags & GLOBSEC_HOME) ) { /* Reverse Home or Away */ x10global.flags &= ~GLOBSEC_HOME; x10global.flags |= (lflags & GLOBSEC_HOME); } return 0; } return 1; } /*----------------------------------------------------------------------------+ | Set the security flags represented by the argument (which has been shifted | | to fit into the size of the argument). | | Allowed transitions: Any | +----------------------------------------------------------------------------*/ int set_globsec_flags_loose ( unsigned char sflags ) { x10global.flags &= ~(GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING); x10global.flags |= ((unsigned long)sflags << GLOBSEC_SHIFT) & (GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING); if ( x10global.flags & GLOBSEC_PENDING ) { set_timer_countdown(ArmDelayTimer, config.arm_max_delay); x10global.timer_count[ArmDelayTimer] = config.arm_max_delay; } else { set_timer_countdown(ArmDelayTimer, 0); x10global.timer_count[ArmDelayTimer] = 0; } return 0; } int set_globsec_flags ( unsigned char sflags ) { return (config.arm_logic == ArmLogicStrict) ? set_globsec_flags_strict(sflags) : (config.arm_logic == ArmLogicMedium) ? set_globsec_flags_medium(sflags) : (config.arm_logic == ArmLogicLoose) ? set_globsec_flags_loose(sflags) : 0; } int set_seclights_flag ( unsigned char sflags ) { if ( sflags ) x10global.flags |= GLOBSEC_SLIGHTS; else x10global.flags &= ~GLOBSEC_SLIGHTS; return 0; } int set_tamper_flag ( unsigned char sflags ) { if ( sflags ) x10global.flags |= GLOBSEC_TAMPER; else x10global.flags &= ~GLOBSEC_TAMPER; return 0; } /*----------------------------------------------------------------------------+ | Display the Armed/Disarmed state of the system - c_show2() | +----------------------------------------------------------------------------*/ void show_armed_status ( void ) { char *tflag; tflag = (x10global.flags & GLOBSEC_TAMPER) ? "*TAMPER*, " : ""; if ( x10global.flags & GLOBSEC_PENDING ) { printf("%sSystem will be Armed %s in %ld seconds\n", tflag, ((x10global.flags & GLOBSEC_HOME) ? "Home" : "Away"), x10global.timer_count[ArmDelayTimer]); } else if ( (x10global.flags & GLOBSEC_ARMED) == 0 ) { printf("%sSystem is Disarmed\n", tflag); } else { printf("%sSystem is Armed %s\n", tflag, ((x10global.flags & GLOBSEC_HOME) ? "Home" : "Away") ); } return; } /*----------------------------------------------------------------------------+ | Display the Armed/Disarmed state of the system (poll.c) | +----------------------------------------------------------------------------*/ char *display_armed_status ( void ) { static char outbuf[120]; if ( x10global.flags & GLOBSEC_PENDING ) { sprintf(outbuf, "%sSystem will be Armed %s in %ld seconds.", ((x10global.flags & GLOBSEC_TAMPER) ? "*TAMPER*, " : ""), ((x10global.flags & GLOBSEC_HOME) ? "Home" : "Away"), x10global.timer_count[ArmDelayTimer]); return outbuf; } *outbuf = '\0'; if ( x10global.flags & GLOBSEC_TAMPER ) strcat(outbuf, "*TAMPER*, "); if ( (x10global.flags & GLOBSEC_ARMED) == 0 ) { strcat(outbuf, "System is Disarmed"); } else { strcat(outbuf, "System is Armed "); strcat(outbuf, ((x10global.flags & GLOBSEC_HOME) ? "Home" : "Away")); } return outbuf; } /*----------------------------------------------------------------------------+ | Send a powerfail message to the heyu monitor/state engine | +----------------------------------------------------------------------------*/ void send_pfail_msg ( int spptty, unsigned char command, unsigned char parm ) { static unsigned char template[7] = { 0xff,0xff,0xff,3,ST_COMMAND,0,0}; template[5] = command; template[6] = parm; write(spptty, template, 7); return; } /*----------------------------------------------------------------------------+ | Send virtual data to the heyu monitor/state engine | +----------------------------------------------------------------------------*/ void send_virtual_data ( unsigned char address, unsigned char vdata, unsigned char vtype, unsigned char vid, unsigned char byte2, unsigned char byte3 ) { static unsigned char template[19] = { 0xff,0xff,0xff,3,ST_COMMAND,ST_SOURCE,0, 0xff,0xff,0xff,8,ST_COMMAND,ST_VDATA,0,0,0,0,0,0}; template[6] = (unsigned char)heyu_parent; template[13] = address; template[14] = vdata; template[15] = vtype; template[16] = vid; template[17] = byte2; template[18] = byte3; write(sptty, template, 19); return; } /*----------------------------------------------------------------------------+ | Send a state command to the heyu monitor/state engine | +----------------------------------------------------------------------------*/ void send_sptty_x10state_command ( int spptty, unsigned char command, unsigned char parm ) { static unsigned char template[7] = { 0xff,0xff,0xff,3,ST_COMMAND,0,0}; template[5] = command; template[6] = parm; write(spptty, template, 7); return; } /*----------------------------------------------------------------------------+ | Send a state command to the heyu monitor/state engine | +----------------------------------------------------------------------------*/ void send_x10state_command ( unsigned char command, unsigned char parm ) { extern int sptty; send_sptty_x10state_command(sptty, command, parm); return; } /*----------------------------------------------------------------------------+ | Translate an X10-encoded bitmap to a linear unit bitmap, i.e., where | | unit 1 = 1, unit 2 = 2, unit 3 = 4, unit 4 = 8, etc. | +----------------------------------------------------------------------------*/ unsigned int x10map2linmap ( unsigned int x10map ) { int j; unsigned int mask, linmap; static unsigned char bitshift[] = {12,4,2,10,14,6,0,8,13,5,3,11,15,7,1,9}; mask = 1; linmap = 0; for ( j = 0; j < 16; j++ ) { if ( x10map & mask ) linmap |= (1 << bitshift[j]); mask <<= 1; } return linmap; } /*----------------------------------------------------------------------------+ | Display a text message in the heyu monitor/state engine logfile | | Maximum length 80 characters. | +----------------------------------------------------------------------------*/ int display_x10state_message ( char *message ) { extern int sptty; int size; unsigned char buf[88]; static unsigned char template[7] = { 0xff,0xff,0xff,0,ST_COMMAND,ST_MSG,0}; if ( (size = strlen(message)) > (int)(sizeof(buf) - 8) ) { fprintf(stderr, "Log message exceeds %d characters\n", (int)(sizeof(buf) - 8)); return 1; } memcpy(buf, template, sizeof(template)); memcpy(buf + 7, message, size + 1); buf[6] = size + 1; buf[3] = size + 1 + 3; write(sptty, buf, size + 8); return 0; } /*----------------------------------------------------------------------------+ | Display a text message in the heyu monitor/state engine logfile | | Maximum length 80 characters. | +----------------------------------------------------------------------------*/ int c_logmsg ( int argc, char *argv[] ) { if ( argc < 3 ) { fprintf(stderr, "%s - Too few arguments\n", argv[1]); return 1; } else if ( argc > 3 ) { fprintf(stderr, "%s - Too many arguments\n", argv[1]); return 1; } return display_x10state_message(argv[2]); } /*----------------------------------------------------------------------------+ | Send an arbitrary state message to the engine. The first parameter must | | be the ST_XXX value (in hex), then followed by the hex bytes to be sent. | +----------------------------------------------------------------------------*/ int c_sendarbst ( int argc, char *argv[] ) { unsigned char buf[80]; long hex; int j; char *sp; if ( argc < 4 ) { fprintf(stderr, "%s - Too few arguments\n", argv[1]); return 1; } hex = strtol(argv[2], &sp, 16); if ( !strchr(" /r/n", *sp) || hex < 0 || hex > 0xff ) { fprintf(stderr, "Invalid ST_xxxx value '%s'\n", argv[2]); return 1; } buf[0] = 0xff; buf[1] = 0xff; buf[2] = 0xff; buf[3] = argc - 1; buf[4] = ST_COMMAND; buf[5] = (unsigned char)hex; for ( j = 3; j < argc; j++ ) { hex = strtol(argv[j], &sp, 16); if ( !strchr(" /r/n", *sp) || hex < 0 || hex > 0xff ) { fprintf(stderr, "Invalid hex byte value '%s'\n", argv[j]); return 1; } buf[j + 3] = (unsigned char)hex; } write(sptty, buf, argc + 3); return 0; } /*----------------------------------------------------------------------------+ | Check the dims table to determine the onstate and dimstate of modules on | | argument housecode, returning X10 bitmaps back through the argument list. | +----------------------------------------------------------------------------*/ void get_states ( unsigned char hcode, unsigned int *onstate, unsigned int *dimstate ) { int ucode; unsigned char level; unsigned int mask, offstate, ex3mask, premask, stdmask, ex0mask, vdatamask; ex3mask = modmask[Ext3Mask][hcode]; premask = modmask[PresetMask][hcode]; stdmask = modmask[StdMask][hcode]; ex0mask = modmask[Ext0Mask][hcode]; vdatamask = modmask[VdataMask][hcode]; offstate = 0; *dimstate = 0; for ( ucode = 0; ucode < 16; ucode++ ) { mask = (1 << ucode); level = x10state[hcode].dimlevel[ucode]; if ( level == 0 ) offstate |= mask; else if ( (mask & ex3mask && level < 62) || (mask & ex0mask && level < 25) || (mask & premask && level < 31) || (mask & stdmask && level < 210) ) { *dimstate |= mask; } else if ( !(mask & (ex3mask | ex0mask | premask | stdmask)) && level < ondimlevel[hcode][ucode] ) *dimstate |= mask; } *onstate = ~offstate & ~vdatamask; *dimstate &= ~vdatamask; return; } /*----------------------------------------------------------------------------+ | Determine the dimstate of modules on argument housecode, returning an X10 | | bitmap of units which are dimmed. | +----------------------------------------------------------------------------*/ unsigned int get_dimstate ( unsigned char hcode ) { int ucode; unsigned int bitmap, mask, dimstate = 0; /* Check Extended Preset Type 3 Dim Units */ bitmap = modmask[Ext3DimMask][hcode]; if ( bitmap ) { mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask && x10state[hcode].dimlevel[ucode] > 0 && x10state[hcode].dimlevel[ucode] < 62 ) { dimstate |= mask; } mask = mask << 1; } } /* Check Extended Preset Type 0 Dim Units */ bitmap = modmask[Ext0Mask][hcode]; if ( bitmap ) { mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask && x10state[hcode].dimlevel[ucode] > 0 && x10state[hcode].dimlevel[ucode] < 25 ) { dimstate |= mask; } mask = mask << 1; } } /* Check Preset Units */ bitmap = modmask[PresetMask][hcode]; if ( bitmap ) { mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask && x10state[hcode].dimlevel[ucode] > 0 && x10state[hcode].dimlevel[ucode] < 31 ) { dimstate |= mask; } mask = mask << 1; } } /* Check Standard Non-Preset Units */ bitmap = (modmask[DimMask][hcode] | modmask[BriMask][hcode]) & ~modmask[Ext3DimMask][hcode] & ~modmask[PresetMask][hcode]; if ( bitmap ) { mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask && x10state[hcode].dimlevel[ucode] > 0 && x10state[hcode].dimlevel[ucode] < 210 ) { dimstate |= mask; } mask = mask << 1; } } return dimstate; } /*----------------------------------------------------------------------------+ | Copy current dimlevel > 0 (0 is the same as OFF) to memlevel for bitmap | | units. | This is only valid for units which support the preset or extended preset | | commands. | +----------------------------------------------------------------------------*/ void save_dimlevel ( unsigned char hcode, unsigned int bitmap ) { int ucode; int level; unsigned int mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { level = x10state[hcode].dimlevel[ucode]; if ( level > 0 ) x10state[hcode].memlevel[ucode] = level; } mask = mask << 1; } return; } /*----------------------------------------------------------------------------+ | Restore current dimlevel from memlevel for bitmap units | | This is only valid for units which support the preset or extended preset | | commands. | +----------------------------------------------------------------------------*/ void restore_dimlevel ( unsigned char hcode, unsigned int bitmap ) { int ucode; int level; unsigned int mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { level = x10state[hcode].memlevel[ucode]; x10state[hcode].dimlevel[ucode] = level; } mask = mask << 1; } return; } /*----------------------------------------------------------------------------+ | Set a specific raw dimlevel 0-255 (limited to native level range). | +----------------------------------------------------------------------------*/ void set_raw_level ( int rawlevel, unsigned char hcode, unsigned int bitmap ) { int ucode; unsigned int mask = 1; unsigned char level; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { level = max(0, rawlevel); level = min(level, maxdimlevel[hcode][ucode]); x10state[hcode].dimlevel[ucode] = level; } mask = mask << 1; } return; } /*----------------------------------------------------------------------------+ | Set a specific dimlevel. | +----------------------------------------------------------------------------*/ void set_dimlevel ( int level , unsigned char hcode, unsigned int bitmap ) { int ucode; unsigned int mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { x10state[hcode].dimlevel[ucode] = level; } mask = mask << 1; } return; } /*----------------------------------------------------------------------------+ | Set a specific memlevel. | +----------------------------------------------------------------------------*/ void set_memlevel ( int level , unsigned char hcode, unsigned int bitmap ) { int ucode; unsigned int mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { x10state[hcode].memlevel[ucode] = level; } mask = mask << 1; } return; } /*----------------------------------------------------------------------------+ | Set the ident for virtual modules | +----------------------------------------------------------------------------*/ void set_ident ( unsigned char vident , unsigned char hcode, unsigned int bitmap ) { int ucode; unsigned int mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { x10state[hcode].vident[ucode] = vident; } mask = mask << 1; } return; } /*----------------------------------------------------------------------------+ | Set dimlevels to '0n' level. | +----------------------------------------------------------------------------*/ void set_max_level ( unsigned char hcode, unsigned int bitmap ) { int ucode; if ( !bitmap ) return; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & (1 << ucode) ) { x10state[hcode].dimlevel[ucode] = maxdimlevel[hcode][ucode]; } } return; } /*----------------------------------------------------------------------------+ | Set memlevels to '0n' level. | +----------------------------------------------------------------------------*/ void set_max_memlevel ( unsigned char hcode, unsigned int bitmap ) { int ucode; if ( !bitmap ) return; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & (1 << ucode) ) { x10state[hcode].memlevel[ucode] = maxdimlevel[hcode][ucode]; } } return; } /*----------------------------------------------------------------------------+ | Set dimlevels to '0n' level. | +----------------------------------------------------------------------------*/ void set_on_level ( unsigned char hcode, unsigned int bitmap ) { int ucode; if ( !bitmap ) return; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & (1 << ucode) ) { x10state[hcode].dimlevel[ucode] = ondimlevel[hcode][ucode]; } } return; } /*----------------------------------------------------------------------------+ | Set a specific dimlevel no higher than that stored in memlevel. | +----------------------------------------------------------------------------*/ void set_limit_dimlevel ( int level , unsigned char hcode, unsigned int bitmap ) { int ucode; unsigned int mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { x10state[hcode].dimlevel[ucode] = min(level, (int)x10state[hcode].memlevel[ucode]); } mask = mask << 1; } return; } /*----------------------------------------------------------------------------+ | Update the stored dimlevel by the amount of the argument level 0-210, | | positive for brightens, negative for dims. | +----------------------------------------------------------------------------*/ void update_dimlevel ( int level , unsigned char hcode, unsigned int bitmap ) { int ucode, newlevel, abslev, table, delta; unsigned int active, mask; /* Table used in conversion of dims to change in ext3 preset level */ /* Table[0] is used for Base-2 dims, table[1] for others */ static int ext3table[2][17] = { {0, 5, 9, 14, 18, 22, 27, 31, 36, 40, 44, 49, 53, 58, 61, 62, 62}, {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 62}, }; /* Table used in conversion of dims to change in old-style Preset level */ /* Table [0] is brights from level 0, [1] is dims from level 31. */ static int presettable[2][15] = { {1, 1, 3, 6, 8, 11, 14, 16, 19, 22, 24, 27, 29, 30, 32}, {1, 1, 4, 7, 9, 12, 15, 17, 20, 22, 25, 28, 30, 32, 32}, }; if ( !bitmap ) return; /* Standard Dimmable Units */ active = bitmap & (modmask[DimMask][hcode] | modmask[BriMask][hcode]) & modmask[StdMask][hcode] ; if ( active ) { mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( active & mask ) { newlevel = level + x10state[hcode].dimlevel[ucode]; newlevel = min(210, newlevel); newlevel = max(newlevel, (mask & modmask[TargMask][hcode]) ? 0 : 3); x10state[hcode].dimlevel[ucode] = newlevel; } mask = mask << 1; } } /* Ext3 Units */ active = bitmap & modmask[Ext3Mask][hcode]; if ( active ) { abslev = abs(level); table = (abslev % 11 == 2) ? 0 : 1; delta = ext3table[table][min(abslev/11, 16)]; delta = (level < 0) ? -delta : delta; mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( active & mask ) { newlevel = delta + x10state[hcode].dimlevel[ucode]; newlevel = min(62, max(1, newlevel)); x10state[hcode].dimlevel[ucode] = newlevel; } mask = mask << 1; } } /* Ext0 (Shutter) Units */ active = bitmap & modmask[Ext0Mask][hcode]; if ( active ) { delta = level * 25 / 210 ; mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( active & mask ) { newlevel = delta + x10state[hcode].dimlevel[ucode]; newlevel = min(25, max(0, newlevel)); x10state[hcode].dimlevel[ucode] = newlevel; } mask = mask << 1; } } /* Preset1/Preset2 Units, zero-base levels 0-31 */ active = bitmap & modmask[PresetMask][hcode]; if ( active ) { abslev = abs(level); delta = (level > 0) ? presettable[0][min(abslev/11, 14)] : -presettable[1][min(abslev/11, 14)]; mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( active & mask ) { newlevel = delta + x10state[hcode].dimlevel[ucode]; newlevel = min(31, max(0, newlevel)); x10state[hcode].dimlevel[ucode] = newlevel; } mask = mask << 1; } } /* Virtual units */ active = bitmap & (modmask[DimMask][hcode] | modmask[BriMask][hcode]) & ~(modmask[StdMask][hcode] | modmask[Ext3Mask][hcode] | modmask[PresetMask][hcode] | modmask[Ext0Mask][hcode]); if ( active ) { mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( active & mask ) { newlevel = level + x10state[hcode].dimlevel[ucode]; newlevel = min((int)ondimlevel[hcode][ucode], newlevel); newlevel = max(newlevel, 0); x10state[hcode].dimlevel[ucode] = newlevel; } mask = mask << 1; } } return; } /*----------------------------------------------------------------------------+ | Update the sticky address state of housecode|units | | Addressing is cumulative until cleared by an initstate command. | +----------------------------------------------------------------------------*/ void x10state_update_sticky_addr ( unsigned char addrbyte ) { unsigned char hcode, ucode; unsigned int bitmap; hcode = (addrbyte & 0xf0u) >> 4; ucode = addrbyte & 0x0fu; bitmap = 1 << ucode; x10state[hcode].sticky |= bitmap; return; } /*----------------------------------------------------------------------------+ | Update the flag states. | +----------------------------------------------------------------------------*/ void update_flags ( unsigned char *buf ) { unsigned int flagmap; flagmap = (buf[3] << 8) | buf[4]; if ( buf[2] == SETFLAG ) x10global.flags |= flagmap; else x10global.flags &= ~flagmap; return; } /*----------------------------------------------------------------------------+ | Update the mask for units with an exclusive attribute. | +----------------------------------------------------------------------------*/ void update_exclusive_mask ( unsigned char hcode, unsigned int bitmap, unsigned int *mask ) { unsigned int others; if ( (others = modmask[Exc4Mask][hcode] & ~bitmap) != 0 ) { *mask |= ( bitmap & 0x4444u ) ? others & 0x4444u : ( bitmap & 0x2222u ) ? others & 0x2222u : ( bitmap & 0x8888u ) ? others & 0x8888u : ( bitmap & 0x1111u ) ? others & 0x1111u : 0; } if ( (others = modmask[Exc8Mask][hcode] & ~bitmap) != 0 ) { *mask |= ( bitmap & 0x6666u ) ? others & 0x6666u : ( bitmap & 0x9999u ) ? others & 0x9999u : 0; } *mask |= modmask[Exc16Mask][hcode] & ~bitmap; return; } /*----------------------------------------------------------------------------+ | Update the addressed/unaddresssed state of housecode|units | | Addressing is cumulative until reset by an intervening function code with | | the same housecode. | | An alloff (all_units_off) function unaddresses all units on that housecode.| +----------------------------------------------------------------------------*/ void x10state_update_addr ( unsigned char addrbyte, int *launchp ) { unsigned char hcode, ucode; unsigned int bitmap; int j; LAUNCHER *launcherp; extern unsigned int signal_source; launcherp = configp->launcherp; hcode = (addrbyte & 0xf0u) >> 4; ucode = addrbyte & 0x0f; bitmap = 1 << ucode; x10state[hcode].lastunit = code2unit(ucode); x10global.lasthc = hcode; if ( signal_source & RCVA ) { x10state[hcode].vaddress = bitmap; } else { x10global.lastaddr = bitmap; if ( x10state[hcode].reset ) { x10state[hcode].addressed = bitmap; x10state[hcode].reset = 0; x10state[hcode].exclusive = 0; } else { x10state[hcode].addressed |= bitmap; } update_exclusive_mask(hcode, bitmap, &x10state[hcode].exclusive); x10state[hcode].addressed &= ~x10state[hcode].exclusive; } *launchp = -1; if ( configp->script_mode & HEYU_HELPER ) return; j = 0; while ( launcherp && launcherp[j].line_no > 0 ) { if ( launcherp[j].type != L_ADDRESS || (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags ) { j++; continue; } if ( launcherp[j].hcode == hcode && launcherp[j].bmaptrig & bitmap && launcherp[j].source & signal_source ) { *launchp = j; launcherp[j].actfunc = 0; launcherp[j].genfunc = 0; launcherp[j].xfunc = 0; launcherp[j].level = 0; launcherp[j].bmaplaunch = bitmap; x10state[hcode].launched = bitmap; break; } j++; } return; } /*----------------------------------------------------------------------------+ | Update the x10state structure per the contents of the argument 'buf' | | buf[0] is the housecode|function byte, buf[1] is the dim level 1-210 for | | dims and brights. (This routine handles just the standard X10 functions - | | Type 3 Extended code functions are handled separately.) | | | | If the user has defined the type of module, e.g. lamp module, for a | | specific housecode|unit address, the change of state for that address | | is filtered by the supported features of that type of module, e.g. a dim | | signal will be ignored for appliance modules. | | | | The received signal and state are tested to see if any of the conditions | | exist for triggering the launching of an external script, and if so, the | | index of the launcher is passed back through argument 'launchp', | | otherwise -1 is passed back. | +----------------------------------------------------------------------------*/ void x10state_update_func ( unsigned char *buf, int *launchp ) { unsigned char hcode, func, level, rawdim, lasthc; unsigned char actfunc, genfunc; unsigned int lastaddr, addressed; unsigned int trigaddr, active = 0, mask, trigactive, inactive; unsigned int resumask, fullmask, fulloffmask, applmask, bbdimask, ext3dimask, lofmask, ext0mask; unsigned int lubmap, exclusmask; unsigned int bmaplaunch; unsigned int onstate, dimstate; unsigned long afuncmap, gfuncmap; int j, dims; LAUNCHER *launcherp; extern unsigned int signal_source; launcherp = configp->launcherp; *launchp = -1; if ( !i_am_state ) return; func = buf[0] & 0x0fu; hcode = (buf[0] & 0xf0u) >> 4; genfunc = actfunc = func; rawdim = 0; x10state[hcode].reset = 1; lasthc = x10global.lasthc; /* Preset1/Preset2 functions take special handling */ if ( func == 10 || func == 11 ) { level = hcode; hcode = x10global.lasthc; x10global.lasthc = level; /* Use zero-base level (0-31) here */ level = rev_low_nybble(level) + 16 * (func - 10); } else { x10global.lasthc = hcode; level = 0; } if ( hcode == lasthc && x10global.lastaddr ) lastaddr = x10global.lastaddr; else lastaddr = 0xffff; x10global.lastaddr = 0; gfuncmap = 0; mask = 0; lubmap = 1 << unit2code(x10state[hcode].lastunit); x10state[hcode].state[ChgState] = 0; x10state[hcode].launched = 0; addressed = (signal_source & RCVA) ? x10state[hcode].vaddress : x10state[hcode].addressed; trigaddr = addressed; switch ( func ) { case 0 : /* AllOff */ /* state = OffState; */ afuncmap = (1 << AllOffTrig); gfuncmap = (1 << OffTrig); genfunc = OffFunc; mask = modmask[AllOffMask][hcode]; trigaddr = 0xffff; lastaddr = 0xffff; onstate = x10state[hcode].state[OnState]; active = modmask[AllOffMask][hcode]; resumask = (modmask[ResumeMask][hcode] | modmask[ResumeDimMask][hcode]) & modmask[Ext3DimMask][hcode]; lofmask = modmask[PresetMask][hcode] & modmask[LightsOnFullMask][hcode] & x10state[hcode].state[LightsOnState]; save_dimlevel(hcode, active & resumask & ~lofmask); set_dimlevel(0, hcode, active); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); if ( signal_source & RCVA ) x10state[hcode].vaddress &= ~(modmask[AllOffMask][hcode] | modmask[VdataMask][hcode]); else x10state[hcode].addressed &= ~(modmask[AllOffMask][hcode] | modmask[VdataMask][hcode]); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] &= ~active; break; case 1 : /* LightsOn */ /* state = OnState; */ afuncmap = (1 << LightsOnTrig); gfuncmap = (1 << OnTrig); genfunc = OnFunc; mask = modmask[LightsOnMask][hcode]; trigaddr = 0xffff; lastaddr = 0xffff; resumask = modmask[ResumeMask][hcode]; fullmask = modmask[LightsOnFullMask][hcode]; onstate = x10state[hcode].state[OnState]; active = modmask[LightsOnMask][hcode]; /* Units which resume saved brightness */ restore_dimlevel(hcode, active & resumask); /* Units which go to 'On' level */ set_on_level(hcode, active & ~resumask & ~fullmask & modmask[OnFullMask][hcode]); /* Units which go to full brightness */ set_max_level(hcode, active & (fullmask | (~onstate & modmask[OnFullOffMask][hcode]) | modmask[TargMask][hcode])); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); if ( signal_source & RCVA ) { x10state[hcode].vaddress &= ~(modmask[LightsOnUnaddrMask][hcode] | modmask[VdataMask][hcode]); } else { x10state[hcode].addressed &= ~(modmask[LightsOnUnaddrMask][hcode] | modmask[VdataMask][hcode]); } x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] |= active; break; case 2 : /* On */ /* state = OnState; */ afuncmap = gfuncmap = (1 << OnTrig); if ( addressed == 0xffff ) { afuncmap = (1 << AllOnTrig); } mask = modmask[OnMask][hcode]; onstate = x10state[hcode].state[OnState]; resumask = modmask[ResumeMask][hcode]; fullmask = modmask[OnFullMask][hcode]; fulloffmask = modmask[OnFullOffMask][hcode]; applmask = ~(modmask[DimMask][hcode] | modmask[BriMask][hcode]); exclusmask = x10state[hcode].exclusive; active = addressed & modmask[OnMask][hcode]; /* Resume-enabled units will go to saved brightness */ restore_dimlevel(hcode, active & resumask); /* Modules which go to 'On' brightness */ set_on_level(hcode, active & ~resumask & (~onstate | modmask[TargMask][hcode])); /* Exclusive modules which turn Off */ set_dimlevel(0, hcode, exclusmask); save_dimlevel(hcode, active & resumask); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = /* active & */ ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] &= ~active; break; case 3 : /* Off */ /* state = OffState; */ afuncmap = gfuncmap = (1 << OffTrig); mask = modmask[OffMask][hcode]; onstate = x10state[hcode].state[OnState]; active = addressed & modmask[OffMask][hcode]; resumask = modmask[ResumeMask][hcode] | modmask[ResumeDimMask][hcode]; ext0mask = modmask[ResumeMask][hcode]; lofmask = modmask[PresetMask][hcode] & modmask[LightsOnFullMask][hcode] & x10state[hcode].state[LightsOnState]; save_dimlevel(hcode, active & resumask & ~lofmask & ~ext0mask); set_dimlevel(0, hcode, active); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] &= ~active; break; case 4 : /* Dim */ /* state = DimState; */ afuncmap = gfuncmap = (1 << DimTrig); genfunc = DimFunc; mask = modmask[DimMask][hcode]; onstate = x10state[hcode].state[OnState]; rawdim = buf[1]; dims = -(int)rawdim; active = addressed & modmask[DimMask][hcode]; bbdimask = modmask[BriDimMask][hcode]; resumask = modmask[ResumeDimMask][hcode]; ext3dimask = modmask[Ext3DimMask][hcode]; inactive = ext3dimask & ~(bbdimask | resumask) & ~onstate; /* Off resume-enabled units will first go to saved brightness */ restore_dimlevel(hcode, active & resumask & ~onstate & ext3dimask); /* Modules which first go to full brightness if Off */ set_max_level(hcode, active & bbdimask & ~onstate); update_dimlevel(dims, hcode, active & ~inactive); lofmask = modmask[PresetMask][hcode] & modmask[LightsOnFullMask][hcode] & x10state[hcode].state[LightsOnState]; save_dimlevel(hcode, active & resumask & ~lofmask); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] &= ~active; break; case 5 : /* Bright */ /* state = DimState; */ afuncmap = (1 << BriTrig); gfuncmap = (1 << DimTrig); genfunc = DimFunc; mask = modmask[BriMask][hcode]; rawdim = buf[1]; dims = (int)rawdim; active = addressed & modmask[BriMask][hcode]; onstate = x10state[hcode].state[OnState]; bbdimask = modmask[BriDimMask][hcode]; resumask = modmask[ResumeDimMask][hcode]; ext3dimask = modmask[Ext3DimMask][hcode]; inactive = ext3dimask & ~(bbdimask | resumask) & ~onstate; /* Off, resume-enabled units will first go to saved brightness */ restore_dimlevel(hcode, active & resumask & ~onstate); /* Modules which first go to full brightness if Off */ set_max_level(hcode, active & bbdimask & ~onstate); update_dimlevel(dims, hcode, active & ~inactive); lofmask = modmask[PresetMask][hcode] & modmask[LightsOnFullMask][hcode] & x10state[hcode].state[LightsOnState]; save_dimlevel(hcode, active & resumask & ~lofmask); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] &= ~active; break; case 6 : /* LightsOff */ /* state = OffState; */ afuncmap = (1 << LightsOffTrig); gfuncmap = (1 << OffTrig); genfunc = OffFunc; mask = modmask[LightsOffMask][hcode]; trigaddr = 0xffff; lastaddr = 0xffff; onstate = x10state[hcode].state[OnState]; active = modmask[LightsOffMask][hcode]; resumask = modmask[ResumeMask][hcode] | modmask[ResumeDimMask][hcode]; lofmask = modmask[PresetMask][hcode] & modmask[LightsOnFullMask][hcode] & x10state[hcode].state[LightsOnState]; save_dimlevel(hcode, active & resumask & ~lofmask); set_dimlevel(0, hcode, active); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); if ( signal_source & RCVA ) { x10state[hcode].vaddress &= ~(modmask[LightsOffUnaddrMask][hcode] | modmask[VdataMask][hcode]); } else { x10state[hcode].addressed &= ~(modmask[LightsOffUnaddrMask][hcode] | modmask[VdataMask][hcode]); } x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] &= ~active; break; case 10 : /* Preset1 */ case 11 : /* Preset2 */ if ( hcode == 0xff ) { /* Housecode unknown - can't do anything */ return; } onstate = x10state[hcode].state[OnState]; dimstate = x10state[hcode].state[DimState]; mask = modmask[PresetMask][hcode]; active = addressed & modmask[PresetMask][hcode]; resumask = modmask[ResumeMask][hcode]; afuncmap = (1 << PresetTrig); if ( level == 0 ) { /* Lowest level => Off */ /* state = OffState; */ gfuncmap = (1 << OffTrig); genfunc = OffFunc; save_dimlevel(hcode, active & resumask); set_dimlevel(0, hcode, active); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; } else if ( level == 31 ) { /* Highest level => On */ /* state = OnState; */ gfuncmap = (1 << OnTrig); genfunc = OnFunc; set_dimlevel(31, hcode, active); save_dimlevel(hcode, active & resumask); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; } else { /* Intermediate level => Dim */ /* state = DimState; */ gfuncmap = (1 << DimTrig); genfunc = DimFunc; set_dimlevel(level, hcode, active); save_dimlevel(hcode, active & resumask); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; } x10state[hcode].state[LightsOnState] &= ~active; /* Preset modules unaddress themselves after receiving a preset function */ /* (otherwise they'd be affected by the next non-Preset function on the */ /* same housecode), but not if they've _sent_ the preset in response to */ /* a status request. */ if ( !(x10state[hcode].lastcmd == StatusReqFunc && signal_source & RCVI) ) x10state[hcode].addressed &= ~active; break; case 13 : /* Status On */ /* state = OnState; */ afuncmap = (1 << StatusOnTrig); mask = modmask[StatusOnMask][hcode]; active = addressed & modmask[StatusOnMask][hcode]; onstate = x10state[hcode].state[OnState]; set_on_level(hcode, active & ~onstate); get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; x10state[hcode].state[LightsOnState] &= ~active; x10state[hcode].statusflags &= ~addressed; break; case 14 : /* Status Off */ /* state = OffState; */ afuncmap = (1 << StatusOffTrig); gfuncmap = 0; mask = modmask[StatusOffMask][hcode]; active = addressed & modmask[StatusOffMask][hcode]; onstate = x10state[hcode].state[OnState]; dimstate = x10state[hcode].state[DimState]; x10state[hcode].state[OnState] &= ~active; x10state[hcode].state[DimState] &= ~active; set_dimlevel(0, hcode, active); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[LightsOnState] &= ~active; x10state[hcode].statusflags &= ~addressed; break; case 15 : /* StatusReq */ afuncmap = (1 << StatusReqTrig); active = addressed & modmask[StatusMask][hcode]; x10state[hcode].statusflags |= addressed; break; case 8 : /* HailReq */ afuncmap = (1 << HailReqTrig); break; case 9 : /* Hail Ack */ afuncmap = (1 << HailAckTrig); break; case 12 : /* Data Xfer */ afuncmap = (1 << DataXferTrig); break; default : /* state = OffState; */ afuncmap = 0; mask = 0; trigaddr = 0; break; } x10state[hcode].lastcmd = func; for ( j = 0; j < 16; j++ ) { if ( addressed & (1 << j) ) x10state[hcode].timestamp[j] = time(NULL); } write_x10state_file(); /* Heyuhelper, if applicable */ if ( signal_source & RCVI && configp->script_mode & HEYU_HELPER ) { launch_heyuhelper(hcode, trigaddr, func); return; } bmaplaunch = 0; j = 0; while ( launcherp && launcherp[j].line_no > 0 ) { if ( (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags || launcherp[j].type != L_NORMAL || !(launcherp[j].bmaptrigemu & lastaddr) ) { j++; continue; } if ( launcherp[j].hcode == hcode && (launcherp[j].afuncmap & afuncmap || launcherp[j].gfuncmap & gfuncmap) && launcherp[j].source & signal_source ) { trigactive = trigaddr & (mask | launcherp[j].signal); if ( (bmaplaunch = (trigactive & launcherp[j].bmaptrig) & (x10state[hcode].state[ChgState] | ~launcherp[j].chgtrig)) ) { *launchp = j; launcherp[j].bmaplaunch = bmaplaunch; launcherp[j].actfunc = func; launcherp[j].genfunc = genfunc; launcherp[j].level = level + 1; /* preset 1-32 */ launcherp[j].rawdim = rawdim; break; } } j++; } x10state[hcode].launched = bmaplaunch; return; } /*----------------------------------------------------------------------------+ | Update the x10state structure per the contents of the argument 'buf' | | for type 3 extended codes. 'buf' will always contain 4 bytes. | | Supported Type/Functions are limited to 0x31, 0x33, 0x34, and 0x38. | | | | If the user has defined the type of module, e.g. lamp module, for a | | specific housecode|unit address, the change of state for that address | | is filtered by the supported features of that type of module, e.g. an | | extended preset dim signal will be ignored for appliance modules. | | | | The received signal and state are tested to see if any of the conditions | | exist for triggering the launching of an external script, and if so, the | | index of the launcher is passed back through argument 'launchp', | | otherwise -1 is passed back. | +----------------------------------------------------------------------------*/ void x10state_update_ext3func ( unsigned char *buf, int *launchp ) { unsigned char hcode, func, xfunc, xdata, ucode, subunit, level, grpbmap, group, subgroup; unsigned char grplevel; int oldlevel, memlevel; unsigned char actfunc, genfunc; unsigned int bitmap, trigaddr, mask, grpmask, active, trigactive, switchmask, onstate, dimstate; unsigned int bmaplaunch; unsigned long afuncmap, gfuncmap; int j; LAUNCHER *launcherp; extern unsigned int signal_source; launcherp = configp->launcherp; *launchp = -1; if ( !i_am_state ) return; func = buf[0] & 0x0f; if ( func != 7 ) return; actfunc = genfunc = func; afuncmap = gfuncmap = (1 << ExtendedTrig); hcode = (buf[0] & 0xf0u) >> 4; ucode = buf[1] & 0x0fu; subunit = (buf[1] & 0xf0u) >> 4; /* Subunit states are not supported */ if ( subunit > 0 ) return; xdata = buf[2]; level = xdata & 0x3f; xfunc = buf[3]; bitmap = 1 << ucode; trigaddr = bitmap; x10state[hcode].lastcmd = func; x10state[hcode].state[ChgState] = 0; x10state[hcode].lastunit = code2unit(ucode); x10global.lasthc = hcode; x10state[hcode].reset = 1; x10global.lastaddr = 0; onstate = x10state[hcode].state[OnState]; dimstate = x10state[hcode].state[DimState]; switchmask = modmask[Ext3Mask][hcode] & ~modmask[Ext3DimMask][hcode]; switch ( xfunc ) { case 0x30 : /* Include in group at current level */ mask = 0; gfuncmap = 0; genfunc = 0; if ( (bitmap & modmask[Ext3GrpExecMask][hcode]) == 0 ) break; group = (xdata & 0xc0u) >> 6; x10state[hcode].grpmember[ucode] |= (1 << group); grplevel = x10state[hcode].dimlevel[ucode]; x10state[hcode].grplevel[ucode][group] = grplevel; if ( xdata & 0x20u ) { subgroup = xdata & 0x0fu; if ( grplevel == 63 && (modmask[Ext3DimMask][hcode] & bitmap) ) x10state[hcode].grplevel[ucode][group] = 62; if ( modmask[Ext3GrpRelT1Mask][hcode] & bitmap ) { /* LM14A - All groups changed to subgroup */ x10state[hcode].grpaddr[ucode] = subgroup | 0x80u; x10state[hcode].grpmember[ucode] &= ~(0x80u); } else if ( modmask[Ext3GrpRelT2Mask][hcode] & bitmap ) { /* LM465-1 change to a different subgroup removes other groups */ if ( x10state[hcode].grpaddr[ucode] != (subgroup | 0x80u) ) { x10state[hcode].grpmember[ucode] &= (1 << group); } x10state[hcode].grpaddr[ucode] = subgroup | 0x80u; x10state[hcode].grpmember[ucode] &= ~(0x80u); } else if ( modmask[Ext3GrpRelT3Mask][hcode] & bitmap ) { /* WS467-1 is in both absolute and relative groups */ x10state[hcode].grpaddr[ucode] = subgroup | 0x80u; x10state[hcode].grpmember[ucode] |= 0x80u; } } else { x10state[hcode].grpaddr[ucode] = 0; } break; case 0x31 : /* Ext Preset */ if ( level > 0 && (switchmask & bitmap) ) { gfuncmap = (1 << OnTrig); genfunc = OnFunc; mask = modmask[Ext3Mask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] = ~x10state[hcode].state[OnState] & active; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= active; set_dimlevel(63, hcode, active); save_dimlevel(hcode, active); } else if ( level >= 62 ) { /* Full On is level 62 or 63 */ gfuncmap = (1 << OnTrig); genfunc = OnFunc; /* On function */ mask = modmask[Ext3DimMask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] = (~x10state[hcode].state[OnState] | x10state[hcode].state[DimState]) & active; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= ~active; set_dimlevel(62, hcode, active); save_dimlevel(hcode, active); } else if ( level == 0 ) { /* Level 0 is Off */ gfuncmap = (1 << OffTrig); genfunc = OffFunc; mask = modmask[Ext3Mask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] = (x10state[hcode].state[OnState] | x10state[hcode].state[DimState]) & active; x10state[hcode].state[OnState] &= ~active; x10state[hcode].state[DimState] &= ~active; save_dimlevel(hcode, active); set_dimlevel(0, hcode, active); } else { /* state = DimState; */ gfuncmap = (1 << DimTrig); genfunc = DimFunc; mask = modmask[Ext3DimMask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] = active & ~x10state[hcode].state[DimState]; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] |= active; set_dimlevel(level, hcode, active); save_dimlevel(hcode, active); } break; case 0x32 : /* Include in Group at specified level */ mask = 0; gfuncmap = 0; genfunc = 0; group = (xdata & 0xc0u) >> 6; grplevel = xdata & 0x3fu; if ( grplevel > 0 && (switchmask & bitmap) ) { grplevel = 63; } x10state[hcode].grpmember[ucode] |= (1 << group); x10state[hcode].grplevel[ucode][group] = grplevel; x10state[hcode].grpaddr[ucode] = 0; break; case 0x33 : /* Ext AllOn */ gfuncmap = (1 << OnTrig); genfunc = OnFunc; mask = modmask[Ext3Mask][hcode]; trigaddr = 0xffff; active = modmask[Ext3Mask][hcode]; x10state[hcode].state[ChgState] = active & (~x10state[hcode].state[OnState] | x10state[hcode].state[DimState]); x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= ~active; /* Ext AllOn sets full brightness regardless of previous dim level */ set_dimlevel(62, hcode, active); save_dimlevel(hcode, active); break; case 0x34 : /* Ext AllOff */ gfuncmap = (1 << OffTrig); genfunc = OffFunc; mask = modmask[Ext3Mask][hcode]; trigaddr = 0xffff; active = modmask[Ext3Mask][hcode]; x10state[hcode].state[ChgState] = active & (x10state[hcode].state[OnState] | x10state[hcode].state[DimState]); x10state[hcode].state[OnState] &= ~active; x10state[hcode].state[DimState] &= ~active; /* Does not set the module "memory" level to 0 */ save_dimlevel(hcode, active & modmask[Ext3DimMask][hcode]); set_dimlevel(0, hcode, active); break; case 0x35 : /* Remove Housecode|Unit from groups */ mask = 0; gfuncmap = 0; genfunc = 0; grpbmap = xdata & 0x0fu; if ( (xdata & 0xf0u ) == 0 ) x10state[hcode].grpmember[ucode] &= ~grpbmap; else { for ( j = 0; j < 16; j++ ) x10state[hcode].grpmember[j] &= ~grpbmap; } break; case 0x3c : /* Group Bright/Dim (one level) */ case 0x36 : /* Execute Group function */ mask = 0; gfuncmap = 0; genfunc = 0; trigaddr = 0; group = (xdata & 0xc0u) >> 6; grpmask = 1 << group; if ( (xdata & 0x20u) ) subgroup = (xdata & 0x0fu) | 0x80; else subgroup = 0; for ( j = 0; j < 16; j++ ) { bitmap = 1 << j; if ( (x10state[hcode].grpmember[j] & grpmask) == 0 || (modmask[Ext3GrpExecMask][hcode] & bitmap) == 0 || (x10state[hcode].grpaddr[j] != subgroup && (x10state[hcode].grpmember[j] & 0x80u) == 0) ) { continue; } ucode = j; bitmap = 1 << ucode; trigaddr |= bitmap; if ( xfunc == 0x3c ) { if ( (modmask[Ext3GrpBriDimMask][hcode] & bitmap) == 0 ) { continue; } oldlevel = x10state[hcode].dimlevel[ucode]; memlevel = x10state[hcode].memlevel[ucode]; if ( (modmask[Ext3GrpBriDimFullMask][hcode] & bitmap) != 0 ) { if ( oldlevel == 0 ) oldlevel = memlevel; } if ( xdata & 0x10u ) grplevel = min(63, oldlevel + 1); else grplevel = max(1, oldlevel - 1); } else { if ( (xdata & 0x10u) && (modmask[Ext3GrpOffMask][hcode] & bitmap) ) grplevel = 0; else if ( (xdata & 0x10u) && (modmask[Ext3GrpOffExecMask][hcode] & bitmap) ) grplevel = x10state[hcode].grplevel[ucode][group]; else grplevel = x10state[hcode].grplevel[ucode][group]; } if ( grplevel > 0 && (bitmap & switchmask) ) { gfuncmap |= (1 << OnTrig); genfunc = OnFunc; mask = modmask[Ext3Mask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] &= ~active; x10state[hcode].state[ChgState] |= ~x10state[hcode].state[OnState] & active; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= ~active; set_dimlevel(63, hcode, active); save_dimlevel(hcode, active); } else if ( grplevel >= 62 ) { /* Full On is level 62 or 63 */ gfuncmap |= (1 << OnTrig); genfunc = OnFunc; /* On function */ mask = modmask[Ext3DimMask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] &= ~active; x10state[hcode].state[ChgState] |= (~x10state[hcode].state[OnState] | x10state[hcode].state[DimState]) & active; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= ~active; set_dimlevel(62, hcode, active); save_dimlevel(hcode, active); } else if ( grplevel == 0 ) { /* Level 0 is Off */ gfuncmap |= (1 << OffTrig); genfunc = OffFunc; mask = modmask[Ext3Mask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] &= ~active; x10state[hcode].state[ChgState] |= (x10state[hcode].state[OnState] | x10state[hcode].state[DimState]) & active; x10state[hcode].state[OnState] &= ~active; x10state[hcode].state[DimState] &= ~active; save_dimlevel(hcode, active); set_dimlevel(0, hcode, active); } else { /* state = DimState; */ gfuncmap |= (1 << DimTrig); genfunc = DimFunc; mask = modmask[Ext3DimMask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] &= ~active; x10state[hcode].state[ChgState] |= active & ~x10state[hcode].state[DimState]; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] |= active; set_dimlevel(grplevel, hcode, active); save_dimlevel(hcode, active); } } break; case 0x37 : /* Ext Status Req */ gfuncmap = 0; genfunc = 0; mask = modmask[Ext3Mask][hcode]; active = bitmap & mask; if ( (xdata & 0x30u) == 0x10u ) { /* Extended PowerUp signal from module */ afuncmap = (1 << ExtPowerUpTrig); } else { x10state[hcode].statusflags |= bitmap; } break; case 0x38 : /* Ext Status Ack */ gfuncmap = 0; if ( level > 0 && (switchmask & bitmap) ) { gfuncmap = (1 << OnTrig); genfunc = OnFunc; mask = modmask[Ext3Mask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] = ~x10state[hcode].state[OnState] & active; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= ~active; set_dimlevel(63, hcode, active); x10state[hcode].statusflags &= ~bitmap; } else if ( level >= 62 ) { gfuncmap = (1 << OnTrig); genfunc = OnFunc; mask = modmask[Ext3DimMask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] = ~x10state[hcode].state[OnState] & active; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= ~active; set_dimlevel(62, hcode, active); save_dimlevel(hcode, active); x10state[hcode].statusflags &= ~bitmap; } else if ( level == 0 ) { /* This does not reflect the module's level */ /* "memory", only it's current status as OFF */ gfuncmap = (1 << OffTrig); genfunc = OffFunc; mask = modmask[Ext3Mask][hcode]; active = bitmap & mask; x10state[hcode].state[ChgState] = x10state[hcode].state[OnState] & active; x10state[hcode].state[OnState] &= ~active; x10state[hcode].state[DimState] &= ~active; /* This may be inaccurate, depending on module's history */ save_dimlevel(hcode, active & modmask[Ext3DimMask][hcode]); set_dimlevel(0, hcode, active); x10state[hcode].statusflags &= ~bitmap; } else { /* state = DimState; */ mask = modmask[Ext3DimMask][hcode]; gfuncmap = (1 << DimTrig); genfunc = DimFunc; active = bitmap & mask; x10state[hcode].state[ChgState] = ~x10state[hcode].state[DimState] & active; x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] |= active; set_dimlevel(level, hcode, active); save_dimlevel(hcode, active); x10state[hcode].statusflags &= ~bitmap; } break; case 0x39 : /* Extended Group Status Ack */ mask = modmask[Ext3Mask][hcode]; active = mask & bitmap; #if 0 if ( active ) { group = (xdata & 0xc0u) >> 6; x10state[hcode].grpmember[ucode] |= (1 << group); x10state[hcode].grplevel[ucode][group] = xdata & 0x3f; x10state[hcode].statusflags &= ~bitmap; } #endif if ( active ) { x10state[hcode].statusflags &= ~bitmap; } x10state[hcode].state[ChgState] &= ~bitmap; mask = 0; gfuncmap = 0; genfunc = 0; break; case 0x3a : /* Extended Group Status Nack */ mask = modmask[Ext3Mask][hcode]; active = mask & bitmap; #if 0 if ( active ) { group = (xdata & 0xc0u) >> 6; x10state[hcode].grpmember[ucode] &= ~(1 << group); x10state[hcode].statusflags &= ~bitmap; } #endif if ( active ) { x10state[hcode].statusflags &= ~bitmap; } x10state[hcode].state[ChgState] &= ~bitmap; mask = 0; gfuncmap = 0; genfunc = 0; break; case 0x3b : /* Configure modules */ mask = 0; gfuncmap = 0; genfunc = 0; x10state[hcode].xconfigmode = xdata & 0x03; break; default : /* state = OffState; */ mask = 0; gfuncmap = 0; genfunc = 0; break; } x10state[hcode].timestamp[ucode] = time(NULL); write_x10state_file(); /* Heyuhelper, if applicable */ if ( signal_source & RCVI && configp->script_mode & HEYU_HELPER ) { launch_heyuhelper(hcode, trigaddr, func); return; } bmaplaunch = 0; j = 0; while ( launcherp && launcherp[j].line_no > 0 ) { if ( (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags || launcherp[j].type != L_NORMAL ) { j++; continue; } if ( launcherp[j].hcode == hcode && (launcherp[j].afuncmap & afuncmap || launcherp[j].gfuncmap & gfuncmap) && launcherp[j].source & signal_source ) { trigactive = trigaddr & (mask | launcherp[j].signal); if ( (bmaplaunch = (trigactive & launcherp[j].bmaptrig) & (x10state[hcode].state[ChgState] | ~launcherp[j].chgtrig)) ) { *launchp = j; launcherp[j].actfunc = actfunc; launcherp[j].genfunc = genfunc; launcherp[j].xfunc = xfunc; launcherp[j].level = level; launcherp[j].bmaplaunch = bmaplaunch; break; } } j++; } x10state[hcode].launched = bmaplaunch; return; } #ifdef HASEXT0 /*----------------------------------------------------------------------------+ | Update the x10state structure per the contents of the argument 'buf' | | for type 0 extended codes. 'buf' will always contain 4 bytes. | | Supported Type/Functions are limited to 0x01, 0x02, 0x03, 0x04, 0x0B | | | | If the user has defined the type of module, e.g. lamp module, for a | | specific housecode|unit address, the change of state for that address | | is filtered by the supported features of that type of module, e.g. an | | extended preset dim signal will be ignored for appliance modules. | | | | The received signal and state are tested to see if any of the conditions | | exist for triggering the launching of an external script, and if so, the | | index of the launcher is passed back through argument 'launchp', | | otherwise -1 is passed back. | +----------------------------------------------------------------------------*/ void x10state_update_ext0func ( unsigned char *buf, int *launchp ) { unsigned char hcode, func, xfunc, ucode, subunit, level, limit; unsigned char actfunc, genfunc; unsigned int bitmap, trigaddr, mask, active, trigactive, onstate, dimstate; unsigned int bmaplaunch; unsigned long afuncmap, gfuncmap; int j; LAUNCHER *launcherp; extern unsigned int signal_source; launcherp = configp->launcherp; *launchp = -1; if ( !i_am_state ) return; func = buf[0] & 0x0f; if ( func != 7 ) return; actfunc = genfunc = func; afuncmap = (1 << ExtendedTrig); hcode = (buf[0] & 0xf0u) >> 4; ucode = buf[1] & 0x0fu; subunit = (buf[1] & 0xf0u) >> 4; /* Subunit states are not supported */ if ( subunit > 0 ) return; level = buf[2] & 0x1f; limit = 25; xfunc = buf[3]; bitmap = 1 << ucode; trigaddr = bitmap; x10state[hcode].lastcmd = func; x10state[hcode].state[ChgState] = 0; x10state[hcode].lastunit = code2unit(ucode); x10global.lasthc = hcode; x10state[hcode].reset = 1; x10global.lastaddr = 0; onstate = x10state[hcode].state[OnState]; dimstate = x10state[hcode].state[DimState]; switch ( xfunc ) { case 0x01 : /* Open, observe limit */ case 0x03 : /* Open, disregard and disable limit */ mask = modmask[Ext0Mask][hcode]; active = bitmap & mask; if ( level >= 25 ) { /* Full Open is level 25 */ level = 25; gfuncmap = (1 << OnTrig); genfunc = OnFunc; /* On function */ } else if ( level == 0 ) { /* Level 0 is Off */ gfuncmap = (1 << OffTrig); genfunc = OffFunc; } else { /* state = DimState; */ gfuncmap = (1 << DimTrig); genfunc = DimFunc; } if ( xfunc == 0x01 ) set_limit_dimlevel(level, hcode, active); else { set_dimlevel(level, hcode, active); set_max_memlevel(hcode, active); } get_states(hcode, &onstate, &dimstate); x10state[hcode].state[ChgState] = active & ((x10state[hcode].state[OnState] ^ onstate) | (x10state[hcode].state[DimState] ^ dimstate)); x10state[hcode].state[OnState] = onstate; x10state[hcode].state[DimState] = dimstate; break; case 0x02 : /* Set limit and open to that limit */ level = min(level, 25); gfuncmap = 0; genfunc = 0; mask = modmask[Ext0Mask][hcode]; active = bitmap & mask; set_memlevel(level, hcode, active); restore_dimlevel(hcode, active); break; case 0x04 : /* Open all shutters, disable limit */ gfuncmap = (1 << OnTrig); genfunc = OnFunc; mask = modmask[Ext0Mask][hcode]; trigaddr = 0xffff; active = modmask[Ext0Mask][hcode]; x10state[hcode].state[ChgState] = active & (~x10state[hcode].state[OnState] | x10state[hcode].state[DimState]); x10state[hcode].state[OnState] |= active; x10state[hcode].state[DimState] &= ~active; set_max_memlevel(hcode, active); break; case 0x0B : /* Close all shutters */ gfuncmap = (1 << OffTrig); genfunc = OffFunc; mask = modmask[Ext0Mask][hcode]; trigaddr = 0xffff; active = modmask[Ext0Mask][hcode]; x10state[hcode].state[ChgState] = active & (x10state[hcode].state[OnState] | x10state[hcode].state[DimState]); x10state[hcode].state[OnState] &= ~active; x10state[hcode].state[DimState] &= ~active; set_dimlevel(0, hcode, active); break; default : /* state = OffState; */ mask = 0; gfuncmap = 0; genfunc = 0; break; } x10state[hcode].timestamp[ucode] = time(NULL); write_x10state_file(); /* Heyuhelper, if applicable */ if ( signal_source & RCVI && configp->script_mode & HEYU_HELPER ) { launch_heyuhelper(hcode, trigaddr, func); return; } bmaplaunch = 0; j = 0; while ( launcherp && launcherp[j].line_no > 0 ) { if ( (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags || launcherp[j].type == L_RFFLOOD || launcherp[j].type == L_POWERFAIL ) { j++; continue; } if ( launcherp[j].hcode == hcode && (launcherp[j].afuncmap & afuncmap || launcherp[j].gfuncmap & gfuncmap) && launcherp[j].source & signal_source ) { trigactive = trigaddr & (mask | launcherp[j].signal); if ( (bmaplaunch = (trigactive & launcherp[j].bmaptrig) & (x10state[hcode].state[ChgState] | ~launcherp[j].chgtrig)) ) { *launchp = j; launcherp[j].actfunc = actfunc; launcherp[j].genfunc = genfunc; launcherp[j].xfunc = xfunc; launcherp[j].level = level; launcherp[j].bmaplaunch = bmaplaunch; break; } } j++; } x10state[hcode].launched = bmaplaunch; return; } #else /*----------------------------------------------------------------------------+ | Dummy stub. | +----------------------------------------------------------------------------*/ void x10state_update_ext0func ( unsigned char *buf, int *launchp ) { *launchp = -1; return; } #endif /* End of HASEXT0 block */ /*----------------------------------------------------------------------------+ | Handler for extended code type/function which are otherwise undefined. | | | | The received signal and state are tested to see if any of the conditions | | exist for triggering the launching of an external script, and if so, the | | index of the launcher is passed back through argument 'launchp', | | otherwise -1 is passed back. | +----------------------------------------------------------------------------*/ void x10state_update_extotherfunc ( unsigned char *buf, int *launchp ) { unsigned char hcode, func, xfunc, ucode, subunit, level; unsigned char actfunc, genfunc; unsigned int bitmap, trigaddr, mask, trigactive; unsigned int bmaplaunch; unsigned long afuncmap, gfuncmap; int j; LAUNCHER *launcherp; extern unsigned int signal_source; launcherp = configp->launcherp; *launchp = -1; if ( !i_am_state ) return; func = buf[0] & 0x0f; if ( func != 7 ) return; actfunc = genfunc = func; afuncmap = (1 << ExtendedTrig); hcode = (buf[0] & 0xf0u) >> 4; ucode = buf[1] & 0x0fu; subunit = (buf[1] & 0xf0u) >> 4; /* Subunit states are not supported */ if ( subunit > 0 ) return; level = buf[2]; xfunc = buf[3]; bitmap = 1 << ucode; trigaddr = bitmap; x10state[hcode].lastcmd = func; x10state[hcode].state[ChgState] = 0; x10state[hcode].lastunit = code2unit(ucode); x10global.lasthc = hcode; x10state[hcode].reset = 1; x10global.lastaddr = 0; mask = 0; gfuncmap = 0; genfunc = 0; x10state[hcode].timestamp[ucode] = time(NULL); write_x10state_file(); /* Heyuhelper, if applicable */ if ( signal_source & RCVI && configp->script_mode & HEYU_HELPER ) { launch_heyuhelper(hcode, trigaddr, func); return; } bmaplaunch = 0; j = 0; while ( launcherp && launcherp[j].line_no > 0 ) { if ( (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags || launcherp[j].type == L_RFFLOOD || launcherp[j].type == L_POWERFAIL ) { j++; continue; } if ( launcherp[j].hcode == hcode && (launcherp[j].afuncmap & afuncmap || launcherp[j].gfuncmap & gfuncmap) && launcherp[j].source & signal_source ) { trigactive = trigaddr & (mask | launcherp[j].signal); if ( (bmaplaunch = (trigactive & launcherp[j].bmaptrig) & (x10state[hcode].state[ChgState] | ~launcherp[j].chgtrig)) ) { *launchp = j; launcherp[j].actfunc = actfunc; launcherp[j].genfunc = genfunc; launcherp[j].xfunc = xfunc; launcherp[j].level = level; launcherp[j].bmaplaunch = bmaplaunch; break; } } j++; } x10state[hcode].launched = bmaplaunch; return; } /*----------------------------------------------------------------------------+ | Update the x10state structure per the contents of the argument 'buf' | | for virtual modules. 'buf' will always contain 4 bytes. The first is the | | standard hcode|ucode byte, the second is the data 0x00-0xff, the third is | | the virtual type, the fourth is the module ID byte. | | | | Only modules with attribute VIRTUAL will be updated. | | | | The received signal and state are tested to see if any of the conditions | | exist for triggering the launching of an external script, and if so, the | | index of the launcher is passed back through argument 'launchp', | | otherwise -1 is passed back. | +----------------------------------------------------------------------------*/ int x10state_update_virtual ( unsigned char *buf, int len, int *launchp ) { unsigned char hcode, func, ucode, vdata, ident, vtype; unsigned char actfunc, genfunc; unsigned int bitmap, trigaddr, mask, active, trigactive, vflags; unsigned int bmaplaunch; unsigned long afuncmap, gfuncmap; unsigned long sflags; struct xlate_vdata_st xlate_vdata; int j, index, trig; char hc; LAUNCHER *launcherp; ALIAS *aliasp; extern unsigned int signal_source; launcherp = configp->launcherp; *launchp = -1; aliasp = config.aliasp; genfunc = 0; gfuncmap = 0; func = VdataFunc; trig = VdataTrig; vflags = 0; hcode = (buf[0] & 0xf0u) >> 4; ucode = buf[0] & 0x0fu; vdata = buf[1]; vtype = buf[2]; ident = buf[3]; bitmap = 1 << ucode; hc = code2hc(hcode); /* Run the decoding function for the module type, if any */ if ( (index = alias_rev_index(hc, bitmap, vtype, ident)) >= 0 && aliasp[index].modtype >= 0 && aliasp[index].xlate_func != NULL ) { xlate_vdata.vdata = vdata; xlate_vdata.hcode = hcode; xlate_vdata.ucode = ucode; xlate_vdata.ident = ident; xlate_vdata.nident = aliasp[index].nident; xlate_vdata.identp = aliasp[index].ident; xlate_vdata.optflags = aliasp[index].optflags; /* Tamper flag is sticky */ xlate_vdata.vflags = x10state[hcode].vflags[ucode] & SEC_TAMPER; if ( aliasp[index].xlate_func(&xlate_vdata) != 0 ) return 1; func = xlate_vdata.func; vflags = xlate_vdata.vflags; trig = xlate_vdata.trig; } x10state[hcode].vaddress = bitmap; x10state[hcode].lastcmd = func; x10state[hcode].lastunit = code2unit(ucode); x10state[hcode].vident[ucode] = ident; x10state[hcode].vflags[ucode] = vflags; x10state[hcode].timestamp[ucode] = time(NULL); x10state[hcode].state[ChgState] = 0; x10global.lasthc = hcode; x10global.lastaddr = 0; if ( vdata != x10state[hcode].dimlevel[ucode] ) { x10state[hcode].state[ChgState] |= bitmap; x10state[hcode].dimlevel[ucode] = vdata; } if ( func == DisarmFunc ) { set_globsec_flags(0); } else if ( func == ArmFunc ) { sflags = (vflags & SEC_HOME) ? GLOBSEC_HOME : 0; sflags |= (vflags & SEC_MIN) ? GLOBSEC_ARMED : GLOBSEC_PENDING; set_globsec_flags(sflags >> GLOBSEC_SHIFT); } else if ( func == SecLightsOnFunc ) set_seclights_flag(1); else if ( func == SecLightsOffFunc ) set_seclights_flag(0); if ( (func == TamperFunc || func == AlertFunc || func == ClearFunc) && (vflags & SEC_TAMPER) ) set_tamper_flag(1); actfunc = func; afuncmap = (1 << trig); trigaddr = bitmap; mask = modmask[VdataMask][hcode]; active = bitmap & mask; if ( i_am_state ) write_x10state_file(); /* Heyuhelper, if applicable */ if ( i_am_state && signal_source & RCVI && configp->script_mode & HEYU_HELPER ) { launch_heyuhelper(hcode, trigaddr, func); return 0; } bmaplaunch = 0; j = 0; while ( launcherp && launcherp[j].line_no > 0 ) { if ( (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags || (launcherp[j].vflags & x10state[hcode].vflags[ucode]) != launcherp[j].vflags || (launcherp[j].notvflags & ~x10state[hcode].vflags[ucode]) != launcherp[j].notvflags || launcherp[j].type == L_RFFLOOD || launcherp[j].type == L_POWERFAIL ) { j++; continue; } if ( launcherp[j].hcode == hcode && (launcherp[j].afuncmap & afuncmap || launcherp[j].gfuncmap & gfuncmap) && launcherp[j].source & signal_source ) { trigactive = trigaddr & (mask | launcherp[j].signal); if ( (bmaplaunch = (trigactive & launcherp[j].bmaptrig) & (x10state[hcode].state[ChgState] | ~launcherp[j].chgtrig)) ) { *launchp = j; launcherp[j].actfunc = actfunc; launcherp[j].genfunc = genfunc; launcherp[j].xfunc = 0; launcherp[j].level = vdata; launcherp[j].bmaplaunch = bmaplaunch; break; } } j++; } x10state[hcode].launched = bmaplaunch; return 0; } /*---------------------------------------------------------------------+ | Return the index of a -sensorfail launcher in the array of launchers| | which matches the launch conditions. Return -1 if none found. | +---------------------------------------------------------------------*/ int find_sensorfail_launcher ( int aliasindex ) { LAUNCHER *launcherp; int j; if ( (launcherp = configp->launcherp) == NULL || (configp->script_mode & HEYU_HELPER) ) return -1; if ( !i_am_state ) return -1; j = 0; while ( launcherp[j].line_no > 0 ) { if ( (launcherp[j].type != L_SENSORFAIL) || (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags ) { j++; continue; } launcherp[j].sensor = aliasindex; return j; } return -1; } /*---------------------------------------------------------------------+ | Return the index of a -timeout launcher in the array of launchers | | which matches the launch conditions. Return -1 if none found. | +---------------------------------------------------------------------*/ int find_timeout_launcher ( int timer ) { LAUNCHER *launcherp; int j; if ( (launcherp = configp->launcherp) == NULL || (configp->script_mode & HEYU_HELPER) ) return -1; if ( !i_am_state ) return -1; j = 0; while ( launcherp[j].line_no > 0 ) { if ( (launcherp[j].type != L_TIMEOUT) || (launcherp[j].timer != timer) || (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags ) { j++; continue; } return j; } return -1; } /*---------------------------------------------------------------------+ | Return the index of a powerfail launcher in the array of launchers | | which matches the launch conditions. Return -1 if none found. | +---------------------------------------------------------------------*/ int find_rfflood_launcher ( void ) { LAUNCHER *launcherp; int j; if ( (launcherp = configp->launcherp) == NULL || (configp->script_mode & HEYU_HELPER) ) return -1; if ( !i_am_state ) return -1; j = 0; while ( launcherp[j].line_no > 0 ) { if ( (launcherp[j].type != L_RFFLOOD) || (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags ) { j++; continue; } return j; } return -1; } /*---------------------------------------------------------------------+ | Return the index of a powerfail launcher in the array of launchers | | which matches the launch conditions. Return -1 if none found. | +---------------------------------------------------------------------*/ int find_powerfail_launcher ( unsigned char bootflag ) { LAUNCHER *launcherp; int j; if ( (launcherp = configp->launcherp) == NULL || (configp->script_mode & HEYU_HELPER) ) return -1; if ( !i_am_state ) return -1; j = 0; while ( launcherp[j].line_no > 0 ) { if ( (launcherp[j].type != L_POWERFAIL) || (launcherp[j].flags & x10global.flags) != launcherp[j].flags || (launcherp[j].notflags & ~x10global.flags) != launcherp[j].notflags || (launcherp[j].bootflag & bootflag) != launcherp[j].bootflag ) { j++; continue; } return j; } return -1; } /*---------------------------------------------------------------------+ | Display the detailed state of each unit on the argument X10-encoded | | housecode, i.e., Addressed, On, Dimmed, Changed. | +---------------------------------------------------------------------*/ void x10state_show ( unsigned char hcode ) { char lasthc; unsigned char lastcmd; unsigned int lastunitbmap; char minibuf[32]; char *chrs = ".*"; int lw = 13; char *bmap2asc2 ( unsigned int, unsigned int, char * ); lastcmd = x10state[hcode].lastcmd; if ( lastcmd == 0xff ) sprintf(minibuf, "_none_"); else sprintf(minibuf, funclabel[lastcmd]); lastunitbmap = (x10state[hcode].lastunit > 0) ? (1 << unit2code(x10state[hcode].lastunit)) : 0; lasthc = (x10global.lasthc == 0xff) ? '_' : code2hc(x10global.lasthc); printf("%*s %c\n", lw + 13, "Housecode", code2hc(hcode)); printf("%*s 1..4...8.......16\n", lw, "Unit:"); printf("%*s (%s)\n", lw, "LastUnit", bmap2asc(lastunitbmap, ".u")); printf("%*s (%s)\n", lw, "Addressed", bmap2asc2(x10state[hcode].vaddress, x10state[hcode].addressed, ".va")); printf("%*s (%s)\n", lw, "On", bmap2asc(x10state[hcode].state[OnState], chrs)); printf("%*s (%s)\n", lw, "Dimmed", bmap2asc(x10state[hcode].state[DimState], ".x")); printf("%*s (%s)\n", lw, "Changed", bmap2asc(x10state[hcode].state[ChgState], ".c")); printf("%*s (%s)\n", lw, "Launched", bmap2asc(x10state[hcode].launched, chrs)); printf("Last function this housecode: %s\n", minibuf); printf("Last Housecode = %c\n", lasthc); printf("\n"); fflush(stdout); return; } /*---------------------------------------------------------------------+ | Return a 16 character ASCII string displaying in ascending order | | an X10 unit bitmap, i.e., char[0] -> unit 1, char[15] -> unit 16. | | The argument chrs is a three-character string, the 1st character of | | represents 'unset' units and the 2nd character the 'set' bits in | | bitmap1. The 3rd character will overwrite the 2nd character for ` | | set bits in bitmap2 | +---------------------------------------------------------------------*/ char *bmap2asc2 ( unsigned int bitmap1, unsigned int bitmap2, char *chrs ) { int j; static char outbuf[17]; for ( j = 0; j < 16; j++ ) { if ( bitmap1 & (1 << j) ) outbuf[code2unit(j) - 1] = chrs[1]; else outbuf[code2unit(j) - 1] = chrs[0]; } for ( j = 0; j < 16; j++ ) { if ( bitmap2 & (1 << j) ) outbuf[code2unit(j) - 1] = chrs[2]; } outbuf[16] = '\0'; return outbuf; } /*---------------------------------------------------------------------+ | Return a 16 character ASCII string displaying in ascending order | | a 16-bit linear bitmap. | | The argument chrs is a three-character string, the 1st character of | | represents 'unset' units and the 2nd character the 'set' bits in | | bitmap1. The 3rd character will overwrite the 2nd character for ` | | set bits in bitmap2 | +---------------------------------------------------------------------*/ char *linmap2asc2 ( unsigned int bitmap1, unsigned int bitmap2, char *chrs ) { int j; static char outbuf[17]; for ( j = 0; j < 16; j++ ) { if ( bitmap1 & (1 << j) ) outbuf[j] = chrs[1]; else outbuf[j] = chrs[0]; } for ( j = 0; j < 16; j++ ) { if ( bitmap2 & (1 << j) ) outbuf[j] = chrs[2]; } outbuf[16] = '\0'; return outbuf; } /*---------------------------------------------------------------------+ | Return a 16 character ASCII string displaying a state bitmap for | | the unit corresponding to the index in the array, i.e. char[0] = | | unit 1; char[1] = unit 2; etc. | | The bits in each bitmap have the following meanings: | | Bit 0 : Addressed | | Bit 1 : Changed | | Bit 2 : Dimmed | | Bit 3 : On | | The arguments are the unit bitmaps indicating On, Dimmed, Changed, | | and Addressed, respectively. | +---------------------------------------------------------------------*/ char *bmap2statestr ( unsigned int onmap, unsigned int dimmap, unsigned int chgmap, unsigned int addrmap ) { int j; int val; unsigned int bmap; static char outbuf[17]; char hexdigit[] = "0123456789abcdef"; for ( j = 0; j < 16; j++ ) { val = 0; bmap = 1 << j; val += ( onmap & bmap ) ? 8 : 0; val += ( dimmap & bmap ) ? 4 : 0; val += ( chgmap & bmap ) ? 2 : 0; val += ( addrmap & bmap ) ? 1 : 0; outbuf[code2unit(j) - 1] = hexdigit[val]; } outbuf[16] = '\0'; return outbuf; } /*---------------------------------------------------------------------+ | Display launcher flags for specific launcher | +---------------------------------------------------------------------*/ void display_launcher_flags ( LAUNCHER *launcherp, int index ) { unsigned int bitmap1, bitmap2, bitmap3, mask; int k; bitmap1 = launcherp[index].flags; bitmap2 = launcherp[index].notflags; bitmap3 = launcherp[index].vflags; if ( launcherp[index].bootflag & R_ATSTART ) printf("boot "); else if ( launcherp[index].bootflag & R_NOTATSTART ) printf("notboot "); if ( bitmap3 & SEC_MIN ) printf("swmin "); if ( bitmap3 & SEC_MAX ) printf("swmax "); if ( bitmap3 & SEC_HOME ) printf("swhome "); if ( bitmap3 & SEC_AWAY ) printf("swaway "); if ( bitmap3 & SEC_LOBAT ) printf("lobat "); if ( launcherp[index].notvflags & SEC_LOBAT ) printf("notlobat "); if ( bitmap3 & SEC_MAIN ) printf("main "); if ( bitmap3 & SEC_AUX ) printf("aux "); if ( bitmap1 & NIGHT_FLAG ) printf("night "); if ( bitmap2 & NIGHT_FLAG ) printf("notnight "); if ( bitmap1 & DARK_FLAG ) printf("dark "); if ( bitmap2 & DARK_FLAG ) printf("notdark "); if ( bitmap1 & GLOBSEC_TAMPER ) printf("tamper "); if ( bitmap1 & GLOBSEC_FLOOD ) printf("started "); if ( bitmap2 & GLOBSEC_FLOOD ) printf("ended "); if ( bitmap1 & GLOBSEC_ARMED ) printf("armed "); if ( bitmap1 & GLOBSEC_PENDING ) printf("armpending "); if ((bitmap2 & GLOBSEC_ARMED) && (bitmap2 & GLOBSEC_PENDING)) printf("disarmed "); else if ( bitmap2 & GLOBSEC_ARMED ) printf("notarmed "); if ( bitmap1 & GLOBSEC_HOME ) printf("home "); if ( bitmap2 & GLOBSEC_HOME ) printf("away "); mask = 1; for ( k = 0; k < 16; k++ ) { if ( bitmap1 & mask ) printf("flag%d ", k + 1); if ( bitmap2 & mask ) printf("notflag%d ", k + 1); mask = mask << 1; } return; } /*---------------------------------------------------------------------+ | Display launcher sources for specific launcher | +---------------------------------------------------------------------*/ void display_launcher_sources ( LAUNCHER *launcherp, int index ) { unsigned int source; source = launcherp[index].source; if ( source & RCVI ) printf("rcvi "); if ( source & RCVT ) printf("rcvt "); if ( source & SNDC ) printf("sndc "); if ( source & SNDM ) printf("sndm "); if ( source & SNDS ) printf("snds "); if ( source & SNDT ) printf("sndt "); if ( source & SNDP ) printf("sndp "); if ( source & SNDA ) printf("snda "); if ( source & RCVA ) printf("rcva "); return; } /*---------------------------------------------------------------------+ | Display all script -sensorfail launch conditions. | +---------------------------------------------------------------------*/ void show_sensorfail_launcher ( void ) { int j, lw = 13; LAUNCHER *launcherp; if ( (launcherp = configp->launcherp) == NULL ) { return; } j = 0; while ( launcherp[j].line_no > 0 ) { if ( launcherp[j].type == L_SENSORFAIL ) { printf("\n"); printf("%*s: %-20s", lw, "Launch Mode", "-sensorfail"); printf("%s: ", "Flags"); display_launcher_flags(launcherp, j); printf("\n"); printf("%*s: %s\n", lw, "Script label", launcherp[j].label); printf("%*s: %s\n", lw, "Command Line", configp->scriptp[launcherp[j].scriptnum].cmdline); } j++; } return; } /*---------------------------------------------------------------------+ | Display all script timout launch conditions. | +---------------------------------------------------------------------*/ void show_timeout_launcher ( void ) { int j, lw = 13; LAUNCHER *launcherp; if ( (launcherp = configp->launcherp) == NULL ) { return; } j = 0; while ( launcherp[j].line_no > 0 ) { if ( launcherp[j].type == L_TIMEOUT ) { printf("\n"); printf("%*s: %-20s", lw, "Launch Mode", "-timeout"); printf("%s: timer%d ", "Flags", launcherp[j].timer); display_launcher_flags(launcherp, j); printf("\n"); printf("%*s: %s\n", lw, "Script label", launcherp[j].label); printf("%*s: %s\n", lw, "Command Line", configp->scriptp[launcherp[j].scriptnum].cmdline); } j++; } return; } /*---------------------------------------------------------------------+ | Display all script rfflood launch conditions. | +---------------------------------------------------------------------*/ void show_rfflood_launcher ( void ) { int j, lw = 13; LAUNCHER *launcherp; if ( (launcherp = configp->launcherp) == NULL ) { return; } j = 0; while ( launcherp[j].line_no > 0 ) { if ( launcherp[j].type == L_RFFLOOD ) { printf("\n"); printf("%*s: %-20s", lw, "Launch Mode", "-rfflood"); printf("%s: ", "Flags"); display_launcher_flags(launcherp, j); printf("\n"); printf("%*s: %s\n", lw, "Script label", launcherp[j].label); printf("%*s: %s\n", lw, "Command Line", configp->scriptp[launcherp[j].scriptnum].cmdline); } j++; } return; } /*---------------------------------------------------------------------+ | Display all script powerfail launch conditions. | +---------------------------------------------------------------------*/ void show_powerfail_launcher ( void ) { int j, lw = 13; LAUNCHER *launcherp; if ( (launcherp = configp->launcherp) == NULL ) { return; } j = 0; while ( launcherp[j].line_no > 0 ) { if ( launcherp[j].type == L_POWERFAIL ) { printf("\n"); printf("%*s: %-20s", lw, "Launch Mode", "-powerfail"); printf("%s: ", "Flags"); display_launcher_flags(launcherp, j); printf("\n"); printf("%*s: %s\n", lw, "Script label", launcherp[j].label); printf("%*s: %s\n", lw, "Command Line", configp->scriptp[launcherp[j].scriptnum].cmdline); } j++; } return; } /*---------------------------------------------------------------------+ | Display all script launch conditions for argument housecode. | +---------------------------------------------------------------------*/ void show_launcher ( unsigned char hcode ) { unsigned int signal, bmaptrig, chgtrig; unsigned long funcmap, afuncmap, gfuncmap; unsigned int bmaptrigemu; unsigned int source; int j, lw = 13; LAUNCHER *launcherp; char minibuf[64]; if ( (launcherp = configp->launcherp) == NULL ) { return; } j = 0; while ( launcherp[j].line_no > 0 ) { if ( launcherp[j].hcode != hcode || launcherp[j].type == L_POWERFAIL || launcherp[j].type == L_SENSORFAIL || launcherp[j].type == L_RFFLOOD || launcherp[j].type == L_TIMEOUT ) { j++; continue; } signal = launcherp[j].signal; afuncmap = launcherp[j].afuncmap; gfuncmap = launcherp[j].gfuncmap; funcmap = gfuncmap | afuncmap; bmaptrig = launcherp[j].bmaptrig; chgtrig = launcherp[j].chgtrig; source = launcherp[j].source; bmaptrigemu = launcherp[j].bmaptrigemu; printf("\n"); sprintf(minibuf, "%c%s", code2hc(launcherp[j].hcode), bmap2units(bmaptrig)); printf("%*s: %-16s", lw, "Address", minibuf); printf("%s: ", "Functions"); if ( gfuncmap & (1 << OnTrig) ) { printf("%s ", "gon"); funcmap &= ~((1 << OnTrig) | (1 << LightsOnTrig) | (1 << AllOnTrig)); } if ( gfuncmap & (1 << OffTrig) ) { printf("%s ", "goff"); funcmap &= ~((1 << OffTrig) | (1 << LightsOffTrig) | (1 << AllOffTrig)); } if ( gfuncmap & (1 << DimTrig) ) { printf("%s ", "gdim"); funcmap &= ~((1 << DimTrig) | (1 << BriTrig)); } if ( funcmap & (1 << OnTrig) ) printf("%s ", "on"); if ( funcmap & (1 << OffTrig) ) printf("%s ", "off"); if ( funcmap & (1 << DimTrig) ) printf("%s ", "dim"); if ( funcmap & (1 << BriTrig) ) printf("%s ", "bright"); if ( funcmap & (1 << LightsOnTrig) ) printf("%s ", "lightson"); if ( funcmap & (1 << LightsOffTrig) ) printf("%s ","lightsoff"); if ( funcmap & (1 << AllOnTrig) ) printf("%s ", "allon"); if ( funcmap & (1 << AllOffTrig) ) printf("%s ", "alloff"); if ( funcmap & (1 << PresetTrig) ) printf("%s ", "preset"); if ( funcmap & (1 << ExtendedTrig) ) printf("%s ", "extended"); if ( funcmap & (1 << StatusReqTrig) ) printf("%s ", "status"); if ( funcmap & (1 << StatusOnTrig) ) printf("%s ", "status_on"); if ( funcmap & (1 << StatusOffTrig) ) printf("%s ", "status_off"); if ( funcmap & (1 << HailReqTrig) ) printf("%s ", "hail"); if ( funcmap & (1 << HailAckTrig) ) printf("%s ", "hail_ack"); if ( funcmap & (1 << DataXferTrig) ) printf("%s ", "data_xfer"); if ( funcmap & (1 << ExtPowerUpTrig) ) printf("%s ", "xpowerup"); if ( funcmap & (1 << VdataTrig) ) printf("%s ", "vdata"); if ( funcmap & (1 << PanicTrig) ) printf("%s ", "panic"); if ( funcmap & (1 << ArmTrig) ) printf("%s ", "arm"); if ( funcmap & (1 << DisarmTrig) ) printf("%s ", "disarm"); if ( funcmap & (1 << AlertTrig) ) printf("%s ", "alert"); if ( funcmap & (1 << ClearTrig) ) printf("%s ", "clear"); if ( funcmap & (1 << SecLightsOnTrig) ) printf("%s ", "slightson"); if ( funcmap & (1 << SecLightsOffTrig) ) printf("%s ", "slightsoff"); printf("\n"); printf("%*s: ", lw, "Launch Mode"); if ( launcherp[j].type == L_ADDRESS ) sprintf(minibuf, "%s", "address"); else sprintf(minibuf, "%s%s%s", (chgtrig ? "changed " : ""), ((bmaptrig & signal) ? "signal " : "module "), ((launcherp[j].trigemuflag) ? "trigemu" : "") ); printf("%-20s", minibuf); printf("%s: ", "Flags"); display_launcher_flags(launcherp, j); printf("\n"); printf("%*s: %-18s", lw, "Script label", launcherp[j].label); printf("%s: ", "Sources"); if ( source & RCVI ) printf("rcvi "); if ( source & RCVT ) printf("rcvt "); if ( source & SNDC ) printf("sndc "); if ( source & SNDM ) printf("sndm "); if ( source & SNDS ) printf("snds "); if ( source & SNDT ) printf("sndt "); if ( source & SNDP ) printf("sndp "); if ( source & SNDA ) printf("snda "); if ( source & RCVA ) printf("rcva "); printf("\n"); printf("%*s: %s\n", lw, "Command Line", configp->scriptp[launcherp[j].scriptnum].cmdline); j++; } return; } void show_all_launchers ( void ) { static int hcode_table[16] = {6, 14, 2, 10, 1, 9, 5, 13, 7, 15, 3, 11, 0, 8, 4, 12}; int j; if ( configp->launcherp == NULL ) { return; } for ( j = 0; j < 16; j++ ) show_launcher(hcode_table[j]); show_powerfail_launcher(); show_sensorfail_launcher(); show_rfflood_launcher(); show_timeout_launcher(); return; } /*---------------------------------------------------------------------+ | Display the sticky address state for all housecodes and units. | +---------------------------------------------------------------------*/ void show_sticky_addr ( void ) { unsigned char hcode; char hc; int j, unit; char outbuf[17]; char label[16]; char *chrs = "*."; int lw = 13; printf("Cumulative received addresses\n"); printf("%*s 1..4...8.......16\n", lw, "Unit:"); for ( hc = 'A'; hc <= 'P'; hc++ ) { hcode = hc2code(hc); for ( j = 0; j < 16; j++ ) { unit = code2unit(j) - 1; if ( x10state[hcode].sticky & (1 << j) ) { outbuf[unit] = chrs[0]; } else outbuf[unit] = chrs[1]; } sprintf(label, "Housecode %c", hc); outbuf[16] = '\0'; printf("%*s (%s)\n", lw, label, outbuf); } return; } /*---------------------------------------------------------------------+ | Display the On/Off/Dimmed state for all housecodes and units. | +---------------------------------------------------------------------*/ void show_housemap ( void ) { unsigned char hcode; char hc; int j, unit; char outbuf[17]; char label[16]; char *chrs = "*x."; int lw = 13; printf("%*s %s\n", lw, "", "* = On x = Dimmed"); printf("%*s 1..4...8.......16\n", lw, "Unit:"); for ( hc = 'A'; hc <= 'P'; hc++ ) { hcode = hc2code(hc); for ( j = 0; j < 16; j++ ) { unit = code2unit(j) - 1; if ( x10state[hcode].state[DimState] & (1 << j) ) { outbuf[unit] = chrs[1]; } else if ( x10state[hcode].state[OnState] & (1 << j) ) { outbuf[unit] = chrs[0]; } else outbuf[unit] = chrs[2]; } sprintf(label, "Housecode %c", hc); outbuf[16] = '\0'; printf("%*s (%s)\n", lw, label, outbuf); } return; } /*---------------------------------------------------------------------+ | Display the flag states, including night and dark flags | +---------------------------------------------------------------------*/ void show_flags ( void ) { int j, lw = 13; char *chrs = "01-"; char outbuf[64]; int offset = 0; for ( j = 0; j < (int)sizeof(outbuf); j++ ) outbuf[j] = ' '; printf("%*s %s\n\n", lw, "Flag states", "0 = Clear 1 = Set"); printf("%*s 1..4...8.......16 night dark\n", lw, "Flag:"); outbuf[offset++] = '('; for ( j = 0; j < 16; j++ ) { if ( x10global.flags & (1 << j) ) outbuf[offset] = chrs[1]; else outbuf[offset] = chrs[0]; offset++; } outbuf[offset++] = ')'; for ( j = 16; j < 18; j++ ) { offset += 6; if ( x10global.flags & (1 << j) ) outbuf[offset] = x10global.dawndusk_enable ? chrs[1] : chrs[2]; else outbuf[offset] = x10global.dawndusk_enable ? chrs[0] : chrs[2]; } outbuf[++offset] = '\0'; printf("%*s %s\n\n", lw, "", outbuf); return; } /*---------------------------------------------------------------------+ | Function to delete the X10state file. | +---------------------------------------------------------------------*/ void remove_x10state_file ( void ) { int code; code = remove( statefile ) ; if ( code != 0 && errno != 2 ) { (void)fprintf(stderr, "WARNING: Unable to delete X10 State File %s - errno = %d\n", statefile, errno); } return; } /*---------------------------------------------------------------------+ | Function to write the x10state structure to the X10state file. | +---------------------------------------------------------------------*/ void write_x10state_file ( void ) { FILE *fd; if ( verbose ) fprintf(stderr, "Writing state file\n"); if ( !(fd = fopen(statefile, "w")) ) { fprintf(stderr, "Unable to open X10 State File '%s' for writing.\n", statefile); exit(1); } x10global.filestamp = time(NULL); fwrite((void *)(&x10global), 1, (sizeof(x10global)), fd); fclose(fd); return; } /*---------------------------------------------------------------------+ | Get the last-modified time of the x10state file. | +---------------------------------------------------------------------*/ long int modtime_x10state_file ( void ) { struct stat statbuf; int retcode; retcode = stat(statefile, &statbuf); if ( retcode == 0 ) return statbuf.st_mtime; return (long)retcode; } /*---------------------------------------------------------------------+ | Verify that the state engine is running by checking for its lock | | file. | +---------------------------------------------------------------------*/ int check_for_engine ( void ) { struct stat statbuf; int retcode; retcode = stat(enginelockfile, &statbuf); return retcode; } /*---------------------------------------------------------------------+ | Function to read the x10state file and store the contents in the | | x10state structure. | +---------------------------------------------------------------------*/ int read_x10state_file (void ) { FILE *fd = NULL; /* Keep some compilers happy; ditto with nread */ int j, nread = 0; for ( j = 0; j < 3; j++ ) { if ( (fd = fopen(statefile, "r")) ) break; millisleep(100); } if ( !fd ) { return 1; } for ( j = 0; j < 3; j++ ) { nread = fread((void *)(&x10global), 1, (sizeof(x10global) + 1), fd ); if ( nread == (sizeof(x10global)) ) break; rewind(fd); millisleep(100); } if ( nread != (sizeof(x10global)) ) { return 2; } fclose(fd); return 0; } /*---------------------------------------------------------------------+ | Send command to heyu_engine process to write the x10 state file, | | then read it and store in the state structure. | +---------------------------------------------------------------------*/ int fetch_x10state ( void ) { struct stat statbuf; int j, retcode; time_t now; int retries = 20; if ( check_for_engine() != 0 ) { fprintf(stderr, "State engine is not running.\n"); return 1; } now = time(NULL); send_x10state_command(ST_WRITE, 0); for ( j = 0; j < retries; j++ ) { if ( (retcode = stat(statefile, &statbuf)) == 0 && read_x10state_file() == 0 && x10global.filestamp >= now ) { break; } millisleep(100); } if ( j >= retries ) { fprintf(stderr, "Unable to read current x10state - is state engine running?\n"); return 1; } return 0; } /*---------------------------------------------------------------------+ | Send command to heyu_engine process to write the x10 state file, | | then read it and store in the state structure. | +---------------------------------------------------------------------*/ int fetch_x10state_old ( void ) { struct stat statbuf; int j, retcode; time_t now; int retries = 50; if ( check_for_engine() != 0 ) { fprintf(stderr, "State engine is not running.\n"); return 1; } time(&now); send_x10state_command(ST_WRITE, 0); for ( j = 0; j < retries; j++ ) { if ( (retcode = stat(statefile, &statbuf)) == 0 && statbuf.st_mtime >= now ) { if ( read_x10state_file() == 0 ) break; } millisleep(100); } if ( j >= retries ) { fprintf(stderr, "Unable to read current x10state - is state engine running?\n"); return 1; } return 0; } /*---------------------------------------------------------------------+ | Identify the type of data read from the spoolfile which was written | | there by heyu, i.e., excluding data received from the interface. | +---------------------------------------------------------------------*/ int identify_sent ( unsigned char *buf, int len ) { int type; unsigned char chksum; chksum = checksum(buf, len); if ( buf[0] == 0 && len == 1 ) type = SENT_WRMI; /* WRMI */ else if ( buf[0] == 0x04 && len == 2 ) type = SENT_ADDR; /* Address */ else if ( (buf[0] & 0x07) == 0x06 && len == 2 ) type = SENT_FUNC; /* Standard Function */ else if ( buf[0] == 0x07 && len == 5 ) type = SENT_EXTFUNC; /* Extended function */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_XMITRF && len == 6 ) type = SENT_RF; /* CM17A command */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_MSG && len == (buf[2] + 3) ) type = SENT_MESSAGE; /* Display message */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_VDATA && len == 8 ) type = SENT_VDATA; /* Virtual data */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_LONGVDATA && len == 8 ) type = SENT_LONGVDATA; /* 4-byte Virtual data */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_FLAGS && len == 5 ) type = SENT_FLAGS; /* Update flag states */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_CLRSTATUS && len == 5 ) type = SENT_CLRSTATUS; /* Clear status flags */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_PFAIL && len == 3 ) type = SENT_PFAIL; /* Relay reports a powerfail update */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_RFFLOOD && len == 3 ) type = SENT_RFFLOOD; /* Aux reports an RF Flood */ else if ( buf[0] == ST_COMMAND && len == 3 ) type = SENT_STCMD; /* Monitor/state control command */ else if ( buf[0] == 0x0C && len == 2 && buf[1] == 0x56 ) type = SENT_ADDR; /* Address of G1 with 5a fix */ else if ( buf[0] == 0x0F && len == 5 && chksum == 0x62 ) type = SENT_EXTFUNC; /* Extended function with 5a fix */ else if ( buf[0] == ST_COMMAND && buf[1] == ST_SETTIMER && len == 7 ) type = SENT_SETTIMER; /* Set a countdown timer */ else type = SENT_OTHER; /* Other, i.e., info, setclock, upload, etc. */ return type; } void create_flagslist ( unsigned char vtype, unsigned int vflags, char *flagslist ) { if ( vtype == RF_SEC ) { if ( vflags & SEC_TAMPER ) strcat(flagslist, "Tamper "); if ( vflags & SEC_MAIN ) strcat(flagslist, "Main "); if ( vflags & SEC_AUX ) strcat(flagslist, "Aux "); if ( vflags & SEC_HOME ) strcat(flagslist, "swHome "); if ( vflags & SEC_AWAY ) strcat(flagslist, "swAway "); if ( vflags & SEC_MIN ) strcat(flagslist, "swMin "); if ( vflags & SEC_MAX ) strcat(flagslist, "swMax "); if ( vflags & SEC_LOBAT ) strcat(flagslist, "LoBat "); } return; } /*---------------------------------------------------------------------+ | Interpret Virtual data string, update the state, and test whether | | any launch condition is satisfied. | | buffer references: | | ST_COMMAND, ST_VDATA, addr, vdata, vtype, vident | | ST_COMMAND, ST_LONGVDATA, 0, byte2, vtype, byte1, byte4 | +---------------------------------------------------------------------*/ char *translate_virtual ( unsigned char *buf, int len, unsigned char *sunchanged, int *launchp ) { static char outbuf[80]; char flagslist[80]; char hc; int j, k, found, index = -1; unsigned char hcode, ucode, unit, func, vdata, vtype, vident, byte2, byte3; unsigned int bitmap, vflags = 0; /* unsigned long flood; */ ALIAS *aliasp; static char *typename[] = {"Std", "Ent", "Sec", "Noise"}; *launchp = -1; *sunchanged = 0; flagslist[0] = '\0'; aliasp = config.aliasp; vdata = buf[3]; vtype = buf[4]; vident = buf[5]; byte2 = buf[6]; byte3 = buf[7]; func = VdataFunc; x10global.longvdata = 0; x10global.lastvtype = vtype; if ( vtype == RF_STD ) { hc = code2hc((buf[2] & 0xf0u) >> 4); ucode = buf[2] & 0x0fu; bitmap = (1 << ucode); unit = code2unit(ucode); vdata = buf[3]; func = VdataFunc; index = alias_rev_index(hc, bitmap, vtype, vident); if ( x10state_update_virtual(buf + 2, len, launchp) != 0 ) return ""; sprintf(outbuf, "function %10s : housecode %c unit %d vdata 0x%02x (%s)", funclabel[func], hc, unit, vdata, lookup_label(hc, (1 << ucode))); return outbuf; } if ( vtype == RF_FLOOD ) { if ( vdata & SEC_FLOOD ) x10global.flags |= GLOBSEC_FLOOD; else x10global.flags &= ~GLOBSEC_FLOOD; sprintf(outbuf, " RF Flood : %s", ((vdata & SEC_FLOOD) ? "Started" : "Ended")); *launchp = find_rfflood_launcher(); return outbuf; } if ( vtype == RF_SEC || vtype == RF_ENT ) { hcode = ucode = 0; found = 0; j = 0; /* Look up the alias, if any */ while ( !found && aliasp && aliasp[j].line_no > 0 ) { if ( aliasp[j].vtype == vtype && (bitmap = aliasp[j].unitbmap) > 0 && (ucode = single_bmap_unit(bitmap)) != 0xff ) { for ( k = 0; k < aliasp[j].nident; k++ ) { if ( aliasp[j].ident[k] == vident ) { index = j; found = 1; break; } } } j++; } if ( !found || !aliasp ) { sprintf(outbuf, "function %10s : Type %s ID 0x%02X Data 0x%02X", "RFdata", typename[vtype], vident, vdata); return outbuf; } x10global.longvdata = (vident << 8) | vdata; hc = aliasp[index].housecode; hcode = hc2code(hc); unit = code2unit(ucode); /* Add address to buf and update the state */ buf[2] = (hcode & 0x0fu) << 4 | (ucode & 0x0fu); if ( x10state_update_virtual(buf + 2, len, launchp) != 0 ) return ""; func = x10state[hcode].lastcmd; vflags = x10state[hcode].vflags[ucode]; create_flagslist(vtype, vflags, flagslist); /* Indicate whether no change in state for Sensor module */ if ( (aliasp[index].optflags & MOPT_SENSOR) && (x10state[hcode].state[ChgState] & (1 << ucode)) == 0 ) { *sunchanged = 1; } /* Update activity timeout for security sensor */ if ( aliasp[index].optflags & MOPT_SENSOR ) aliasp[index].timeout = configp->inactive_timeout; if ( vtype == RF_SEC ) { sprintf(outbuf, "function %10s : housecode %c unit %d %s (%s)", funclabel[func], hc, unit, flagslist, aliasp[index].label); } else if ( vtype == RF_ENT && aliasp[index].nident == 1 ) { sprintf(outbuf, "function %10s : housecode %c unit %d data 0x%02X (%s)", funclabel[func], hc, unit, vdata, aliasp[index].label); } else if ( vtype == RF_ENT ) { sprintf(outbuf, "function %10s : housecode %c unit %d lvdata 0x%04lX (%s)", funclabel[func], hc, unit, x10global.longvdata, aliasp[index].label); } else { sprintf(outbuf, "function %10s : housecode %c unit %d data 0x%02X (%s)", funclabel[func], hc, unit, vdata, aliasp[index].label); } } else if ( vtype == RF_RAWW800 ) { x10global.longvdata = (buf[3] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; sprintf(outbuf, "function %10s : Data (hex) %02X %02X %02X %02X", "RFrawdata", buf[3], buf[5], buf[6], buf[7]); } else if ( vtype == RF_NOISEW800 ) { x10global.longvdata = (buf[3] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; sprintf(outbuf, "function %10s : Data (hex) %02X %02X %02X %02X", "RFnoise", buf[3], buf[5], buf[6], buf[7]); } else if ( vtype == RF_RAWMR26 ) { x10global.longvdata = (buf[2] << 8) | buf[3]; sprintf(outbuf, "function %10s : Data (hex) %02X %02X %02X %02X %02X", "RFrawdata", buf[2], buf[3], buf[5], buf[6], buf[7]); } else if ( vtype == RF_NOISEMR26 ) { x10global.longvdata = (buf[2] << 8) | buf[3]; sprintf(outbuf, "function %10s : Data (hex) %02X %02X %02X %02X %02X", "RFnoise", buf[2], buf[3], buf[5], buf[6], buf[7]); } else { x10global.longvdata = (buf[3] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; sprintf(outbuf, "function %10s : Type 0x%02x Data (hex) %02x %02x %02x %02x", "RFdata", vtype, buf[3], buf[5], buf[6], buf[7]); } return outbuf; } /*---------------------------------------------------------------------+ | Interpret byte string sent to CM11A, update the state, and test | | whether any launch condition is satisfied. | +---------------------------------------------------------------------*/ char *translate_sent ( unsigned char *buf, int len, int *launchp ) { static char outbuf[80]; char hc; unsigned char hcode, func, level, unit, subunit, chksum; unsigned char xgroup, xfunc, xsubfunc, xtype, xdata; unsigned char newbuf[2]; unsigned int bmap, memloc; int dims; double ddims; char *prefix, *suffix; *launchp = -1; chksum = checksum(buf, len); if ( buf[0] == 0 && len == 1 ) { return ""; } else if ( (buf[0] == 0x04 && len == 2) || (buf[0] == 0x0C && len == 2 && buf[1] == 0x56 ) ) { /* Address */ hcode = (buf[1] & 0xf0u) >> 4; hc = code2hc(hcode); unit = code2unit(buf[1] & 0x0fu); bmap = 1 << (buf[1] & 0x0fu); x10state_update_addr(buf[1], launchp); sprintf(outbuf, "address unit %3d : housecode %c (%s)", unit, hc, lookup_label(hc, bmap)); return outbuf; } else if ( (buf[0] & 0x07) == 0x06 && len == 2 ) { /* Standard function */ level = (buf[0] & 0xf8u) >> 3; dims = level2dims(level, &prefix, &suffix); ddims = 100. * (double)dims / 210.; hcode = (buf[1] & 0xf0u) >> 4; hc = code2hc(hcode); func = buf[1] & 0x0f; if ( level > 0 && func != 4 && func != 5 ) { sprintf(outbuf, "Unknown transmission: %02x %02x", buf[0], buf[1]); return outbuf; } newbuf[0] = buf[1]; newbuf[1] = dims; x10state_update_func(newbuf, launchp); switch ( func ) { case 4 : /* Dim */ sprintf(outbuf, "function %10s : housecode %c, dimmed by %%%02.0f [%s%d%s]", funclabel[func], hc, ddims, prefix, dims, suffix); return outbuf; case 5 : /* Bright */ sprintf(outbuf, "function %10s : housecode %c, brighten %%%02.0f [%s%d%s]", funclabel[func], hc, ddims, prefix, dims, suffix); return outbuf; case 10 : /* Preset level */ case 11 : /* Preset level */ level = rev_low_nybble(hcode) + 1; level += (func == 11) ? 16 : 0; sprintf(outbuf, "function %10s : level %d", funclabel[func], level); return outbuf; default: sprintf(outbuf, "function %10s : housecode %c", funclabel[func], hc); } } else if ( (buf[0] == 0x07 && len == 5) || (buf[0] == 0x0F && len == 5 && chksum == 0x62) ) { /* Extended code function */ char stmp[16]; if ( (buf[1] & 0x0fu) != 0x07 ) { sprintf(outbuf, "Unknown transmission: %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4]); return outbuf; } hcode = (buf[1] & 0xf0u) >> 4; hc = code2hc(hcode); unit = code2unit(buf[2] & 0x0fu); subunit = (buf[2] & 0xf0u) >> 4; bmap = 1 << (buf[2] & 0x0fu); xfunc = buf[4]; xtype = (xfunc & 0xf0u) >> 4; xdata = buf[3]; if ( xtype == 3 ) { x10state_update_ext3func(buf + 1, launchp); } #ifdef HASEXT0 else if ( xtype == 0 ) { x10state_update_ext0func(buf + 1, launchp); } #endif else { x10state_update_extotherfunc(buf + 1, launchp); } stmp[0] = '\0'; if ( xfunc == 0x30 ) { if ( (xdata & 0x30u) == 0x20 ) sprintf(outbuf, "function xGrpAdd : housecode %c unit %d%s group %d.%-2d (%s)", hc, unit, stmp, (xdata >> 6), (xdata & 0x0fu) + 1, lookup_label(hc, bmap)); else sprintf(outbuf, "function xGrpAdd : housecode %c unit %d%s group %d (%s)", hc, unit, stmp, (xdata >> 6), lookup_label(hc, bmap)); } else if ( xfunc == 0x31 ) { if ( xdata & 0xc0u ) { sprintf(outbuf, "function xPreset : housecode %c unit %d%s level %d ramp %d (%s)", hc, unit, stmp, xdata & 0x3fu, (xdata & 0xc0u) >> 6, lookup_label(hc, bmap)); } else { sprintf(outbuf, "function xPreset : housecode %c unit %d%s level %d (%s)", hc, unit, stmp, xdata, lookup_label(hc, bmap)); } } else if ( xfunc == 0x32 ) sprintf(outbuf, "function xGrpAddLvl : housecode %c unit %d%s group %d level %d (%s)", hc, unit, stmp, ((xdata & 0xc0) >> 6), (xdata & 0x3f), lookup_label(hc, bmap)); else if ( xfunc == 0x33 ) sprintf(outbuf, "function xAllOn : housecode %c", hc); else if ( xfunc == 0x34 ) sprintf(outbuf, "function xAllOff : housecode %c", hc); else if ( xfunc == 0x35 ) { if ( (xdata & 0x30) == 0 ) { sprintf(outbuf, "function xGrpRem : housecode %c unit %d%s group(s) %s (%s)", hc, unit, stmp, linmap2list(xdata & 0x0f), lookup_label(hc, bmap)); } else { sprintf(outbuf, "function xGrpRemAll : housecode %c group(s) %s", hc, linmap2list(xdata & 0x0f)); } } else if ( xfunc == 0x36 ) { xsubfunc = (xdata & 0x30u) >> 4; xgroup = (xdata & 0xc0u) >> 6; if ( xsubfunc == 0 ) sprintf(outbuf, "function xGrpExec : housecode %c group %d", hc, xgroup); else if ( xsubfunc == 2 ) sprintf(outbuf, "function xGrpExec : housecode %c group %d.%-2d", hc, xgroup, (xdata & 0x0fu) + 1); else if ( xsubfunc == 1 ) sprintf(outbuf, "function xGrpOff : housecode %c group %d", hc, xgroup); else sprintf(outbuf, "function xGrpOff : housecode %c group %d.%-2d", hc, xgroup, (xdata & 0x0fu) + 1); } else if ( xfunc == 0x37 ) { xsubfunc = (xdata & 0x30u) >> 4; xgroup = (xdata & 0xc0u) >> 6; if ( xsubfunc == 0 ) sprintf(outbuf, "function xStatusReq : housecode %c unit %d%s (%s)", hc, unit, stmp, lookup_label(hc, bmap)); else if ( xsubfunc == 1 ) sprintf(outbuf, "function xPowerUp : housecode %c unit %d%s (%s)", hc, unit, stmp, lookup_label(hc, bmap)); else if ( xsubfunc == 2 ) sprintf(outbuf, "function xGrpStatus : housecode %c unit %d%s group %d (%s)", hc, unit, stmp, (xdata & 0xc0) >> 6, lookup_label(hc, bmap)); else sprintf(outbuf, "function xGrpStatus : housecode %c unit %d%s group %d.%-2d (%s)", hc, unit, stmp, xgroup, (xdata & 0x0fu) + 1, lookup_label(hc, bmap)); } else if ( xfunc == 0x38 ) { if ( xdata & 0x40 ) sprintf(outbuf, "function xStatusAck : Switch %c%d%s is %-3s %s (%s)", hc, unit, stmp, ((xdata & 0x3f) ? "On " : "Off"), ((xdata & 0x80) ? "LoadOK" : "NoLoad"), lookup_label(hc, bmap)); else sprintf(outbuf, "function xStatusAck : Lamp %c%d%s at level %d, %s (%s)", hc, unit, stmp, xdata & 0x3f, ((xdata & 0x80) ? "BulbOK" : "NoBulb"), lookup_label(hc, bmap)); } else if ( xfunc == 0x3b ) { sprintf(outbuf, "function xConfig : housecode %c mode=%d", hc, xdata); } else if ( xfunc == 0x3C ) { /* Unsupported by X-10 modules */ xsubfunc = (xdata & 0x30u) >> 4; xgroup = (xdata & 0xc0u) >> 6; if ( xsubfunc == 0 ) sprintf(outbuf, "function xGrpDim : housecode %c group %d", hc, xgroup); else if ( xsubfunc == 2 ) sprintf(outbuf, "function xGrpDim : housecode %c group %d.%-2d", hc, xgroup, (xdata & 0x0fu) + 1); else if ( xsubfunc == 1 ) sprintf(outbuf, "function xGrpBright : housecode %c group %d", hc, xgroup); else sprintf(outbuf, "function xGrpBright : housecode %c group %d.%-2d", hc, xgroup, (xdata & 0x0fu) + 1); } #ifdef HASEXT0 else if ( xfunc == 0x01 ) { sprintf(outbuf, "function shOpenLim : housecode %c unit %d%s level %d (%s)", hc, unit, stmp, xdata & 0x1f, lookup_label(hc, bmap)); } else if ( xfunc == 0x03 ) { sprintf(outbuf, "function shOpen : housecode %c unit %d%s level %d (%s)", hc, unit, stmp, xdata & 0x1f, lookup_label(hc, bmap)); } else if ( xfunc == 0x02 ) { sprintf(outbuf, "function shSetLim : housecode %c unit %d%s level %d (%s)", hc, unit, stmp, xdata & 0x1f, lookup_label(hc, bmap)); } else if ( xfunc == 0x04 ) { sprintf(outbuf, "function shOpenAll : housecode %c", hc); } else if ( xfunc == 0x0B ) { sprintf(outbuf, "function shCloseAll : housecode %c", hc); } #endif /* HASEXT0 block */ else if ( xfunc == 0xff ) sprintf(outbuf, "function ExtCode : Incomplete xcode in buffer."); else { sprintf(outbuf, "function xFunc %02x : housecode %c unit %d%s data=0x%02x (%s)", xfunc, hc, unit, stmp, xdata, lookup_label(hc, bmap)); } return outbuf; } else if ( buf[0] == 0xfb && len == 19 ) { chksum = checksum(buf + 1, len - 1); memloc = (buf[1] << 8) + buf[2]; sprintf(outbuf, "Upload to EEPROM location %03x, checksum = %02x", memloc, chksum); return outbuf; } else if ( buf[0] == 0x8b && len == 1 ) { sprintf(outbuf, "Request CM11A info"); return outbuf; } else if ( buf[0] == 0x9b && len == 7 ) { sprintf(outbuf, "Update CM11A info"); return outbuf; } else if ( buf[0] == 0xeb && len == 1 ) { *outbuf = '\0'; /* sprintf(outbuf, "Enable serial ring signal"); */ return outbuf; } else if ( buf[0] == 0xdb && len == 1 ) { *outbuf = '\0'; /* sprintf(outbuf, "Disable serial ring signal"); */ return outbuf; } else if ( buf[0] == ST_COMMAND && len == 3 ) { sprintf(outbuf, "State command"); return outbuf; } else if ( buf[0] == 0xfb && len == 43 && configp->device_type & DEV_CM10A ) { sprintf(outbuf, "Initialize CM10A"); return outbuf; } else { sprintf(outbuf, "Unknown transmission: %d bytes, 1st byte = %02x", len, buf[0]); } return outbuf; } /*---------------------------------------------------------------------+ | Interpret bytes sent to CM17A | +---------------------------------------------------------------------*/ char *translate_rf_sent ( unsigned char *buf ) { extern void xlate_rf( unsigned char, char **, unsigned int, char *, int *, unsigned char * ); static char outbuf[80]; char verb[16]; unsigned char type, bursts, nosw; char hc; unsigned int rfword; char *fname, *vp, *nsw; int unit; type = buf[2]; rfword = buf[3] << 8 | buf[4]; bursts = buf[5]; xlate_rf( type, &fname, rfword, &hc, &unit, &nosw ); if ( configp->disp_rf_xmit == VERBOSE ) { sprintf(verb, " [%02x %02x %d]", buf[3], buf[4], buf[5]); vp = verb; } else { vp = ""; } nsw = ( nosw && (type == 0 || unit == 1 || unit == 9) ) ? " NoSw" : ""; if ( type == 9 ) sprintf(outbuf, "RF func fArb : bytes 0x%02X 0x%02X count %d%s", buf[3], buf[4], buf[5], vp); else if ( type == 10 ) sprintf(outbuf, "RF func fArw : word 0x%04X count %d%s", ((buf[3] << 8) | buf[4]), buf[5], vp); else if ( type == 11 ) sprintf(outbuf, "RF func fLux : word 0x%04X count %d%s", ((buf[3] << 8) | buf[4]), buf[5], vp); else if ( type == 2 || type == 3 ) sprintf(outbuf, "RF func %11s : housecode %c unit %d%s%s", fname, hc, unit, nsw, vp); else if ( type == 4 || type == 5 ) sprintf(outbuf, "RF func %11s : housecode %c count %d%s", fname, hc, bursts, vp); else sprintf(outbuf, "RF func %11s : housecode %c%s%s", fname, hc, nsw, vp); return outbuf; } /*---------------------------------------------------------------------+ | Interpret byte string sent to CM11A. | +---------------------------------------------------------------------*/ char *translate_other ( unsigned char *buf, int len ) { static char outbuf[80]; unsigned char chksum; unsigned int memloc; if ( buf[0] == 0 && len == 1 ) { return ""; } else if ( buf[0] == 0xfb && len == 19 ) { chksum = checksum(buf + 1, len - 1); memloc = (buf[1] << 8) + buf[2]; sprintf(outbuf, "Upload to EEPROM location %03x, checksum = %02x", memloc, chksum); return outbuf; } else if ( buf[0] == 0x8b && len == 1 ) { sprintf(outbuf, "Request CM11A info"); return outbuf; } else if ( buf[0] == 0x9b && len == 7 ) { sprintf(outbuf, "Update CM11A info"); return outbuf; } else if ( buf[0] == 0xeb && len == 1 ) { *outbuf = '\0'; /* sprintf(outbuf, "Enable serial ring signal"); */ return outbuf; } else if ( buf[0] == 0xdb && len == 1 ) { *outbuf = '\0'; /* sprintf(outbuf, "Disable serial ring signal"); */ return outbuf; } else if ( buf[0] == ST_COMMAND && len == 3 ) { sprintf(outbuf, "State command"); return outbuf; } else if ( buf[0] == 0xfb && len == 43 && configp->device_type & DEV_CM10A ) { sprintf(outbuf, "Initialize CM10A"); return outbuf; } else { sprintf(outbuf, "Unknown transmission: %d bytes, 1st byte = %02x", len, buf[0]); } return outbuf; } /*---------------------------------------------------------------------+ | Create argument list string for heyuhelper. | | If argument big = 0, a token for only the last unit is included, | | otherwise tokens are include for each unit in the launcher bitmap. | +---------------------------------------------------------------------*/ char *helper_string ( int index, int big ) { int ucode; unsigned int bitmap, mask; unsigned char actfunc; char hc; LAUNCHER *launcherp; char buffer[256]; char minibuf[32]; char *dup; launcherp = configp->launcherp; bitmap = launcherp[index].bmaplaunch; actfunc = launcherp[index].actfunc; hc = tolower((int)code2hc(launcherp[index].hcode)); strncpy2(buffer, ((big) ? "bighelper=" : "helper="), sizeof(buffer) - 1); for ( ucode = 15; ucode >= 0; ucode-- ) { mask = 1 << ucode; if ( bitmap & mask ) { sprintf(minibuf, "%c%d%s ", hc, code2unit(ucode), funclabel[actfunc]); strncat(buffer, minibuf, sizeof(buffer) - 1 - strlen(buffer)); if ( !big ) break; } } if ( (dup = strdup(buffer)) == NULL ) { fprintf(stderr, "Out of memory in helper_string()\n"); exit(1); } return dup; } /*---------------------------------------------------------------------+ | Return the heyu state in the same bitmap format as is put into the | | environment when a script is launched. | +---------------------------------------------------------------------*/ int get_heyu_state ( unsigned char hcode, unsigned char ucode ) { unsigned int bitmap, value; int level; bitmap = (1 << ucode); /* Start with the dim level (0-100) */ level = x10state[hcode].dimlevel[ucode]; if ( modmask[Ext3Mask][hcode] & bitmap ) value = ext3level2pct(level); else if ( modmask[Ext0Mask][hcode] & bitmap ) value = ext0level2pct(level); else if ( modmask[PresetMask][hcode] & bitmap ) value = presetlevel2pct(level); else if ( modmask[StdMask][hcode] & bitmap ) value = dims2pct(level); else value = 100 * level / 255; /* Add the state bits */ if ( bitmap & ~(modmask[DimMask][hcode] | modmask[BriMask][hcode]) ) value |= HEYUMAP_APPL; if ( bitmap & ~x10state[hcode].state[OnState] ) value |= HEYUMAP_OFF; if ( bitmap & x10state[hcode].addressed ) value |= HEYUMAP_ADDR; if ( bitmap & x10state[hcode].state[ChgState] ) value |= HEYUMAP_CHG; if ( bitmap & x10state[hcode].state[DimState] ) value |= HEYUMAP_DIM; if ( bitmap & x10state[hcode].state[OnState] ) value |= HEYUMAP_ON; return value; } /*---------------------------------------------------------------------+ | Return the heyu state in the same bitmap format as is put into the | | environment when a script is launched with "-rawlevel" option. | +---------------------------------------------------------------------*/ int get_heyu_rawstate ( unsigned char hcode, unsigned char ucode ) { unsigned int bitmap, value; int level; bitmap = (1 << ucode); /* Start with the raw dim level */ level = x10state[hcode].dimlevel[ucode]; if ( modmask[PresetMask][hcode] & bitmap ) value = level + 1; else value = level; /* Add the state bits */ if ( bitmap & ~(modmask[DimMask][hcode] | modmask[BriMask][hcode]) ) value |= HEYUMAP_APPL; if ( bitmap & ~x10state[hcode].state[OnState] ) value |= HEYUMAP_OFF; if ( bitmap & x10state[hcode].addressed ) value |= HEYUMAP_ADDR; if ( bitmap & x10state[hcode].state[ChgState] ) value |= HEYUMAP_CHG; if ( bitmap & x10state[hcode].state[DimState] ) value |= HEYUMAP_DIM; if ( bitmap & x10state[hcode].state[OnState] ) value |= HEYUMAP_ON; return value; } /*---------------------------------------------------------------------+ | Return the xtend state in the same bitmap format as is put into the | | environment when a script is launched. | +---------------------------------------------------------------------*/ int get_xtend_state ( unsigned char hcode, unsigned char ucode ) { unsigned int bitmap, value; bitmap = (1 << ucode); value = 0; /* Add the state bits */ if ( bitmap & ~(modmask[DimMask][hcode] | modmask[BriMask][hcode]) ) value |= XTMAP_APPL; if ( bitmap & x10state[hcode].addressed ) value |= XTMAP_ADDR; if ( bitmap & x10state[hcode].state[OnState] ) value |= XTMAP_ON; return value; } /*---------------------------------------------------------------------+ | Allocate memory for argument envstr. | +---------------------------------------------------------------------*/ char *add_envptr ( char *envstr ) { char *dup; char *out_of_memory = "add_envptr() : Out of memory\n"; if ( (dup = strdup(envstr)) == NULL ) { fprintf(stderr, out_of_memory); exit(1); } return dup; } /*---------------------------------------------------------------------+ | Create a minimal environment for the -noenv option | +---------------------------------------------------------------------*/ char **create_noenv_environment ( int index, unsigned char daemon ) { extern char **environ; int j, size, newsize; char **envp, **ep; char minibuf[512]; static int sizchrptr = sizeof(char *); LAUNCHER *launcherp; launcherp = configp->launcherp; if ( daemon == D_RELAY || (index >= 0 && launcherp && launcherp[index].type == L_POWERFAIL) ) putenv("HEYU_PARENT=RELAY"); else putenv("HEYU_PARENT=ENGINE"); /* Get length of original environment */ size = 0; while ( environ[size] != NULL ) size++; newsize = size + 50; if ( (envp = calloc(newsize, sizchrptr)) == NULL ) { fprintf(stderr, "create_noenv__environment() : out_of_memory\n"); exit(1); } ep = envp; *ep++ = add_envptr("IAMNOENV=1"); /* Put config file pathname in environment for child processes */ sprintf(minibuf, "X10CONFIG=%s", pathspec(heyu_config)); *ep++ = add_envptr(minibuf); /* Append the user's original environment */ for ( j = 0; j < size; j++ ) { *ep++ = add_envptr(environ[j]); } /* Add NULL terminator */ *ep++ = NULL; return envp; } /*---------------------------------------------------------------------+ | Create the environment for heyu scripts. | +---------------------------------------------------------------------*/ char **create_heyu_environment( int index, unsigned char option) { extern char **environ; LAUNCHER *launcherp; int j, size, newsize, level, aliasindex; unsigned char hcode, ucode, actfunc; char **envp, **ep; char hc; unsigned int value, bitmap, addressed, onstate, appliance; unsigned int dimstate, chgstate, spendstate; long int dawn, dusk, systime; int expire; char *aliaslabel; char minibuf[512]; static int sizchrptr = sizeof(char *); time_t now; struct tm *tms; extern unsigned char bootflag; static struct { char *query; unsigned int mask; } maskval[] = { {"whatLevel", HEYUMAP_LEVEL}, {"isAppl", HEYUMAP_APPL }, {"isSpend", HEYUMAP_SPEND}, {"isOff", HEYUMAP_OFF }, {"isAddr", HEYUMAP_ADDR }, {"isChg", HEYUMAP_CHG }, {"isDim", HEYUMAP_DIM }, {"isOn", HEYUMAP_ON }, { NULL, 0 }, }; launcherp = configp->launcherp; if ( launcherp[index].type != L_POWERFAIL ) putenv("HEYU_PARENT=ENGINE"); else putenv("HEYU_PARENT=RELAY"); /* Get length of original environment */ size = 0; while ( environ[size] != NULL ) size++; newsize = size + 256 + 16 + 60 + ((option & ENV_ALIAS) ? alias_count() : 0); if ( (envp = calloc(newsize, sizchrptr)) == NULL ) { fprintf(stderr, "create_heyu_environment() : out of memory\n"); exit(1); } ep = envp; *ep++ = add_envptr("IAMHEYU=1"); /* Put config file pathname in environment for child processes */ sprintf(minibuf, "X10CONFIG=%s", pathspec(heyu_config)); *ep++ = add_envptr(minibuf); /* Create the various masks */ j = 0; while ( maskval[j].query ) { sprintf(minibuf, "%s=%d", maskval[j].query, maskval[j].mask); *ep++ = add_envptr(minibuf); j++; } hcode = launcherp[index].hcode; actfunc = launcherp[index].actfunc; /* Variables for scripts launched by a "normal" launcher, */ /* i.e., not a powerfail or address launcher. */ if ( launcherp[index].type == L_NORMAL ) { /* Put in the variables 'helper' and 'bighelper' */ /* with arguments for heyuhelper */ *ep++ = helper_string(index, 0); *ep++ = helper_string(index, 1); } /* Variables for scripts launched by normal or address launchers */ if ( launcherp[index].type == L_NORMAL || launcherp[index].type == L_ADDRESS ) { if ( launcherp[index].type == L_NORMAL ) sprintf(minibuf, "X10_Function=%s", funclabel[actfunc]); else sprintf(minibuf, "X10_Function=none"); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Housecode=%c", code2hc(hcode)); *ep++ = add_envptr(minibuf); for ( ucode = 0; ucode < 16; ucode++ ) { if ( launcherp[index].bmaplaunch & (1 << ucode) ) break; } sprintf(minibuf, "X10_Unit=%d", code2unit(ucode)); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Timestamp=%ld", (unsigned long)x10state[hcode].timestamp[ucode]); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Vident=%d", x10state[hcode].vident[ucode]); *ep++ = add_envptr(minibuf); /* Security functions local switch flags */ if ( actfunc == ArmFunc ) { sprintf(minibuf, "X10_swHome=%d", ((x10state[hcode].vflags[ucode] & SEC_HOME) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_swAway=%d", ((x10state[hcode].vflags[ucode] & SEC_AWAY) ? 1 : 0) ); *ep++ = add_envptr(minibuf); } if ( actfunc == ArmFunc || actfunc == AlertFunc || actfunc == ClearFunc ) { sprintf(minibuf, "X10_swMin=%d", ((x10state[hcode].vflags[ucode] & SEC_MIN) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_swMax=%d", ((x10state[hcode].vflags[ucode] & SEC_MAX) ? 1 : 0) ); *ep++ = add_envptr(minibuf); } if ( actfunc == AlertFunc || actfunc == ClearFunc ) { sprintf(minibuf, "X10_LoBat=%d", ((x10state[hcode].vflags[ucode] & SEC_LOBAT) ? 1 : 0) ); *ep++ = add_envptr(minibuf); } sprintf(minibuf, "X10_LastUnit=%d", x10state[hcode].lastunit); *ep++ = add_envptr(minibuf); if ( actfunc == DimFunc || actfunc == BrightFunc ) { sprintf(minibuf, "X10_RawVal=%d", launcherp[index].rawdim); *ep++ = add_envptr(minibuf); if ( actfunc == 4 ) { sprintf(minibuf, "X10_DimVal=%d", launcherp[index].rawdim); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_BrightVal=-%d", launcherp[index].rawdim); *ep++ = add_envptr(minibuf); } else { sprintf(minibuf, "X10_DimVal=-%d", launcherp[index].rawdim); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_BrightVal=%d", launcherp[index].rawdim); *ep++ = add_envptr(minibuf); } } if ( actfunc == Preset1Func || actfunc == Preset2Func ) { sprintf(minibuf, "X10_PresetLevel=%d", launcherp[index].level); *ep++ = add_envptr(minibuf); } if ( actfunc == ExtendedFunc ) { sprintf(minibuf, "X10_Xfunc=%d", launcherp[index].xfunc); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Xdata=%d", launcherp[index].level); *ep++ = add_envptr(minibuf); } if ( actfunc == VdataFunc ) { sprintf(minibuf, "X10_Vdata=%d", launcherp[index].level); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Lvdata=%ld", x10global.longvdata); *ep++ = add_envptr(minibuf); } } if ( launcherp[index].type == L_POWERFAIL ) { sprintf(minibuf, "X10_Boot=%d", ((bootflag & R_ATSTART) ? 1 : 0) ); *ep++ = add_envptr(minibuf); } else if ( launcherp[index].type == L_TIMEOUT ) { sprintf(minibuf, "X10_Timer=%d", launcherp[index].timer); *ep++ = add_envptr(minibuf); } else if ( launcherp[index].type == L_SENSORFAIL && config.aliasp != NULL ) { aliasindex = launcherp[index].sensor; sprintf(minibuf, "X10_Sensor=%c%s", config.aliasp[aliasindex].housecode, bmap2units(config.aliasp[aliasindex].unitbmap)); *ep++ = add_envptr(minibuf); } /* The following are valid for all launchers */ /* Put in the flag variables */ for ( j = 0; j < 16; j++ ) { sprintf(minibuf, "X10_Flag%d=%d", j+1, ((x10global.flags & (1 << j)) ? 1 : 0) ); *ep++ = add_envptr(minibuf); } /* Global Security flags */ sprintf(minibuf, "X10_Tamper=%d", ((x10global.flags & GLOBSEC_TAMPER) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Armed=%d", ((x10global.flags & GLOBSEC_ARMED) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_ArmPending=%d", ((x10global.flags & GLOBSEC_PENDING) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Disarmed=%d", ( !(x10global.flags & GLOBSEC_ARMED) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Home=%d", ((x10global.flags & GLOBSEC_HOME) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_Away=%d", (((x10global.flags & GLOBSEC_ARMED) && !(x10global.flags & GLOBSEC_HOME)) ? 1 : 0) ); *ep++ = add_envptr(minibuf); sprintf(minibuf, "X10_SecLights=%d", ((x10global.flags & GLOBSEC_SLIGHTS) ? 1 : 0) ); *ep++ = add_envptr(minibuf); /* Put in the variables X10_DawnTime, X10_DuskTime, and X10_SysTime */ if ( dawndusk_today(&dawn, &dusk) == 0 ) { sprintf(minibuf, "%s=%ld", "X10_DawnTime", dawn); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%ld", "X10_DuskTime", dusk); *ep++ = add_envptr(minibuf); } /* Add Clock/Calendar variables */ time(&now); tms = localtime(&now); systime = 3600L * (long)tms->tm_hour + 60L * (long)tms->tm_min + (long)tms->tm_sec; sprintf(minibuf, "%s=%ld", "X10_SysTime", systime); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_Year", tms->tm_year + 1900); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_Month", tms->tm_mon + 1); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%s", "X10_MonthName", month_name[tms->tm_mon]); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_Day", tms->tm_mday); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_WeekDay", tms->tm_wday); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%s", "X10_WeekDayName", wday_name[tms->tm_wday]); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_Hour", tms->tm_hour); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_Minute", tms->tm_min); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_Second", tms->tm_sec); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%d", "X10_isDST", ((tms->tm_isdst > 0) ? 1 : 0)); *ep++ = add_envptr(minibuf); sprintf(minibuf, "%s=%s", "X10_DateString", datstrf()); strtrim(minibuf); *ep++ = add_envptr(minibuf); if ( x10global.dawndusk_enable ) { /* Logical variable is true between dusk and dawn */ sprintf(minibuf, "%s=%d", "X10_isNightTime", ((x10global.flags & NIGHT_FLAG) ? 1 : 0)); *ep++ = add_envptr(minibuf); /* Logical variable is true in the interval after Dusk */ /* and before Dawn by the number of minutes defined by */ /* the config directive ISDARK_OFFSET */ sprintf(minibuf, "%s=%d", "X10_isDarkTime", ((x10global.flags & DARK_FLAG) ? 1 : 0)); *ep++ = add_envptr(minibuf); } /* Add upload expiration status and time */ expire = get_upload_expire(); sprintf(minibuf, "%s=%d", "X10_Expire", expire); *ep++ = add_envptr(minibuf); /* Add bitmap for each Housecode|Unit */ for ( hcode = 0; hcode < 16; hcode++ ) { hc = code2hc(hcode); addressed = x10state[hcode].addressed; onstate = x10state[hcode].state[OnState]; dimstate = x10state[hcode].state[DimState]; chgstate = x10state[hcode].state[ChgState]; spendstate = x10state[hcode].statusflags; appliance = ~(modmask[DimMask][hcode] | modmask[BriMask][hcode]); for ( ucode = 0; ucode < 16; ucode++ ) { bitmap = 1 << ucode; /* Start with the dim level either raw or 0-100% */ level = x10state[hcode].dimlevel[ucode]; if ( option & LEVEL_RAW ) { if ( modmask[PresetMask][hcode] & bitmap ) value = level + 1; else value = level; } else { if ( modmask[Ext3Mask][hcode] & bitmap ) value = ext3level2pct(level); else if ( modmask[PresetMask][hcode] & bitmap ) value = presetlevel2pct(level); else value = dims2pct(level); } /* Add the state bits */ if ( appliance & bitmap ) value |= HEYUMAP_APPL; if ( spendstate & bitmap ) value |= HEYUMAP_SPEND; if ( ~onstate & bitmap ) value |= HEYUMAP_OFF; if ( addressed & bitmap ) value |= HEYUMAP_ADDR; if ( chgstate & bitmap ) value |= HEYUMAP_CHG; if ( dimstate & bitmap ) value |= HEYUMAP_DIM; if ( onstate & bitmap ) value |= HEYUMAP_ON; /* Add standard variable, e.g., X10_A9=nnnn */ sprintf(minibuf, "X10_%c%d=%d", hc, code2unit(ucode), value); *ep++ = add_envptr(minibuf); /* Add 'alias' variable, e.g., x10_porch_light=nnnn */ if ( option & ENV_ALIAS ) { j = 0; while ( (aliaslabel = lookup_label_mult(hc, bitmap, &j)) != NULL ) { sprintf(minibuf, "%s_%s=%d", configp->env_alias_prefix, aliaslabel, value); *ep++ = add_envptr(minibuf); } } } } /* Append the user's original environment */ for ( j = 0; j < size; j++ ) { *ep++ = add_envptr(environ[j]); } /* Add NULL terminator */ *ep++ = NULL; /* Make sure we haven't exceeded allocated size */ if ( (int)(ep - envp) > newsize ) { fprintf(stderr, "Internal error in create_heyu_environment()\n"); fprintf(stderr, "Allocated = %d, actual = %d\n", newsize, (int)(ep - envp)); exit(1); } return envp; } /*---------------------------------------------------------------------+ | Create an environment compatible with Xtend scripts. | +---------------------------------------------------------------------*/ char **create_xtend_environment( int index ) { extern char **environ; int j, size, newsize; unsigned char hcode, ucode; char **envp, **ep; char hc; unsigned int value, bitmap, addressed, onstate, appliance; char minibuf[512]; static int sizchrptr = sizeof(char *); LAUNCHER *launcherp; static struct { char *query; int mask; } maskval[] = { {"isAppl", XTMAP_APPL }, {"isAddr", XTMAP_ADDR }, {"isOn", XTMAP_ON }, { NULL, 0 }, }; launcherp = configp->launcherp; if ( launcherp[index].type == L_NORMAL ) putenv("HEYU_PARENT=ENGINE"); else putenv("HEYU_PARENT=RELAY"); /* Get length of original environment */ size = 0; while ( environ[size] != NULL ) size++; newsize = size + 256 + 50; if ( (envp = calloc(newsize, sizchrptr)) == NULL ) { fprintf(stderr, "create_xtend_environment() : out_of_memory\n"); exit(1); } ep = envp; *ep++ = add_envptr("IAMXTEND=1"); /* Put config file pathname in environment for child processes */ sprintf(minibuf, "X10CONFIG=%s", pathspec(heyu_config)); *ep++ = add_envptr(minibuf); /* Create the various masks */ j = 0; while ( maskval[j].query ) { sprintf(minibuf, "%s=%d", maskval[j].query, maskval[j].mask); *ep++ = add_envptr(minibuf); j++; } for ( hcode = 0; hcode < 16; hcode++ ) { hc = code2hc(hcode); addressed = x10state[hcode].addressed; onstate = x10state[hcode].state[OnState]; appliance = ~(modmask[DimMask][hcode] | modmask[BriMask][hcode]); for ( ucode = 0; ucode < 16; ucode++ ) { bitmap = 1 << ucode; value = 0; /* Add the state bits */ if ( appliance & bitmap ) value |= XTMAP_APPL; if ( addressed & bitmap ) value |= XTMAP_ADDR; if ( onstate & bitmap ) value |= XTMAP_ON; sprintf(minibuf, "X10_%c%d=%d", hc, code2unit(ucode), value); *ep++ = add_envptr(minibuf); } } /* Append the user's original environment */ for ( j = 0; j < size; j++ ) { *ep++ = add_envptr(environ[j]); } /* Add NULL terminator */ *ep++ = NULL; return envp; } void free_environment ( char **envp ) { int j = 0; if ( !envp ) return; while ( envp[j] != NULL ) { free(envp[j]); j++; } free(envp); return; } /*---------------------------------------------------------------------+ | For debugging. | +---------------------------------------------------------------------*/ void display_environment ( char **envp ) { int j = 0; while ( envp[j] != NULL ) { printf("%s\n", envp[j]); j++; } } #ifdef EXEC_POSIX /*---------------------------------------------------------------------+ | Launch a general script by the state engine - POSIX | +---------------------------------------------------------------------*/ int launch_script ( int *launchp ) { int retcode, scriptnum, lindex, status; PID_T pid; unsigned char option; char *cmdline, *label; char **envp; char *argv[4]; char *shell; LAUNCHER *launcherp; if ( configp->script_ctrl == DISABLE ) return 0; write_x10state_file(); launcherp = configp->launcherp; lindex = *launchp; scriptnum = launcherp[lindex].scriptnum; cmdline = configp->scriptp[scriptnum].cmdline; option = configp->scriptp[scriptnum].script_option; label = configp->scriptp[scriptnum].label; shell = configp->script_shell; /* We don't want to do this more than once. */ *launchp = -1; if ( option & SCRIPT_QUIET ) fprintf(stdout, "%s Launching '%s': \n", datstrf(), label); else fprintf(stdout, "%s Launching '%s': %s\n", datstrf(), label, cmdline); fflush(stdout); argv[0] = shell; argv[1] = "-c"; argv[2] = cmdline; argv[3] = NULL; retcode = fork(); if ( retcode == -1 ) { fprintf(stderr, "Unable to fork() for launching script.\n"); fflush(stderr); return 1; } if ( retcode == 0 ) { /* In child process */ if ( (pid = fork()) > (PID_T)0 ) { /* Child dies; grandchild inherited by init */ _exit(0); } else if ( pid < (PID_T)0 ) { fprintf(stderr, "Failed to double fork() for launching script.\n"); fflush(stderr); _exit(1); } else { if ( option & SCRIPT_NOENV ) envp = create_noenv_environment(lindex, D_ENGINE); else if ( option & SCRIPT_XTEND ) envp = create_xtend_environment(lindex); else envp = create_heyu_environment(lindex, option); execve(shell, argv, envp); perror("Execution of script has failed"); exit(1); } } /* Wait for child process */ while ( waitpid(retcode, &status, 0) < (PID_T)0 && errno == EINTR ) ; if (WEXITSTATUS(status) != 0 ) { fprintf(stderr, "Unable to double fork() for launching script.\n"); return 1; } return 0; } /*---------------------------------------------------------------------+ | Launch a power-fail script by the heyu relay - POSIX | +---------------------------------------------------------------------*/ int relay_powerfail_script ( void ) { int status; PID_T pid, retpid; char **envp; char *argv[4]; if ( configp->script_ctrl == DISABLE ) return 0; if ( !configp->pfail_script ) return 0; argv[0] = configp->script_shell; argv[1] = "-c"; argv[2] = configp->pfail_script; argv[3] = NULL; retpid = fork(); if ( retpid == (PID_T)(-1) ) { syslog(LOG_ERR,"Unable to fork() in relay_powerfail_script().\n"); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ if ( (pid = fork()) > (PID_T)0 ) { /* Child dies; grandchild inherited by init */ _exit(0); } else if ( pid < (PID_T)0 ) { syslog(LOG_ERR,"Failed to double fork() in relay_powerfail_script()."); _exit(1); } else { envp = create_noenv_environment(-1, D_RELAY); execve(configp->script_shell, argv, envp); perror("Execution of relay_powerfail_script() has failed"); exit(1); } } /* Wait for child process */ while ( waitpid(retpid, &status, 0) < (PID_T)0 && errno == EINTR ) ; if (WEXITSTATUS(status) != 0 ) { syslog(LOG_ERR,"waitpid failed in relay_powerfail_script()."); return 1; } return 0; } /*---------------------------------------------------------------------+ | Start the state engine from main() - POSIX | +---------------------------------------------------------------------*/ int start_engine_main ( void ) { int status; PID_T pid, retpid; char **envp; char *argv[4]; extern char heyuprogname[PATH_LEN + 1 + 10]; strncat(heyuprogname, " engine", sizeof(heyuprogname) - 1 - strlen(heyuprogname)); argv[0] = configp->script_shell; argv[1] = "-c"; argv[2] = heyuprogname; argv[3] = NULL; retpid = fork(); if ( retpid == (PID_T)(-1) ) { syslog(LOG_ERR,"Unable to fork() in start_engine_main().\n"); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ if ( (pid = fork()) > (PID_T)0 ) { /* Child dies; grandchild inherited by init */ _exit(0); } else if ( pid < (PID_T)0 ) { syslog(LOG_ERR,"Failed to double fork() in start_engine_main()."); _exit(1); } else { envp = create_noenv_environment(-1, D_RELAY); execve(configp->script_shell, argv, envp); perror("Execution of start_engine_main() has failed"); exit(1); } } /* Wait for child process */ while ( waitpid(retpid, &status, 0) < (PID_T)0 && errno == EINTR ) ; if (WEXITSTATUS(status) != 0 ) { syslog(LOG_ERR,"waitpid failed in start_engine_main()."); return 1; } return 0; } /*---------------------------------------------------------------------+ | Launch heyuhelper - POSIX | +---------------------------------------------------------------------*/ int launch_heyuhelper ( unsigned char hcode, unsigned int bitmap, unsigned char actfunc ) { int status; PID_T pid, retpid; char *argv[4]; char *shell; char **envp; char hc; char cmdline[128]; if ( configp->script_ctrl == DISABLE ) return 0; write_x10state_file(); hc = tolower((int)code2hc(hcode)); sprintf(cmdline, "heyuhelper %c%d%s", hc, x10state[hcode].lastunit, funclabel[actfunc]); shell = configp->script_shell; argv[0] = shell; argv[1] = "-c"; argv[2] = cmdline; argv[3] = 0; retpid = fork(); if ( retpid == (PID_T)(-1) ) { fprintf(stderr, "Unable to fork() for launching heyuhelper.\n"); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ if ( (pid = fork()) > (PID_T)0 ) { /* Child dies; grandchild inherited by init */ _exit(0); } else if ( pid < (PID_T)0 ) { fprintf(stderr, "Unable to fork() 2nd gen for launching heyuhelper.\n"); fflush(stderr); _exit(1); } else { envp = create_noenv_environment(-1, D_ENGINE); execve(shell, argv, envp); /* Silently exit if failure */ exit(1); } } /* wait for child process */ while ( waitpid(retpid, &status, 0) < (PID_T)0 && errno == EINTR ) ; if (WEXITSTATUS(status) != 0 ) { fprintf(stderr, "Unable to double fork() for launching heyuhelper.\n"); return 1; } return 0; } #else /*---------------------------------------------------------------------+ | Launch a general script by the state engine - non-POSIX | +---------------------------------------------------------------------*/ int launch_script ( int *launchp ) { int scriptnum, lindex; PID_T retpid; unsigned char option; char *cmdline, *label; char **envp; char *argv[4]; char *shell; LAUNCHER *launcherp; if ( configp->script_ctrl == DISABLE ) return 0; signal(SIGCHLD, SIG_IGN); write_x10state_file(); launcherp = configp->launcherp; lindex = *launchp; scriptnum = launcherp[lindex].scriptnum; cmdline = configp->scriptp[scriptnum].cmdline; option = configp->scriptp[scriptnum].script_option; label = configp->scriptp[scriptnum].label; shell = configp->script_shell; /* We don't want to do this more than once. */ *launchp = -1; if ( option & SCRIPT_QUIET ) fprintf(stdout, "%s Launching '%s': \n", datstrf(), label); else fprintf(stdout, "%s Launching '%s': %s\n", datstrf(), label, cmdline); fflush(stdout); argv[0] = shell; argv[1] = "-c"; argv[2] = cmdline; argv[3] = 0; retpid = fork(); if ( retpid == (PID_T)(-1) ) { fprintf(stderr, "Unable to fork() for launching script.\n"); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ if ( option & SCRIPT_NOENV ) envp = create_noenv_environment(lindex, D_ENGINE); else if ( option & SCRIPT_XTEND ) envp = create_xtend_environment(lindex); else envp = create_heyu_environment(lindex, option); execve(shell, argv, envp); perror("Execution of script has failed"); exit(1); } return 0; } /*---------------------------------------------------------------------+ | Launch a power-fail script by the heyu relay - non-POSIX | +---------------------------------------------------------------------*/ int relay_powerfail_script ( void ) { PID_T retpid; char **envp; char *argv[4]; if ( configp->script_ctrl == DISABLE ) return 0; if ( !configp->pfail_script ) return 0; signal(SIGCHLD, SIG_IGN); argv[0] = configp->script_shell; argv[1] = "-c"; argv[2] = configp->pfail_script; argv[3] = 0; retpid = fork(); if ( retpid == (PID_T)(-1) ) { fprintf(stderr, "Unable to fork() in relay_powerfail_script().\n"); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ envp = create_noenv_environment(-1, D_RELAY); execve(configp->script_shell, argv, envp); perror("Execution of relay_powerfail_script() has failed"); exit(1); } return 0; } /*---------------------------------------------------------------------+ | Start the state engine from main() - non-POSIX | +---------------------------------------------------------------------*/ int start_engine_main ( void ) { int j; PID_T retpid; char **envp; char *argv[4]; extern char heyuprogname[PATH_LEN + 1 + 10]; strncat(heyuprogname, " engine", sizeof(heyuprogname) - 1 - strlen(heyuprogname)); signal(SIGCHLD, SIG_IGN); argv[0] = configp->script_shell; argv[1] = "-c"; argv[2] = heyuprogname; argv[3] = 0; retpid = fork(); if ( retpid == (PID_T)(-1) ) { fprintf(stderr, "Unable to fork() in start_engine_main().\n"); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ envp = create_noenv_environment(-1, D_RELAY); execve(configp->script_shell, argv, envp); perror("Execution of start_engine_main() has failed"); exit(1); } /* Wait until engine is ready to go */ for ( j = 0; j < 20; j++ ) { if ( check_for_engine() == 0 ) break; millisleep(100); } return 0; } /*---------------------------------------------------------------------+ | Launch heyuhelper - non-POSIX. | +---------------------------------------------------------------------*/ int launch_heyuhelper ( unsigned char hcode, unsigned int bitmap, unsigned char actfunc ) { PID_T retpid; char *argv[4]; char *shell; char **envp; char hc; char cmdline[128]; if ( configp->script_ctrl == DISABLE ) return 0; signal(SIGCHLD, SIG_IGN); write_x10state_file(); hc = tolower(code2hc(hcode)); sprintf(cmdline, "heyuhelper %c%d%s", hc, x10state[hcode].lastunit, funclabel[actfunc]); shell = configp->script_shell; argv[0] = shell; argv[1] = "-c"; argv[2] = cmdline; argv[3] = NULL; retpid = fork(); if ( retpid == (PID_T)(-1) ) { fprintf(stderr, "Unable to fork() for launching heyuhelper.\n"); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ envp = create_noenv_environment(-1, D_ENGINE); execve(shell, argv, envp); /* Silently exit if failure */ exit(1); } return 0; } #endif /* End of ifdef EXEC_POSIX / else block */ /*---------------------------------------------------------------------+ | Display state information in a variety of formats (primarily for | | use in scripts) in response to command line requests. | +---------------------------------------------------------------------*/ int c_x10state ( int argc, char *argv[] ) { extern void show_module_mask ( unsigned char ); char hc; long delta, timestamp, timenow; unsigned char hcode, ucode, unit, check; unsigned char level, vident; unsigned int state, bitmap, aflags; argv++; argc--; check = check_for_engine(); if ( strcmp(argv[0], "enginestate") == 0 ) { printf("%d\n", (check ? 0 : 1)); return 0; } if ( check != 0 ) { fprintf(stderr, "State engine is not running.\n"); return 1; } if ( argc < 1 ) { fprintf(stderr, "Too few arguments\n"); return 1; } get_configuration(CONFIG_INIT); if ( strcmp(argv[0], "initstate") == 0 ) { /* Initialize the state structure to 0 */ if ( argc == 1 ) { send_x10state_command( ST_INIT_ALL, 0 ); return 0; } else { aflags = parse_addr(argv[1], &hc, &bitmap); if ( !(aflags & A_VALID) || !(aflags & A_HCODE) || (aflags & A_DUMMY) ) { fprintf(stderr, "Invalid argument %s\n", argv[1]); return 1; } if ( bitmap ) fprintf(stderr, "Unit code ignored\n"); send_x10state_command( ST_INIT, hc2code(hc) ); } return 0; } if ( strcmp(argv[0], "initothers") == 0 ) { /* Initialize the cumulative address table to 0 */ send_x10state_command( ST_INIT_OTHERS, 0 ); return 0; } if ( strcmp(argv[0], "fetchstate") == 0 ) { return fetch_x10state(); } else { if ( read_x10state_file() != 0 ) { fprintf(stderr, "Unable to read state file.\n"); return 1; } } if ( strcmp(argv[0], "onstate") == 0 || strcmp(argv[0], "offstate") == 0 || strcmp(argv[0], "dimstate") == 0 || strcmp(argv[0], "chgstate") == 0 || strcmp(argv[0], "addrstate") == 0 || strcmp(argv[0], "lowbattstate") == 0 || strcmp(argv[0], "inactivestate") == 0 ) { if ( argc < 2 ) { fprintf(stderr, "%s: Housecode[Unit] needed\n", argv[0]); return 1; } aflags = parse_addr(argv[1], &hc, &bitmap) ; if ( !(aflags & A_VALID) || !(aflags & A_HCODE) || (aflags & A_DUMMY) ) { fprintf(stderr, "%s: Invalid Housecode|Unit '%s'\n", argv[0], argv[1]); return 1; } if ( aflags & A_MULT && config.state_ctrl == SC_SINGLE ) { fprintf(stderr, "%s: Only a single unit code is acceptable\n", argv[0]); return 1; } if ( aflags & A_MINUS ) bitmap = 0; if ( aflags & A_PLUS && bitmap == 0 ) bitmap = 1; if ( bitmap == 0 ) bitmap = 0xffff; hcode = hc2code(hc); if ( configp->state_format == NEW ) { if ( strcmp(argv[0], "onstate") == 0 ) state = x10state[hcode].state[OnState] & bitmap; else if ( strcmp(argv[0], "offstate") == 0 ) state = ~x10state[hcode].state[OnState] & bitmap; else if ( strcmp(argv[0], "dimstate") == 0 ) state = x10state[hcode].state[DimState] & bitmap; else if ( strcmp(argv[0], "chgstate") == 0 ) state = x10state[hcode].state[ChgState] & bitmap; else if ( strcmp(argv[0], "addrstate") == 0 ) { if ( configp->autofetch == YES && fetch_x10state() != 0 ) return 1; state = x10state[hcode].addressed & bitmap; } else if ( strcmp(argv[0], "lowbattstate") == 0 ) { state = 0; for ( ucode = 0; ucode < 16; ucode++ ) { state |= (x10state[hcode].dimlevel[ucode] & 0x01u) << ucode; } state &= bitmap; } else if ( strcmp(argv[0], "inactivestate") == 0 ) { state = 0; timenow = (long)time(NULL); for ( ucode = 0; ucode < 16; ucode++ ) { timestamp = (long)x10state[hcode].timestamp[ucode]; delta = timenow - timestamp; if ( delta < 0 || delta > config.inactive_timeout ) state |= 1 << ucode; } state &= bitmap; } else { state = 0; } if ( bitmap == 0xffff || config.state_ctrl == SC_BITMAP ) printf("%d\n", x10map2linmap(state)); else printf("%d\n", ((state & bitmap) ? 1 : 0) ); return 0; } if ( configp->state_format == OLD ) { /* heyuhelper format */ if ( !bitmap ) { fprintf(stderr, "%s: No unit specified in '%s'\n", argv[0], argv[1]); return 1; } hc = tolower((int)hc); ucode = single_bmap_unit(bitmap); unit = code2unit(ucode); if ( strcmp(argv[0], "onstate") == 0 ) { if ( x10state[hcode].state[OnState] & bitmap ) printf("%c%dOn\n", hc, unit); else printf("%c%dOff\n", hc, unit); } else if ( strcmp(argv[0], "dimstate") == 0 ) { if ( x10state[hcode].state[DimState] & bitmap ) printf("%c%dDim\n", hc, unit); else if ( x10state[hcode].state[OnState] & bitmap ) printf("%c%dOn\n", hc, unit); else printf("%c%dOff\n", hc, unit); } else if ( strcmp(argv[0], "chgstate") == 0 ) { if ( x10state[hcode].state[ChgState] & bitmap ) printf("%c%dChg\n", hc, unit); else printf("%c%dUnchg\n", hc, unit); } else if ( strcmp(argv[0], "addrstate") == 0 ) { if ( configp->autofetch == YES && fetch_x10state() != 0 ) return 1; if ( x10state[hcode].addressed & bitmap ) printf("%c%dAddr\n", hc, unit); else printf("%c%dUnaddr\n", hc, unit); } return 0; } } if ( strcmp(argv[0], "dimlevel") == 0 || strcmp(argv[0], "rawlevel") == 0 || strcmp(argv[0], "memlevel") == 0 || strcmp(argv[0], "rawmemlevel") == 0 || strcmp(argv[0], "vident") == 0 || strcmp(argv[0], "sincelast") == 0 || strcmp(argv[0], "heyu_state") == 0 || strcmp(argv[0], "heyu_rawstate") == 0 || strcmp(argv[0], "xtend_state") == 0 ) { if ( argc < 2 ) { fprintf(stderr, "%s: Housecode|Unit needed\n", argv[0]); return 1; } aflags = parse_addr(argv[1], &hc, &bitmap) ; if ( !(aflags & A_VALID) || !(aflags & A_HCODE) || (aflags & A_DUMMY) ) { fprintf(stderr, "%s: Invalid Housecode|Unit '%s'\n", argv[0], argv[1]); return 1; } if ( aflags & A_MULT ) { fprintf(stderr, "%s: Only a single unit code is acceptable\n", argv[0]); return 1; } if ( aflags & A_MINUS ) bitmap = 0; if ( aflags & A_PLUS && bitmap == 0 ) bitmap = 1; if ( !bitmap ) { fprintf(stderr, "%s: No unit specified in '%s'\n", argv[0], argv[1]); return 1; } hcode = hc2code(hc); hc = tolower((int)hc); ucode = single_bmap_unit(bitmap); unit = code2unit(ucode); if ( strcmp(argv[0], "dimlevel") == 0 ) { /* Level as a percentage of full On */ level = x10state[hcode].dimlevel[ucode]; if ( modmask[Ext3Mask][hcode] & bitmap ) printf("%d\n", ext3level2pct(level)); else if ( modmask[PresetMask][hcode] & bitmap ) printf("%d\n", presetlevel2pct(level)); else if ( modmask[StdMask][hcode] & bitmap ) printf("%d\n", dims2pct(level)); else if ( modmask[Ext0Mask][hcode] & bitmap ) printf("%d\n", ext0level2pct(level)); else { printf("%d\n", (int)(100 * level) / 255); } } else if ( strcmp(argv[0], "memlevel") == 0 ) { /* Memory level as a percentage of full On */ level = x10state[hcode].memlevel[ucode]; if ( modmask[Ext3Mask][hcode] & bitmap ) printf("%d\n", ext3level2pct(level)); else if ( modmask[PresetMask][hcode] & bitmap ) printf("%d\n", presetlevel2pct(level)); else if ( modmask[StdMask][hcode] & bitmap ) printf("%d\n", dims2pct(level)); else if ( modmask[Ext0Mask][hcode] & bitmap ) printf("%d\n", ext0level2pct(level)); else { printf("%d\n", (int)(100 * level) / 255); } } else if ( strcmp(argv[0], "rawlevel") == 0 ) { /* Native module level */ level = x10state[hcode].dimlevel[ucode]; if ( modmask[PresetMask][hcode] & bitmap ) printf("%d\n", level + 1); else { printf("%d\n", level); } } else if ( strcmp(argv[0], "rawmemlevel") == 0 ) { /* Native module memory level */ level = x10state[hcode].memlevel[ucode]; if ( modmask[PresetMask][hcode] & bitmap ) printf("%d\n", level + 1); else { printf("%d\n", level); } } else if ( strcmp(argv[0], "vident") == 0 ) { /* Virtual ident */ vident = x10state[hcode].vident[ucode]; printf("0x%02X\n", vident); } else if ( strcmp(argv[0], "sincelast") == 0 ) { /* Seconds since last signal */ delta = (long)(time(NULL) - x10state[hcode].timestamp[ucode]); if ( x10state[hcode].timestamp[ucode] == 0 || delta < 0 || delta > (365L * 86400L) ) { printf("-1\n"); } else { printf("%ld\n", delta) ; } } else if ( strcmp(argv[0], "heyu_state") == 0 ) { /* Heyu script bitmap format */ if ( configp->autofetch == YES && fetch_x10state() != 0 ) return 1; printf("%d\n", get_heyu_state(hcode, ucode)); } else if ( strcmp(argv[0], "heyu_rawstate") == 0 ) { /* Heyu script bitmap format, but with native levels */ if ( configp->autofetch == YES && fetch_x10state() != 0 ) return 1; printf("%d\n", get_heyu_rawstate(hcode, ucode)); } else if ( strcmp(argv[0], "xtend_state") == 0 ) { /* Xtend script bitmap format */ if ( configp->autofetch == YES && fetch_x10state() != 0 ) return 1; printf("%d\n", get_xtend_state(hcode, ucode)); } return 0; } if ( strcmp(argv[0], "statestr") == 0 ) { if ( argc > 1 ) { aflags = parse_addr(argv[1], &hc, &bitmap) ; if ( !(aflags & A_VALID) || !(aflags & A_HCODE) || (aflags & A_DUMMY) ) { fprintf(stderr, "%s: Invalid Housecode in '%s'\n", argv[0], argv[1]); return 1; } if ( aflags & A_MINUS ) bitmap = 0; if ( aflags & A_PLUS && bitmap == 0 ) bitmap = 1; if ( bitmap ) { fprintf(stderr, "%s: Units in '%s' ignored\n", argv[0], argv[1]); } if ( configp->autofetch == YES && fetch_x10state() != 0 ) return 1; hcode = hc2code(hc); hc = toupper((int)hc); printf("%s\n", bmap2statestr(x10state[hcode].state[OnState], x10state[hcode].state[DimState], x10state[hcode].state[ChgState], x10state[hcode].addressed) ); return 0; } fprintf(stderr, "%s: Housecode needed\n", argv[0]); return 1; } fprintf(stderr, "State command '%s' not recognized\n", argv[0]); return 1; } /*---------------------------------------------------------------------+ | Display a table showing the dimlevels of every Housecode|Unit, | | expressed as a (integer) percentage of full On brightness. | +---------------------------------------------------------------------*/ void show_all_dimlevels ( void ) { char hc; int unit; unsigned char hcode, ucode, level; unsigned int mask; printf("%20sPercent of Full Brightness\n", ""); printf(" Unit:"); for ( unit = 1; unit <= 16; unit++ ) printf("%3d ", unit); printf("\n"); printf("Hcode "); for ( unit = 1; unit <= 16; unit++ ) printf(" -- "); printf("\n"); for ( hc = 'A'; hc <= 'P'; hc++ ) { printf(" %c ", hc); hcode = hc2code(hc); for ( unit = 1; unit <= 16; unit++ ) { ucode = unit2code(unit); mask = 1 << ucode; level = x10state[hcode].dimlevel[ucode]; if ( modmask[Ext3Mask][hcode] & mask ) printf("%3d ", ext3level2pct(level)); else if ( modmask[PresetMask][hcode] & mask ) printf("%3d ", presetlevel2pct(level)); else if ( modmask[StdMask][hcode] & mask ) printf("%3d ", dims2pct(level)); else if ( modmask[Ext0Mask][hcode] & mask ) printf("%3d ", ext0level2pct(level)); else { printf("%3d ", (int)(100 * level) / ondimlevel[hcode][ucode]); } } printf("\n"); } return; } /*---------------------------------------------------------------------+ | Display a table showing the dimlevel of each Housecode|Unit in the | | native form for the module at the address, i.e., 0-210 for standard | | modules, 1-32 for Preset modules, and 0-63 for Extended modules. | | Preset and Extended levels are prefixed respectively by 'p' and 'e'.| +---------------------------------------------------------------------*/ void show_all_dimlevels_raw ( void ) { char hc; int unit; unsigned char hcode, ucode, level; unsigned int mask; char buffer[16]; printf("Raw Levels:\n (p: Preset 1-32, e: Ext 0-63, s: Shutter 0-25, v: Virt 00-ff, else 0-210)\n"); printf(" Unit:"); for ( unit = 1; unit <= 16; unit++ ) printf("%3d ", unit); printf("\n"); printf("Hcode "); for ( unit = 1; unit <= 16; unit++ ) printf(" -- "); printf("\n"); for ( hc = 'A'; hc <= 'P'; hc++ ) { printf(" %c ", hc); hcode = hc2code(hc); for ( unit = 1; unit <= 16; unit++ ) { ucode = unit2code(unit); mask = 1 << ucode; level = x10state[hcode].dimlevel[ucode]; if ( modmask[Ext3Mask][hcode] & mask ) sprintf(buffer, "e%d", level); else if ( modmask[PresetMask][hcode] & mask ) sprintf(buffer, "p%d", level + 1); else if ( modmask[StdMask][hcode] & mask ) sprintf(buffer, "%d", level); else if ( modmask[Ext0Mask][hcode] & mask ) sprintf(buffer, "s%d", level); else if ( modmask[VdataMask][hcode] & mask ) sprintf(buffer, "v%02x", level); else sprintf(buffer, "%d", level); printf("%3s ", buffer); } printf("\n"); } return; } /*---------------------------------------------------------------------+ | Return the index to a new LAUNCHER in the array of same. | +---------------------------------------------------------------------*/ int launcher_index ( LAUNCHER **launcherpp ) { static int size, maxsize; static int sizlauncher = sizeof(LAUNCHER); int j, index, blksize = 5; /* Allocate initial block if not already done */ if ( *launcherpp == NULL ) { *launcherpp = calloc(blksize, sizlauncher ); if ( *launcherpp == NULL ) { (void) fprintf(stderr, "Unable to allocate memory for Launcher.\n"); exit(1); } maxsize = blksize; size = 0; for ( j = 0; j < maxsize; j++ ) { (*launcherpp)[j].type = L_NORMAL; (*launcherpp)[j].line_no = -1 ; (*launcherpp)[j].label[0] = '\0'; (*launcherpp)[j].bmaptrig = 0; (*launcherpp)[j].chgtrig = 0; (*launcherpp)[j].flags = 0; (*launcherpp)[j].notflags = 0; (*launcherpp)[j].vflags = 0; (*launcherpp)[j].notvflags = 0; (*launcherpp)[j].bootflag = 0; (*launcherpp)[j].trigemuflag = 0; (*launcherpp)[j].timer = 0; (*launcherpp)[j].sensor = 0; (*launcherpp)[j].afuncmap = 0; (*launcherpp)[j].gfuncmap = 0; (*launcherpp)[j].signal = 0; (*launcherpp)[j].module = 0; (*launcherpp)[j].source = 0; (*launcherpp)[j].nosource = 0; (*launcherpp)[j].oksofar = 0; } } /* Check to see if there's an available location */ /* If not, increase the size of the memory allocation. */ /* (Always leave room for a final termination indicator.) */ if ( size == (maxsize - 1) ) { maxsize += blksize ; *launcherpp = realloc(*launcherpp, maxsize * sizlauncher ); if ( *launcherpp == NULL ) { (void) fprintf(stderr, "Unable to increase size of Launcher list.\n"); exit(1); } /* Initialize the new memory allocation */ for ( j = size; j < maxsize; j++ ) { (*launcherpp)[j].type = L_NORMAL; (*launcherpp)[j].line_no = -1 ; (*launcherpp)[j].label[0] = '\0'; (*launcherpp)[j].bmaptrig = 0; (*launcherpp)[j].chgtrig = 0; (*launcherpp)[j].flags = 0; (*launcherpp)[j].notflags = 0; (*launcherpp)[j].vflags = 0; (*launcherpp)[j].notvflags = 0; (*launcherpp)[j].bootflag = 0; (*launcherpp)[j].trigemuflag = 0; (*launcherpp)[j].timer = 0; (*launcherpp)[j].sensor = 0; (*launcherpp)[j].afuncmap = 0; (*launcherpp)[j].gfuncmap = 0; (*launcherpp)[j].signal = 0; (*launcherpp)[j].module = 0; (*launcherpp)[j].source = 0; (*launcherpp)[j].nosource = 0; (*launcherpp)[j].oksofar = 0; } } index = size; size += 1; return index; } /*---------------------------------------------------------------------+ | Create a sensorfail launcher, i.e., triggered when any security | | security sensor inactive countdown reaches zero. | | Return 0 if successful, or -1 if error. | +---------------------------------------------------------------------*/ int create_sensorfail_launcher ( LAUNCHER *launcherp, int tokc, char **tokv ) { int k, flag; char *sp; char errmsg[128]; char tbuf[32]; launcherp->type = L_SENSORFAIL; launcherp->bmaptrigemu = 0xffff; /* Scan the remainder of the tokens for keywords */ for ( k = 1; k < tokc; k++ ) { strncpy2(tbuf, tokv[k], (sizeof(tbuf))); strlower(tbuf); if ( strncmp("flag", tbuf, 4) == 0 ) { flag = (int)strtol(tbuf + 4, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid flag launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->flags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strncmp("notflag", tbuf, 7) == 0 ) { flag = (int)strtol(tbuf + 7, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid notflag launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->notflags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strcmp("night", tbuf) == 0 ) { launcherp->flags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notnight", tbuf) == 0 ) { launcherp->notflags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("dark", tbuf) == 0 ) { launcherp->flags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notdark", tbuf) == 0 ) { launcherp->notflags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("tamper", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_TAMPER; *tokv[k] = '\0'; } else if ( strcmp("armed", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("notarmed", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("disarmed", tbuf) == 0 ) { launcherp->notflags |= (GLOBSEC_ARMED | GLOBSEC_PENDING); *tokv[k] = '\0'; } else if ( strcmp("armpending", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_PENDING; *tokv[k] = '\0'; } else if ( strcmp("home", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("away", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else { sprintf(errmsg, "Parameter '%s' invalid for -sensorfail launcher", tokv[k]); store_error_message(errmsg); return -1; } } /* home and away flags require armed or armpending - default to armed */ if ( ((launcherp->flags | launcherp->notflags) & GLOBSEC_HOME) && !(launcherp->flags & (GLOBSEC_ARMED | GLOBSEC_PENDING)) ) { launcherp->flags |= GLOBSEC_ARMED; } if ( launcherp->flags & launcherp->notflags ) { store_error_message("Conflicting flags in launch conditions."); return -1; } launcherp->oksofar = 1; return 0; } /*---------------------------------------------------------------------+ | Create a timeout launcher, i.e., triggered when the specified timer | | (1-16) countdown reaches zero. | | Return 0 if successful, or -1 if error. | +---------------------------------------------------------------------*/ int create_timeout_launcher ( LAUNCHER *launcherp, int tokc, char **tokv ) { int k, flag, timer, tcount = 0; char *sp; char errmsg[128]; char tbuf[32]; launcherp->type = L_TIMEOUT; launcherp->bmaptrigemu = 0xffff; /* Scan the remainder of the tokens for keywords */ for ( k = 1; k < tokc; k++ ) { strncpy2(tbuf, tokv[k], (sizeof(tbuf))); strlower(tbuf); if ( strncmp("timer", tbuf, 5) == 0 ) { timer = (int)strtol(tbuf + 5, &sp, 10); if ( !strchr(" /t/n", *sp) || timer < 1 || timer > 16 ) { sprintf(errmsg, "Invalid timer launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->timer = timer; tcount++; *tokv[k] = '\0'; } else if ( strncmp("flag", tbuf, 4) == 0 ) { flag = (int)strtol(tbuf + 4, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid flag launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->flags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strncmp("notflag", tbuf, 7) == 0 ) { flag = (int)strtol(tbuf + 7, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid notflag launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->notflags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strcmp("night", tbuf) == 0 ) { launcherp->flags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notnight", tbuf) == 0 ) { launcherp->notflags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("dark", tbuf) == 0 ) { launcherp->flags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notdark", tbuf) == 0 ) { launcherp->notflags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("tamper", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_TAMPER; *tokv[k] = '\0'; } else if ( strcmp("armed", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("notarmed", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("disarmed", tbuf) == 0 ) { launcherp->notflags |= (GLOBSEC_ARMED | GLOBSEC_PENDING); *tokv[k] = '\0'; } else if ( strcmp("armpending", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_PENDING; *tokv[k] = '\0'; } else if ( strcmp("home", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("away", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else { sprintf(errmsg, "Parameter '%s' invalid for -timeout launcher", tokv[k]); store_error_message(errmsg); return -1; } } /* home and away flags require armed or armpending - default to armed */ if ( ((launcherp->flags | launcherp->notflags) & GLOBSEC_HOME) && !(launcherp->flags & (GLOBSEC_ARMED | GLOBSEC_PENDING)) ) { launcherp->flags |= GLOBSEC_ARMED; } if ( launcherp->flags & launcherp->notflags ) { store_error_message("Conflicting flags in launch conditions."); return -1; } if ( tcount == 0 ) { store_error_message("No timer (timer1 ... timer16) has been specified."); return -1; } else if ( tcount > 1 ) { store_error_message("Only one timer (timer1 ... timer16) may be specified."); return -1; } launcherp->oksofar = 1; return 0; } /*---------------------------------------------------------------------+ | Create an RF Flood launcher, i.e., triggered when heyu_aux detects | | an unbroken flood of RF signals. | | Return 0 if successful, or -1 if error. | +---------------------------------------------------------------------*/ int create_rfflood_launcher ( LAUNCHER *launcherp, int tokc, char **tokv ) { int k, flag; char *sp; char errmsg[128]; char tbuf[32]; launcherp->type = L_RFFLOOD; launcherp->bmaptrigemu = 0xffff; /* Scan the remainder of the tokens for keywords */ for ( k = 1; k < tokc; k++ ) { strncpy2(tbuf, tokv[k], (sizeof(tbuf))); strlower(tbuf); if ( strncmp("flag", tbuf, 4) == 0 ) { flag = (int)strtol(tbuf + 4, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->flags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strncmp("notflag", tbuf, 7) == 0 ) { flag = (int)strtol(tbuf + 7, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->notflags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strcmp("night", tbuf) == 0 ) { launcherp->flags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notnight", tbuf) == 0 ) { launcherp->notflags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("dark", tbuf) == 0 ) { launcherp->flags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notdark", tbuf) == 0 ) { launcherp->notflags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("tamper", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_TAMPER; *tokv[k] = '\0'; } else if ( strcmp("armed", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("notarmed", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("disarmed", tbuf) == 0 ) { launcherp->notflags |= (GLOBSEC_ARMED | GLOBSEC_PENDING); *tokv[k] = '\0'; } else if ( strcmp("armpending", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_PENDING; *tokv[k] = '\0'; } else if ( strcmp("home", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("away", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("started", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_FLOOD; *tokv[k] = '\0'; } else if ( strcmp("ended", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_FLOOD; *tokv[k] = '\0'; } else { sprintf(errmsg, "Parameter '%s' invalid for rfflood launcher", tokv[k]); store_error_message(errmsg); return -1; } } /* home and away flags require armed or armpending - default to armed */ if ( ((launcherp->flags | launcherp->notflags) & GLOBSEC_HOME) && !(launcherp->flags & (GLOBSEC_ARMED | GLOBSEC_PENDING)) ) { launcherp->flags |= GLOBSEC_ARMED; } if ( launcherp->flags & launcherp->notflags ) { store_error_message("Conflicting flags in launch conditions."); return -1; } launcherp->oksofar = 1; return 0; } /*---------------------------------------------------------------------+ | Create a powerfail launcher, i.e., triggered when the CM11A polls | | for a time update. Return 0 if successful, or -1 if error. | +---------------------------------------------------------------------*/ int create_powerfail_launcher ( LAUNCHER *launcherp, int tokc, char **tokv ) { int k, flag; char *sp; char errmsg[128]; char tbuf[32]; launcherp->type = L_POWERFAIL; launcherp->bmaptrigemu = 0xffff; /* Scan the remainder of the tokens for keywords */ for ( k = 1; k < tokc; k++ ) { strncpy2(tbuf, tokv[k], (sizeof(tbuf))); strlower(tbuf); if ( strncmp("flag", tbuf, 4) == 0 ) { flag = (int)strtol(tbuf + 4, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->flags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strncmp("notflag", tbuf, 7) == 0 ) { flag = (int)strtol(tbuf + 7, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->notflags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strcmp("night", tbuf) == 0 ) { launcherp->flags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notnight", tbuf) == 0 ) { launcherp->notflags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("dark", tbuf) == 0 ) { launcherp->flags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notdark", tbuf) == 0 ) { launcherp->notflags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("tamper", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_TAMPER; *tokv[k] = '\0'; } else if ( strcmp("armed", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("notarmed", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("disarmed", tbuf) == 0 ) { launcherp->notflags |= (GLOBSEC_ARMED | GLOBSEC_PENDING); *tokv[k] = '\0'; } else if ( strcmp("armpending", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_PENDING; *tokv[k] = '\0'; } else if ( strcmp("home", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("away", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("boot", tbuf) == 0 ) { launcherp->bootflag |= R_ATSTART; } else if ( strcmp("notboot", tbuf) == 0 ) { launcherp->bootflag |= R_NOTATSTART; } else { sprintf(errmsg, "Parameter '%s' invalid for powerfail launcher", tokv[k]); store_error_message(errmsg); return -1; } } /* home and away flags require armed or armpending - default to armed */ if ( ((launcherp->flags | launcherp->notflags) & GLOBSEC_HOME) && !(launcherp->flags & (GLOBSEC_ARMED | GLOBSEC_PENDING)) ) { launcherp->flags |= GLOBSEC_ARMED; } if ( launcherp->flags & launcherp->notflags ) { store_error_message("Conflicting flags in launch conditions."); return -1; } if ( launcherp->bootflag == (R_ATSTART | R_NOTATSTART) ) { store_error_message("Conflicting boot/notboot in launch conditions."); return -1; } launcherp->oksofar = 1; return 0; } /*---------------------------------------------------------------------+ | Create a normal launcher, i.e., triggered with a standard X10 | | signal. Return 0 if successful, or -1 if error. | +---------------------------------------------------------------------*/ int create_normal_launcher ( LAUNCHER *launcherp, int tokc, char **tokv ) { int k, m, mm, mask, flag; char *sp; char hc; char errmsg[128]; unsigned char aflags, hcode, change; unsigned char actual, generic, signal, module, address; unsigned int source, nosource; unsigned int bitmap; char tbuf[32]; change = 0; module = 0; signal = 0; source = 0; nosource = 0; actual = 0; generic = 0; address = 0; launcherp->type = L_NORMAL; launcherp->bmaptrigemu = 0xffff; aflags = parse_addr(tokv[0], &hc, &bitmap); if ( !(aflags & A_VALID) || !(aflags & A_HCODE) || !bitmap ) { sprintf(errmsg, "Invalid Housecode|Units '%s' in Launcher.", tokv[0]); store_error_message(errmsg); return -1; } hcode = hc2code(hc); launcherp->hcode = hc2code(hc); launcherp->bmaptrig = bitmap; /* Scan the remainder of the tokens for keywords */ for ( k = 1; k < tokc; k++ ) { strncpy2(tbuf, tokv[k], (sizeof(tbuf))); strlower(tbuf); if ( strcmp("address", tbuf) == 0 ) { address = 1; *tokv[k] = '\0'; } else if ( strcmp("changed", tbuf) == 0 ) { change = 1; *tokv[k] = '\0'; } else if ( strcmp("module", tbuf) == 0 ) { module = 1; *tokv[k] = '\0'; } else if ( strcmp("signal", tbuf) == 0 ) { signal = 1; *tokv[k] = '\0'; } else if ( strcmp("nosrc", tbuf) == 0 ) { source |= LSNONE; *tokv[k] = '\0'; } else if ( strcmp("anysrc", tbuf) == 0 ) { source |= LSALL; *tokv[k] = '\0'; } else if ( strcmp("sndc", tbuf) == 0 ) { source |= SNDC; *tokv[k] = '\0'; } else if ( strcmp("snds", tbuf) == 0 ) { source |= SNDS; *tokv[k] = '\0'; } else if ( strcmp("sndm", tbuf) == 0 ) { source |= SNDM; *tokv[k] = '\0'; } else if ( strcmp("snda", tbuf) == 0 ) { source |= SNDA; *tokv[k] = '\0'; } else if ( strcmp("sndt", tbuf) == 0 ) { source |= SNDT; *tokv[k] = '\0'; } else if ( strcmp("rcvi", tbuf) == 0 ) { source |= RCVI; *tokv[k] = '\0'; } else if ( strcmp("rcvt", tbuf) == 0 ) { source |= RCVT; *tokv[k] = '\0'; } else if ( strcmp("sndp", tbuf) == 0 ) { source |= SNDP; *tokv[k] = '\0'; } else if ( strcmp("snda", tbuf) == 0 ) { source |= SNDA; *tokv[k] = '\0'; } else if ( strcmp("rcva", tbuf) == 0 ) { source |= RCVA; *tokv[k] = '\0'; } else if ( strcmp("xmtf", tbuf) == 0 ) { source |= XMTF; *tokv[k] = '\0'; } else if ( strcmp("nosndc", tbuf) == 0 ) { nosource |= SNDC; *tokv[k] = '\0'; } else if ( strcmp("nosnds", tbuf) == 0 ) { nosource |= SNDS; *tokv[k] = '\0'; } else if ( strcmp("nosndm", tbuf) == 0 ) { nosource |= SNDM; *tokv[k] = '\0'; } else if ( strcmp("nosnda", tbuf) == 0 ) { nosource |= SNDA; *tokv[k] = '\0'; } else if ( strcmp("nosndt", tbuf) == 0 ) { nosource |= SNDT; *tokv[k] = '\0'; } else if ( strcmp("norcvi", tbuf) == 0 ) { nosource |= RCVI; *tokv[k] = '\0'; } else if ( strcmp("norcvt", tbuf) == 0 ) { nosource |= RCVT; *tokv[k] = '\0'; } else if ( strcmp("nosndp", tbuf) == 0 ) { nosource |= SNDP; *tokv[k] = '\0'; } else if ( strcmp("nosnda", tbuf) == 0 ) { nosource |= SNDA; *tokv[k] = '\0'; } else if ( strcmp("norcva", tbuf) == 0 ) { nosource |= RCVA; *tokv[k] = '\0'; } else if ( strcmp("noxmtf", tbuf) == 0 ) { nosource |= XMTF; *tokv[k] = '\0'; } else if ( strcmp("trigemu", tbuf) == 0 ) { launcherp->trigemuflag = 1; launcherp->bmaptrigemu = bitmap; *tokv[k] = '\0'; } else if ( strncmp("flag", tbuf, 4) == 0 ) { flag = (int)strtol(tbuf + 4, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->flags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strncmp("notflag", tbuf, 7) == 0 ) { flag = (int)strtol(tbuf + 7, &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { sprintf(errmsg, "Invalid launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } launcherp->notflags |= 1 << (flag - 1); *tokv[k] = '\0'; } else if ( strcmp("night", tbuf) == 0 ) { launcherp->flags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notnight", tbuf) == 0 ) { launcherp->notflags |= NIGHT_FLAG; *tokv[k] = '\0'; } else if ( strcmp("dark", tbuf) == 0 ) { launcherp->flags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("notdark", tbuf) == 0 ) { launcherp->notflags |= DARK_FLAG; *tokv[k] = '\0'; } else if ( strcmp("tamper", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_TAMPER; *tokv[k] = '\0'; } else if ( strcmp("armed", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("notarmed", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_ARMED; *tokv[k] = '\0'; } else if ( strcmp("disarmed", tbuf) == 0 ) { launcherp->notflags |= (GLOBSEC_ARMED | GLOBSEC_PENDING); *tokv[k] = '\0'; } else if ( strcmp("armpending", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_PENDING; *tokv[k] = '\0'; } else if ( strcmp("home", tbuf) == 0 ) { launcherp->flags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("away", tbuf) == 0 ) { launcherp->notflags |= GLOBSEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("swmin", tbuf) == 0 ) { /* For the command itself */ launcherp->vflags |= SEC_MIN; *tokv[k] = '\0'; } else if ( strcmp("swmax", tbuf) == 0 ) { /* For the command itself */ launcherp->vflags |= SEC_MAX; *tokv[k] = '\0'; } else if ( strcmp("swhome", tbuf) == 0 ) { launcherp->vflags |= SEC_HOME; *tokv[k] = '\0'; } else if ( strcmp("swaway", tbuf) == 0 ) { launcherp->vflags |= SEC_AWAY; *tokv[k] = '\0'; } else if ( strcmp("lobat", tbuf) == 0 ) { launcherp->vflags |= SEC_LOBAT; *tokv[k] = '\0'; } else if ( strcmp("notlobat", tbuf) == 0 ) { launcherp->notvflags |= SEC_LOBAT; *tokv[k] = '\0'; } else if ( strcmp("main", tbuf) == 0 ) { /* For the command itself */ launcherp->vflags |= SEC_MAIN; *tokv[k] = '\0'; } else if ( strcmp("aux", tbuf) == 0 ) { /* For the command itself */ launcherp->vflags |= SEC_AUX; *tokv[k] = '\0'; } else if ( strcmp("boot", tbuf) == 0 ) { store_error_message("Keyword 'boot' is valid only for -powerfail launchers."); return -1; } else if ( strcmp("notboot", tbuf) == 0 ) { store_error_message("Keyword 'notboot' is valid only for -powerfail launchers."); return -1; } } for ( k = 1; k < tokc; k++ ) { if ( *tokv[k] == '\0' ) continue; for ( m = 0; m < (int)NTRIGTYPES; m++ ) { if ( strcmp(trig_type[m].label, tokv[k]) == 0 ) { if ( trig_type[m].flags & AnyFlag ) { mask = trig_type[m].flags & ~AnyFlag; for ( mm = 0; mm < (int)NTRIGTYPES; mm++ ) { if ( (flag = trig_type[mm].flags) & AnyFlag ) continue; launcherp->afuncmap |= (mask & flag) ? (1 << trig_type[mm].signal) : 0; } } else if ( trig_type[m].flags & GenPLC ) { launcherp->gfuncmap |= (1 << trig_type[m].signal); } else { launcherp->afuncmap |= (1 << trig_type[m].signal); } break; } } if ( m >= (int)NTRIGTYPES ) { sprintf(errmsg, "Invalid launch parameter '%s'", tokv[k]); store_error_message(errmsg); return -1; } } if ( launcherp->gfuncmap == 0 && launcherp->afuncmap == 0 && address == 0 ) { store_error_message("No launch function has been specified."); return -1; } if ( address && (launcherp->gfuncmap || launcherp->afuncmap) ) { store_error_message("Keyword 'address' is incompatible with any functions."); return -1; } else if ( address && (change || module) ) { store_error_message("Keywords 'change' and 'module' are invalid in an address launcher."); return -1; } else if ( address ) { launcherp->type = L_ADDRESS; } /* home and away flags require armed or armpending - default to armed */ if ( ((launcherp->flags | launcherp->notflags) & GLOBSEC_HOME) && !(launcherp->flags & (GLOBSEC_ARMED | GLOBSEC_PENDING)) ) { launcherp->flags |= GLOBSEC_ARMED; } if ( launcherp->flags & launcherp->notflags & 0xffff) { store_error_message("Conflicting flag/notflag in launch conditions."); return -1; } if ( (launcherp->flags & launcherp->notflags & (GLOBSEC_ARMED|GLOBSEC_HOME|GLOBSEC_PENDING)) || ((launcherp->flags & GLOBSEC_PENDING) && (launcherp->flags & GLOBSEC_ARMED)) || ((launcherp->flags & GLOBSEC_PENDING) && (launcherp->notflags & GLOBSEC_ARMED)) ) { store_error_message("Conflicting security launch conditions."); return -1; } if ( change && signal ) { store_error_message("Keywords change and signal are incompatible."); return -1; } if ( module && signal ) { store_error_message("Keywords module and signal are incompatible."); return -1; } if ( change ) { launcherp->chgtrig = launcherp->bmaptrig; module = 1; signal = 0; } else launcherp->chgtrig = 0; if ( module ) { launcherp->module = bitmap; launcherp->signal = 0; } else if ( signal ) { launcherp->module = 0; launcherp->signal = bitmap; } else { launcherp->module = 0; launcherp->signal = 0; } launcherp->source = source; launcherp->nosource = nosource; launcherp->oksofar = 1; return 0; } /*---------------------------------------------------------------------+ | Create individual launchers with argument label from the sets of | | launch conditions in argument tail. Return 0 if successful, or -1 | | if error. | +---------------------------------------------------------------------*/ int create_launchers ( LAUNCHER **launcherpp, char *label, int line_no, char *tail ) { int j, index, retcode = 0; int argc, tokc; char **argv, **tokv; char *sp; char errmsg[128]; LAUNCHER *launcherp; sp = tail; /* Tokenize the line into individual sets of launch conditions */ tokenize( sp, ";", &argc, &argv ); if ( argc == 0 ) { store_error_message("No launch parameters specified"); free(argv); return -1; } for ( j = 0; j < argc; j++ ) { /* Tokenize each set of launch conditions */ tokenize(argv[j], " \t", &tokc, &tokv); if ( tokc < 1 ) { sprintf(errmsg, "No launch parameters specified for %s", tokv[0]); store_error_message(errmsg); free(tokv); free(argv); return -1; } index = launcher_index(launcherpp); (*launcherpp)[index].line_no = line_no; strncpy2((*launcherpp)[index].label, label, NAME_LEN); launcherp = &(*launcherpp)[index]; if ( strcmp(tokv[0], "-powerfail") == 0 ) retcode = create_powerfail_launcher(launcherp, tokc, tokv); else if ( strcmp(tokv[0], "-rfflood") == 0 ) retcode = create_rfflood_launcher(launcherp, tokc, tokv); else if ( strcmp(tokv[0], "-timeout") == 0 ) retcode = create_timeout_launcher(launcherp, tokc, tokv); else if ( strcmp(tokv[0], "-sensorfail") == 0 ) retcode = create_sensorfail_launcher(launcherp, tokc, tokv); else retcode = create_normal_launcher(launcherp, tokc, tokv); free(tokv); if ( retcode != 0 ) break; } free(argv); return retcode; } /*---------------------------------------------------------------------+ | Add a launcher to the array of LAUNCHER structures for each set | | of launch conditions in argument tail. Return 0 if successful. | +---------------------------------------------------------------------*/ int add_launchers ( LAUNCHER **launcherpp, int line_no, char *tail ) { char label[51]; char errmsg[128]; char *sp; sp = tail; /* The first token is the script label */ if ( *get_token(label, &sp, " \t", 50) == '\0' || *sp == '\0' ) { store_error_message("Too few items on line."); return -1; } if ( (int)strlen(label) > NAME_LEN ) { sprintf(errmsg, "Label '%s' too long (max %d characters)", label, NAME_LEN); store_error_message(errmsg); return -1; } strtrim(sp); if ( *sp == '\0' ) { store_error_message("Too few items on line."); return -1; } return create_launchers(launcherpp, label, line_no, sp); } /*---------------------------------------------------------------------+ | Make the appropriate changes/additions/error checks for launchers | | once all the config file information has been stored. | +---------------------------------------------------------------------*/ int finalize_launchers ( void ) { unsigned char mode, matched; int j, k, noloc, errors = 0; LAUNCHER *launcherp; SCRIPT *scriptp; mode = configp->launch_mode ; launcherp = configp->launcherp; scriptp = configp->scriptp; noloc = (configp->loc_flag == (LONGITUDE | LATITUDE)) ? 0 : 1; if ( !launcherp && !scriptp ) return 0; else if ( launcherp && !scriptp ) { fprintf(stderr, "Config File: No SCRIPTs defined for LAUNCHERs"); return 1; } else if ( !launcherp && scriptp ) { fprintf(stderr, "Config File: No LAUNCHERs defined for SCRIPTs"); return 1; } errors = 0; /* Check for duplicate script labels */ k = 0; while ( scriptp[k].line_no > 0 ) { j = k + 1; while ( scriptp[j].line_no > 0 ) { if ( strcmp(scriptp[j].label, scriptp[k].label) == 0 ) { fprintf(stderr, "Config Line %02d: Script label '%s' duplicates line %d\n", scriptp[j].line_no, scriptp[j].label, scriptp[k].line_no); errors++; } j++; } k++; } /* Match up scripts and launchers */ k = 0; while ( scriptp[k].line_no > 0 ) { j = 0; matched = 0; while ( launcherp[j].line_no > 0 ) { if ( strcmp(launcherp[j].label, scriptp[k].label) == 0 ) { matched = 1; launcherp[j].scriptnum = k; break; } j++; } if ( !matched ) { fprintf(stderr, "Config Line %02d: No LAUNCHER for SCRIPT with label '%s'\n", scriptp[k].line_no, scriptp[k].label); errors++; } k++; } j = 0; while ( launcherp[j].line_no > 0 ) { /* Skip known bad launchers */ if ( !(launcherp[j].oksofar) ) { j++; continue; } if ( noloc && ((launcherp[j].flags | launcherp[j].notflags) & (NIGHT_FLAG | DARK_FLAG)) ) { fprintf(stderr, "Config Line %02d: Flags night/notnight/dark/notdark require LONGITUDE and LATITUDE\n", launcherp[j].line_no); errors++; } if ( launcherp[j].type == L_POWERFAIL || launcherp[j].type == L_RFFLOOD || launcherp[j].type == L_TIMEOUT || launcherp[j].type == L_SENSORFAIL ) { j++; continue; } /* Consolidate signal sources */ if ( launcherp[j].source & LSNONE ) launcherp[j].source &= ~(launcherp[j].nosource | LSNONE); else launcherp[j].source = (configp->launch_source | launcherp[j].source) & ~launcherp[j].nosource; if ( !launcherp[j].source ) { fprintf(stderr, "Config Line %02d: No sources (e.g., RCVI) for LAUNCHER with label '%s'\n", launcherp[j].line_no, launcherp[j].label); errors++; } if ( launcherp[j].signal ) launcherp[j].signal = launcherp[j].bmaptrig; else if ( launcherp[j].module ) launcherp[j].signal = 0 /* ~launcherp[j].bmaptrig */; else if ( mode == TMODE_SIGNAL ) launcherp[j].signal = launcherp[j].bmaptrig; else launcherp[j].signal = 0 /* ~launcherp[j].bmaptrig */; /* Fix up 'changed' function if no other function specified */ if ( launcherp[j].chgtrig != 0 ) { if ( launcherp[j].afuncmap == 0 && launcherp[j].gfuncmap == 0 ) launcherp[j].afuncmap = ~(0); } j++; } return errors; } /*---------------------------------------------------------------------+ | Add a script to the array of SCRIPT structures and return | | the index of the new member. | +---------------------------------------------------------------------*/ int add_script ( SCRIPT **scriptpp, LAUNCHER **launcherpp, int line_no, char *tail ) { static int size, maxsize; static int sizscript = sizeof(SCRIPT); int blksize = 5; int j, index; char *sp, *dup; char token[51]; char errmsg[128]; char *separator = "::"; /* Allocate memory for script structure */ /* Allocate initial block if not already done */ if ( *scriptpp == NULL ) { *scriptpp = calloc(blksize, sizscript ); if ( *scriptpp == NULL ) { (void) fprintf(stderr, "Unable to allocate memory for Script.\n"); exit(1); } maxsize = blksize; size = 0; for ( j = 0; j < maxsize; j++ ) { (*scriptpp)[j].line_no = -1 ; (*scriptpp)[j].label[0] = '\0'; (*scriptpp)[j].script_option = 0; (*scriptpp)[j].cmdline = NULL; } } /* Check to see if there's an available location */ /* If not, increase the size of the memory allocation. */ /* (Always leave room for a final termination indicator.) */ if ( size == (maxsize - 1) ) { maxsize += blksize ; *scriptpp = realloc(*scriptpp, maxsize * sizscript ); if ( *scriptpp == NULL ) { (void) fprintf(stderr, "Unable to increase size of Script list.\n"); exit(1); } /* Initialize the new memory allocation */ for ( j = size; j < maxsize; j++ ) { (*scriptpp)[j].line_no = -1 ; (*scriptpp)[j].label[0] = '\0'; (*scriptpp)[j].script_option = 0; (*scriptpp)[j].cmdline = NULL; } } index = size; size += 1; /* Now add the new script information */ sp = tail; (*scriptpp)[index].line_no = line_no; /* Locate substring separating the launcher from the command line */ if ( (sp = strstr(tail, separator)) == NULL ) { sprintf(errmsg, "Invalid SCRIPT format - no '%s' separator\n", separator); store_error_message(errmsg); return -1; } *sp = '\0'; sp += strlen(separator); strtrim(sp); if ( *sp == '\0' ) { store_error_message("Missing script command line\n"); return -1; } /* Set the option for alias states in the heyu environment */ (*scriptpp)[index].script_option |= ENV_ALIAS; /* Look for -xtend option */ if ( strncmp(sp, "-x", 2) == 0 ) { (*scriptpp)[index].script_option |= SCRIPT_XTEND; (*scriptpp)[index].script_option &= ~ENV_ALIAS; /* Bypass the token */ get_token(token, &sp, " \t", 50); strtrim(sp); } /* Look for -noenv option */ if ( strncmp(sp, "-n", 2) == 0 ) { (*scriptpp)[index].script_option |= SCRIPT_NOENV; /* Bypass the token */ get_token(token, &sp, " \t", 50); strtrim(sp); } /* Look for -rawlevel option */ if ( strncmp(sp, "-r", 2) == 0 ) { (*scriptpp)[index].script_option |= LEVEL_RAW; /* Bypass the token */ get_token(token, &sp, " \t", 50); strtrim(sp); } /* Look for -quiet option */ if ( strncmp(sp, "-q", 2) == 0 ) { (*scriptpp)[index].script_option |= SCRIPT_QUIET; /* Bypass the token */ get_token(token, &sp, " \t", 50); strtrim(sp); } if ( *sp == '\0' ) { store_error_message("Missing script command line\n"); return -1; } if ( (dup = strdup(sp)) == NULL ) { fprintf(stderr, "Unable to allocate memory for script command line.\n"); exit(1); } (*scriptpp)[index].cmdline = dup; sp = tail; /* Does script have a label? */ get_token(token, &sp, " \t", 50); if ( strcmp(token, "-l") == 0 ) { /* Yes */ get_token(token, &sp, " \t", 50); strncpy2((*scriptpp)[index].label, token, NAME_LEN); } else { /* No - create default label */ sprintf((*scriptpp)[index].label, "Script_%02d", line_no); sp = tail; } strtrim(sp); /* Add launchers if specified here, otherwise assume user will */ /* add them as a separate config item. */ if ( *sp != '\0' ) { if ( create_launchers(launcherpp, (*scriptpp)[index].label, line_no, sp) < 0 ) return -1; } return index; } /*---------------------------------------------------------------------+ | Load the x10image file and pass back its checksum in the argument. | | Return 1 if OK, 0 otherwise. | +---------------------------------------------------------------------*/ int load_image ( int *chksump ) { FILE *fd; if ( !(fd = fopen(pathspec(IMAGE_FILE), "r")) ) { return 0; } if ( fread((void *)image, 1, PROMSIZE + 2, fd) != PROMSIZE ) { fclose(fd); return 0; } fclose(fd); if ( (*chksump = image_chksum(image)) == -1 ) return 0; return 1; } /*---------------------------------------------------------------------+ | Load the x10image file and compare it's checksum with the argument. | | Return 1 if OK, 0 otherwise. | +---------------------------------------------------------------------*/ int loadcheck_image ( int chksum ) { FILE *fd; int sum; if ( !(fd = fopen(pathspec(IMAGE_FILE), "r")) ) { return 0; } if ( fread((void *)image, 1, PROMSIZE + 2, fd) != PROMSIZE ) { fclose(fd); return 0; } fclose(fd); sum = image_chksum(image); if ( sum != chksum || sum == -1 ) return 0; return 1; } /*---------------------------------------------------------------------+ | Return a pointer to the (static) image. | +---------------------------------------------------------------------*/ unsigned char *image_ptr ( void ) { return image; } /*---------------------------------------------------------------------+ | Scan the cm11a memory image to locate the trigger having argument | | tag which resulted in execution of the macro at macaddr. Pass back | | the housecode|unit and housecode|function bytes through the | | argument list. Return the tag (1-6) of the matching trigger, or 0 | | if no match or if the tag is 7 (indicating anything more than 6). | +---------------------------------------------------------------------*/ int find_trigger ( unsigned char *image, unsigned int macaddr, unsigned char tag, unsigned char *hcu, unsigned char *hcf, unsigned char *fwver ) { unsigned char func, count, *sp; unsigned int addr; if ( tag > 6 ) return 0; count = 0; /* Start of trigger table */ addr = ((image[0] << 8) | image[1]) & 0x3ff; sp = image + addr; /* Future: */ /* Heyu stores CM11A firmware version in a byte */ /* between the timer table terminator 0xff and the */ /* start of the trigger table. */ *fwver = *(sp - 1); while ( sp < (image + PROMSIZE - 2) ) { if ( *sp == 0xff && *(sp+1) == 0xff ) break; addr = (((*(sp+1)) << 8) | *(sp+2)) & 0x7ff; if ( addr == macaddr && ++count == tag ) { *hcu = *sp; func = (*(sp+1) & 0x80 ) ? 2 : 3; *hcf = (*hcu & 0xf0u) | func; return count; } sp += 3; } return 0; } /*---------------------------------------------------------------------+ | Display the trigger with argument tag which executed the macro at | | macaddr. Also update the system state and launch a script if so | | programmed. | +---------------------------------------------------------------------*/ void display_trigger( char *statstr, unsigned int macaddr, unsigned char tag ) { unsigned char hcu, hcf, fwver, buf[4]; int launchp; int count; count = find_trigger(image, macaddr, tag, &hcu, &hcf, &fwver); /* Just return if macro delay > 0 */ if ( image[macaddr] > 0 ) return; if ( count > 0 ) { buf[0] = 0x04; buf[1] = hcu; fprintf(fdsout, "%s rcvt %s\n", statstr, translate_sent(buf, 2, &launchp)); buf[0] = 0x06; buf[1] = hcf; fprintf(fdsout, "%s rcvt %s\n", statstr, translate_sent(buf, 2, &launchp)); if ( launchp >= 0 ) launch_script(&launchp); } else { fprintf(fdsout, "%s rcvt Undetermined trigger\n", statstr); } return; } /*---------------------------------------------------------------------+ | Perform multiple functions when a macro is executed: | | Display individual addresses/functions in monitor | | Update x10status for each address/function | | If trigger conditions are met, launch the user's script. | | | | Argument 'type' is TRIGGER_EXEC or TIMER_EXEC | +---------------------------------------------------------------------*/ int expand_macro( char *statstr, unsigned int macaddr, int type ) { int nelem, j; unsigned char hcode, ucode, level, len; unsigned int bitmap, mask; unsigned char buf[8]; unsigned char *elemp; int launchp; char *pfix; static unsigned char cmdlen[16] = { 3,3,3,3,4,4,3,6,3,3,3,3,3,3,3,3 }; if ( type == TRIGGER_EXEC ) pfix = "sndt"; else pfix = "sndm"; nelem = image[macaddr + 1]; elemp = image + macaddr + 2; for ( j = 0; j < nelem; j++ ) { hcode = (elemp[0] & 0xf0u) >> 4; len = cmdlen[elemp[0] & 0x0f]; bitmap = (elemp[1] << 8) | elemp[2]; /* Display addresses */ mask = 1; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & mask ) { buf[0] = 0x04; buf[1] = (hcode << 4) | ucode ; fprintf(fdsout, "%s %s %s\n", statstr, pfix, translate_sent(buf, 2, &launchp)); } mask = mask << 1; } if ( len == 3 ) { /* Basic command */ buf[0] = 0x06; buf[1] = elemp[0]; fprintf(fdsout, "%s %s %s\n", statstr, pfix, translate_sent(buf, 2, &launchp)); if ( launchp >= 0 ) launch_script(&launchp); } else if ( len == 4 ) { /* Dim or Bright command */ level = elemp[3] & 0x1f; if ( elemp[3] & 0x80 ) { /* Brighten bit is set */ buf[0] = (22 << 3) | 0x06; buf[1] = (hcode << 4) | 5; fprintf(fdsout, "%s %s %s\n", statstr, pfix, translate_sent(buf, 2, &launchp)); if ( launchp >= 0 ) launch_script(&launchp); } buf[0] = (level << 3) | 0x06; buf[1] = elemp[0]; fprintf(fdsout, "%s %s %s\n", statstr, pfix, translate_sent(buf, 2, &launchp)); if ( launchp >= 0 ) launch_script(&launchp); } else { /* Extended command */ buf[0] = 0x07; buf[1] = elemp[0]; buf[2] = elemp[3]; buf[3] = elemp[4]; buf[4] = elemp[5]; fprintf(fdsout, "%s %s %s\n", statstr, pfix, translate_sent(buf, 5, &launchp)); if ( launchp >= 0 ) launch_script(&launchp); } elemp += len; } return 1; } /*---------------------------------------------------------------------+ | Write a lock file for the state engine. Return 1 if one already | | exists or can't be written; 0 otherwise. | +---------------------------------------------------------------------*/ int lock_state_engine ( PID_T pid ) { FILE *fdlock; int acc; if ( (acc = access(enginelockfile, F_OK)) == 0 ) { /* File exists - assume state engine is running */ printf("Heyu state engine is already running.\n"); return 1; } if ( (fdlock = fopen(enginelockfile, "w")) == NULL ) { fprintf(stderr, "Unable to lock heyu state engine."); return 1; } fprintf(fdlock, "%ld\n", (long)pid); fclose(fdlock); return 0; } /*---------------------------------------------------------------------+ | Unlink the lock file for the state engine. | +---------------------------------------------------------------------*/ int unlock_state_engine ( void ) { int retcode; retcode = unlink(enginelockfile); if ( retcode == 0 || errno == ENOENT ) return 0; return retcode; } /*---------------------------------------------------------------------+ | Return 1 if a lock file exists for the state engine, 0 otherwise. | +---------------------------------------------------------------------*/ int checklock_state_engine ( void ) { int acc; if ( (acc = access(enginelockfile, F_OK)) == 0 ) { return 1; } return 0; } /*---------------------------------------------------------------------+ | Tell the state engine daemon to shut itself down and wait for it to | | unlink the lock file. This will fail if called by heyu on a | | different channel (serial_port) since the spool file is different. | | Return 0 if successful or non-zero otherwise. | +---------------------------------------------------------------------*/ int stop_state_engine ( void ) { int j, k, retcode = 1; if ( (retcode = checklock_state_engine()) != 0 ) { for ( j = 0; j < 3; j++ ) { send_x10state_command(ST_EXIT, 0); for ( k = 0; k < 10; k++ ) { millisleep(100); if ( (retcode = checklock_state_engine()) == 0 ) return 0; } } } /* Orphan lockfile? */ retcode = unlock_state_engine(); return retcode; } #ifdef EXEC_POSIX /*---------------------------------------------------------------------+ | Start the state engine process - POSIX style. | +---------------------------------------------------------------------*/ int c_start_engine ( int argc, char *argv[] ) { extern int c_engine(int, char **); int status; char *dup; char tmpfile[PATH_LEN + 1 + 10]; PID_T pid, retpid; extern char *argptr; if ( check_for_engine() == 0 ) { fprintf(stderr,"heyu_engine is running - use 'heyu restart' to reconfigure\n"); return 1; } retpid = fork(); if ( retpid == (PID_T)(-1) ) { fprintf(stderr, "Unable to fork() for starting state engine.\n"); fflush(stderr); return 1; } if ( retpid == (PID_T)0 ) { /* In child process */ if ( (pid = fork()) > (PID_T)0 ) { /* Child dies; grandchild inherited by init */ _exit(0); } else if ( pid < (PID_T)0 ) { fprintf(stderr, "Failed to double fork() for state engine.\n"); fflush(stderr); _exit(1); } else { /* Put config path into environment for any scripts calling heyu */ sprintf(tmpfile, "X10CONFIG=%s", pathspec(heyu_config)); if ( (dup = strdup(tmpfile)) == NULL || putenv(dup) == -1 ) { fprintf(stderr, "Memory allocation error in c_start_engine()\n"); exit(1); } strcpy(argptr, "heyu_engine"); close(0); close(1); close(2); pid = setsid(); if ( pid < (PID_T)0 ) { fprintf(stderr, "Unable to setsid() for state engine.\n"); exit(1); } i_am_state = 1; i_am_relay = 0; heyu_parent = D_CMDLINE; if ( read_x10state_file() != 0 ) x10state_init_all(); lock_state_engine(pid); c_engine(argc, argv); return 0; } } /* Wait for child process to die */ while ( waitpid(retpid, &status, 0) < (PID_T)0 && errno == EINTR ) ; if (WEXITSTATUS(status) != 0 ) { fprintf(stderr, "Unable to double fork() for starting engine.\n"); return 1; } return 0; } #else /*---------------------------------------------------------------------+ | Start the state engine process - non-POSIX | +---------------------------------------------------------------------*/ int c_start_engine ( int argc, char *argv[] ) { extern int c_engine(int, char **); char *dup; char tmpfile[PATH_LEN + 1 + 10]; PID_T pid, retpid; extern char *argptr; if ( check_for_engine() == 0 ) { fprintf(stderr,"heyu_engine is running - use 'heyu restart' to reconfigure\n"); return 1; } signal(SIGCHLD, SIG_IGN); retpid = fork(); if ( retpid == (PID_T)(-1) ) { fprintf(stderr, "Unable to fork() for starting state engine.\n"); return 1; } if ( retpid == (PID_T)0 ) { /* Put config path into environment for any scripts calling heyu */ sprintf(tmpfile, "X10CONFIG=%s", pathspec(heyu_config)); if ( (dup = strdup(tmpfile)) == NULL || putenv(dup) == -1 ) { fprintf(stderr, "Memory allocation error in c_start_engine()\n"); exit(1); } strcpy(argptr, "heyu_engine"); close(0); close(1); close(2); pid = setsid(); if ( pid < (PID_T)0 ) { fprintf(stderr, "Failure in setsid() for starting state engine.\n"); exit(1); } i_am_state = 1; i_am_relay = 0; heyu_parent = D_CMDLINE; if ( read_x10state_file() != 0 ) x10state_init_all(); lock_state_engine(pid); c_engine(argc, argv); return 0; } return 0; } #endif /* End of ifdef EXEC_POSIX / else block */ /*----------------------------------------------------------------------------+ | Instruct state engine to update the flags. Argument mode is SETFLAG or | | CLRFLAG | +----------------------------------------------------------------------------*/ int send_flag_update ( unsigned int flagmap, unsigned char mode ) { extern int sptty; static unsigned char template[9] = { 0xff,0xff,0xff,5,ST_COMMAND,ST_FLAGS,0,0,0}; template[6] = mode; template[7] = (flagmap & 0xff00) >> 8; template[8] = flagmap & 0xff; write(sptty, template, 9); return 0; } /*----------------------------------------------------------------------------+ | Instruct state engine to clear the status flags for hcode and bitmap | +----------------------------------------------------------------------------*/ int send_clear_statusflags ( unsigned char hcode, unsigned int bitmap ) { extern int sptty; static unsigned char template[9] = { 0xff,0xff,0xff,5,ST_COMMAND,ST_CLRSTATUS,0,0,0}; template[6] = hcode; template[7] = (bitmap & 0xff00) >> 8; template[8] = bitmap & 0xff; write(sptty, template, 9); return 0; } void clear_statusflags ( unsigned char hcode, unsigned int bitmap ) { x10state[hcode].statusflags &= ~bitmap; return; } /*---------------------------------------------------------------------+ | Return states of Night and/or Dark flags. | | 'nightstate' or 'darkstate' return boolian. | | 'sunstate' returns bitmap where night = 1, dark = 2 | +---------------------------------------------------------------------*/ int c_sunstate ( int argc, char *argv[] ) { unsigned int flagmap; if ( check_for_engine() != 0 ) { fprintf(stderr, "State engine is not running.\n"); return 1; } if ( argc > 2 ) { fprintf(stderr, "%s: Too many parameters.\n", argv[1]); return 1; } if ( read_x10state_file() != 0 ) { fprintf(stderr, "Unable to read state file.\n"); return 1; } if ( !x10global.dawndusk_enable ) { fprintf(stderr, "LATITUDE and/or LONGITUDE have not been defined.\n"); return 1; } if ( strcmp(argv[1], "nightstate") == 0 ) { printf("%d\n", ((x10global.flags & NIGHT_FLAG) ? 1 : 0)); } else if ( strcmp(argv[1], "darkstate") == 0 ) { printf("%d\n", ((x10global.flags & DARK_FLAG) ? 1 : 0)); } else if ( strcmp(argv[1], "sunstate") == 0 ) { flagmap = (x10global.flags & NIGHT_FLAG) ? 1 : 0; flagmap |= (x10global.flags & DARK_FLAG) ? 2 : 0; printf("%d\n", flagmap); } return 0; } /*---------------------------------------------------------------------+ | Flag reading and setting commands. | +---------------------------------------------------------------------*/ int c_flagstate ( int argc, char *argv[] ) { unsigned int flagmap; int flag; char *sp; if ( check_for_engine() != 0 ) { fprintf(stderr, "State engine is not running.\n"); return 1; } if ( argc < 3 ) { fprintf(stderr, "%s: Too few parameters.\n", argv[1]); return 1; } else if ( argc > 3 ) { fprintf(stderr, "%s: Too many parameters.\n", argv[1]); return 1; } if ( strcmp(argv[1], "flagstate") == 0 ) { flag = (int)strtol(argv[2], &sp, 10); if ( !strchr(" /t/n", *sp) || flag < 1 || flag > 16 ) { fprintf(stderr, "Invalid flag number.\n"); return 1; } if ( read_x10state_file() != 0 ) { fprintf(stderr, "Unable to read state file.\n"); return 1; } flagmap = (1 << (flag - 1)) & x10global.flags; printf("%d\n", ((flagmap) ? 1 : 0)); return 0; } clear_error_message(); flagmap = flags2bmap(argv[2]); if ( *error_message() ) { /* Display error from error handler */ fprintf(stderr, "%s: %s\n", argv[1], error_message()); return 1; } if ( strncmp(argv[1], "setflag", 7) == 0 ) send_flag_update(flagmap, SETFLAG); else if ( strncmp(argv[1], "clrflag", 7) == 0 ) send_flag_update(flagmap, CLRFLAG); return 0; } /*---------------------------------------------------------------------+ | Status flag query. (A status flag for Hu is set when a status_req | | is sent or received and cleared when a status_on or status_off is | | sent or received. | +---------------------------------------------------------------------*/ int c_status_state ( int argc, char *argv[] ) { unsigned int aflags, bitmap, flagstate; char hc; if ( check_for_engine() != 0 ) { fprintf(stderr, "%s: State engine is not running.\n", argv[1]); return 1; } if ( argc < 3 ) { fprintf(stderr, "%s: Too few parameters.\n", argv[1]); return 1; } else if ( argc > 3 ) { fprintf(stderr, "%s: Too many parameters.\n", argv[1]); return 1; } aflags = parse_addr(argv[2], &hc, &bitmap) ; if ( !(aflags & A_VALID) || !(aflags & A_HCODE) || (aflags & A_DUMMY) ) { fprintf(stderr, "%s: Invalid Housecode|Unit '%s'\n", argv[1], argv[2]); return 1; } if ( aflags & A_MULT ) { fprintf(stderr, "%s: Only a single unit code is acceptable\n", argv[1]); return 1; } if ( read_x10state_file() != 0 ) { fprintf(stderr, "%s: Unable to read state file.\n", argv[1]); return 1; } if ( aflags & A_MINUS ) bitmap = 0; if ( aflags & A_PLUS && bitmap == 0 ) bitmap = 1; flagstate = x10state[hc2code(hc)].statusflags; if ( bitmap ) printf("%d\n", ((flagstate & bitmap) ? 1 : 0) ); else printf("%d\n", x10map2linmap(flagstate)); return 0; } /*---------------------------------------------------------------------+ | Display timestamp for a module address | +---------------------------------------------------------------------*/ int show_signal_timestamp ( char *address ) { unsigned int aflags, bitmap; unsigned char ucode; char hc; time_t timestamp; struct tm *tmp; extern char *heyu_tzname[2]; extern void fix_tznames ( void ); aflags = parse_addr(address, &hc, &bitmap); if ( !(aflags & A_VALID) || (aflags & A_MULT) || bitmap == 0 ) { fprintf(stderr, "Invalid Hu address parameter.\n"); return 1; } ucode = single_bmap_unit(bitmap); timestamp = x10state[hc2code(hc)].timestamp[ucode]; if ( timestamp == 0 ) { printf("No signal has been recorded for this address.\n"); return 0; } fix_tznames(); tmp = localtime(×tamp); printf("%c%d: %s %02d:%02d:%02d %s %02d %d %s\n", hc, code2unit(ucode), wday_name[tmp->tm_wday], tmp->tm_hour, tmp->tm_min, tmp->tm_sec, month_name[tmp->tm_mon], tmp->tm_mday, tmp->tm_year + 1900, heyu_tzname[tmp->tm_isdst]); return 0; } /*---------------------------------------------------------------------+ | Reset all user timers 1-16 | +---------------------------------------------------------------------*/ void reset_user_timers ( void ) { int j; for ( j = 1; j <= 16; j++ ) set_timer_countdown(j, 0); return; } /*---------------------------------------------------------------------+ | Translate settimer message and set the timer countdown | +---------------------------------------------------------------------*/ char *translate_settimer_message( unsigned char *buffer ) { unsigned long timeout = 0; int j, timer; static char message[64]; timer = (int)buffer[2]; for ( j = 0; j < 4; j++ ) timeout |= (unsigned long)(buffer[j + 3] << (8 * j)); set_timer_countdown(timer, (long)timeout); if ( timeout < 120 ) sprintf(message, "Countdown timer %d set to %ld seconds", timer, timeout); else sprintf(message, "Countdown timer %d set to %ld:%02ld:%02ld", timer, timeout / 3600L, (timeout % 3600L) / 60L, timeout % 3600L % 60L); return message; } /*---------------------------------------------------------------------+ | Send settimer message to State Engine. | +---------------------------------------------------------------------*/ void send_settimer_msg( int timer, long timeout ) { int j; static unsigned char template[11] = {0xff, 0xff, 0xff, 7, ST_COMMAND, ST_SETTIMER, 0, 0,0,0,0}; template[6] = (unsigned char)timer; for ( j = 0; j < 4; j++ ) { template[j + 7] = (unsigned char)(timeout & 0xff); timeout >>= 8; } write(sptty, (char *)template, 11); return; } /*---------------------------------------------------------------------+ | Display security flags as bitmap: | | 0=disarmed, 1=armed, 2=home, 4=armpending, 8 = tamper | +---------------------------------------------------------------------*/ int c_armedstate( int argc, char *argv[] ) { long globsec; if ( check_for_engine() != 0 ) { fprintf(stderr, "Engine is not running\n"); return 1; } fetch_x10state(); globsec = x10global.flags & (GLOBSEC_ARMED | GLOBSEC_HOME | GLOBSEC_PENDING | GLOBSEC_TAMPER); printf("%ld\n", (globsec >> GLOBSEC_SHIFT)); return 0; } /*---------------------------------------------------------------------+ | Display security Armed, Disarmed, Pending, Home, Away | +---------------------------------------------------------------------*/ int c_armed_status( int argc, char *argv[] ) { if ( check_for_engine() != 0 ) { fprintf(stderr, "Engine is not running\n"); return 1; } fetch_x10state(); printf("%s\n", display_armed_status()); return 0; } /*---------------------------------------------------------------------+ | Date display for monitor and log file. | +---------------------------------------------------------------------*/ char *datstrf ( void ) { static char buffer[40]; time_t now; struct tm *tmp; now = time(NULL); tmp = localtime(&now); sprintf(buffer, "%d/%02d/%02d %02d:%02d:%02d ", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); return buffer; } /*---------------------------------------------------------------------+ | Raise Armed flag - called by countdown function. | +---------------------------------------------------------------------*/ void arm_delay_complete ( int timerindex ) { x10global.flags |= GLOBSEC_ARMED; x10global.flags &= ~GLOBSEC_PENDING; if ( i_am_state || i_am_monitor ) { fprintf(fdsout, "%s %s\n", datstrf(), display_armed_status()); fflush(fdsout); } if ( i_am_state ) write_x10state_file(); return; } /*---------------------------------------------------------------------+ | Launch countdown timer script - called by countdown function. | +---------------------------------------------------------------------*/ void timer_timeout ( int timer ) { int launchp; if ( i_am_state || i_am_monitor ) { fprintf(fdsout, "%s Timeout, timer%d\n", datstrf(), timer); fflush(fdsout); } if ( i_am_state ) { write_x10state_file(); if ( (launchp = find_timeout_launcher(timer)) >= 0 ) { launch_script(&launchp); } } return; } /*---------------------------------------------------------------------+ | Launch inactive sensor script - called by countdown function. | +---------------------------------------------------------------------*/ void sensor_elapsed_func ( int index ) { ALIAS *aliasp; int launchp; int check4poll(int, int); aliasp = configp->aliasp; if ( i_am_state || i_am_monitor ) { check4poll(0,0); fprintf(fdsout, "%s Inactive sensor %c%s (%s)\n", datstrf(), aliasp[index].housecode, bmap2units(aliasp[index].unitbmap), aliasp[index].label); fflush(fdsout); } if ( i_am_state ) { write_x10state_file(); if ( (launchp = find_sensorfail_launcher(index)) >= 0 ) { launch_script(&launchp); } } return; } /*---------------------------------------------------------------------+ | Initialize a countdown timer with the countdown time in seconds. | +---------------------------------------------------------------------*/ void set_timer_countdown ( int timer_nbr, long seconds ) { if ( timer_nbr < 0 || timer_nbr > NumCountdownTimers ) return; countdown_timer[timer_nbr].count = seconds; return; } /*---------------------------------------------------------------------+ | Set a countdown timer 1-16 | +---------------------------------------------------------------------*/ int c_settimer ( int argc, char *argv[] ) { int timer; long timeout; char *sp; if ( argc < 4 ) { fprintf(stderr, "Too few parameters.\n"); return 1; } else if ( argc > 4 ) { fprintf(stderr, "Too many parameters.\n"); return 1; } strtrim(argv[2]); timer = (int)strtol(argv[2], &sp, 10); if ( !strchr(" /t/n/r", *sp) || timer < 1 || timer > 16 ) { fprintf(stderr, "Timer number must be 1-16\n"); return 1; } timeout = parse_hhmmss(argv[3], 3); if ( timeout < 0 || timeout > 0x7FFFFFFF ) { fprintf(stderr, "Invalid timout parameter.\n"); return 1; } send_settimer_msg(timer, timeout); return 0; } /*---------------------------------------------------------------------+ | Display state of security sensors | +---------------------------------------------------------------------*/ int show_security_sensors ( void ) { ALIAS *aliasp; char hc; int j, unit, count = 0, maxmod = 0, maxlabel = 0; unsigned char hcode, ucode; unsigned int bitmap; time_t timenow, timestamp; long elapsed; if ( !(aliasp = config.aliasp) ) return 0; /* Get maximum lengths of module name and alias label */ j = 0; while ( aliasp[j].line_no > 0 ) { if ( (aliasp[j].optflags & MOPT_SENSOR) != 0 ) { count++; maxlabel = max(maxlabel, (int)strlen(aliasp[j].label)); maxmod = max(maxmod, (int)strlen(lookup_module_name(aliasp[j].modtype)) ); } j++; } if ( count == 0 ) { printf("No security sensors have been defined.\n"); return 0; } j = 0; while ( aliasp[j].line_no > 0 ) { if ( (aliasp[j].optflags & MOPT_SENSOR) == 0 ) { j++; continue; } hc = aliasp[j].housecode; hcode = hc2code(hc); bitmap = aliasp[j].unitbmap; timenow = time(NULL); for ( unit = 1; unit <= 16; unit++ ) { ucode = unit2code(unit); if ( bitmap & (1 << ucode) ) { timestamp = x10state[hcode].timestamp[ucode]; elapsed = (long)(timenow - timestamp); printf("%c%2d %-*s %-*s %sbattery %-4s sensor ", hc, unit, maxmod, lookup_module_name(aliasp[j].modtype), maxlabel, aliasp[j].label, ((x10state[hcode].vflags[ucode] & SEC_TAMPER) ? "*TAMPER*, " : ""), ((timestamp == 0) ? "???," : (x10state[hcode].vflags[ucode] & SEC_LOBAT) ? "LOW," : "OK,") ); if ( x10state[hcode].timestamp[ucode] == 0 ) printf("has never been active\n"); else if ( elapsed < config.inactive_timeout ) printf("is Active\n"); else printf("has been Inactive for %ld minutes\n", elapsed/60L); } } j++; } return 0; } /*---------------------------------------------------------------------+ | Check all modules defined by their module_type in an alias as a | | security sensor. OR the results from all security sensors. | | Display a bitmap with 0 = OK, 1 = low battery, 2 = inactive | | Display an error message if the heyu_engine is not running. | +---------------------------------------------------------------------*/ int c_sensorfault ( int argc, char *argv[] ) { ALIAS *aliasp; int j, count = 0; unsigned char hcode, ucode, status = 0; unsigned int bitmap; time_t timenow, timestamp; long elapsed; if ( check_for_engine() != 0 ) { fprintf(stderr, "State engine is not running.\n"); return 1; } if ( argc > 2 ) { fprintf(stderr, "This command takes no parameters.\n"); return 1; } fetch_x10state(); timenow = time(NULL); aliasp = config.aliasp; j = 0; while ( aliasp && aliasp[j].line_no > 0 ) { if ( (aliasp[j].optflags & MOPT_SENSOR) == 0 ) { j++; continue; } hcode = hc2code(aliasp[j].housecode); bitmap = aliasp[j].unitbmap; for ( ucode = 0; ucode < 16; ucode++ ) { if ( bitmap & (1 << ucode) ) { count++; timestamp = x10state[hcode].timestamp[ucode]; elapsed = (long)(timenow - timestamp); if ( elapsed > config.inactive_timeout ) status |= 2; if ( x10state[hcode].vflags[ucode] & SEC_LOBAT ) status |= 1; if ( x10state[hcode].vflags[ucode] & SEC_TAMPER ) status |= 8; } } j++; } printf("%d\n", status); return 0; } /*---------------------------------------------------------------------+ | Create the list of security sensor alias indexes | +---------------------------------------------------------------------*/ int initialize_security_sensor_timeout ( CONFIG *configp ) { ALIAS *aliasp; int j; extern int num_sec_sensors; extern int sensor_alias_indexes[MAX_SEC_SENSORS]; if ( !(aliasp = configp->aliasp) ) return 0; num_sec_sensors = 0; j = 0; while ( aliasp[j].line_no > 0 && num_sec_sensors < MAX_SEC_SENSORS ) { if ( aliasp[j].optflags & MOPT_SENSOR ) { aliasp[j].timeout = configp->inactive_timeout; sensor_alias_indexes[num_sec_sensors++] = j; } j++; } return num_sec_sensors; } /*---------------------------------------------------------------------+ | Clear global and module tamper flags | +---------------------------------------------------------------------*/ int clear_tamper_flags ( void ) { unsigned char hcode, ucode; for ( hcode = 0; hcode < 16; hcode++ ) { for ( ucode = 0; ucode < 16; ucode++ ) { x10state[hcode].vflags[ucode] &= ~SEC_TAMPER; } } x10global.flags &= ~GLOBSEC_TAMPER; return 0; } /*---------------------------------------------------------------------+ | Load UTC0 times of today's Dawn and Dusk into global structure. | | Called at heyu_engine startup or restart, and at Midnight, STD Time | +---------------------------------------------------------------------*/ int set_global_dawndusk ( time_t utc0 ) { if ( x10global.dawndusk_enable == 0 ) { x10global.utc0_dawn = x10global.utc0_dusk = 0; return 0; } local_dawndusk(utc0, &x10global.utc0_dawn, &x10global.utc0_dusk); return 0; } /*---------------------------------------------------------------------+ | Called at startup and again once every minute to update the 'night' | | and 'dark' global flags. | +---------------------------------------------------------------------*/ int update_global_nightdark_flags ( time_t utc0 ) { time_t dawndark, duskdark; unsigned long oldflags; if ( x10global.dawndusk_enable == 0 ) { x10global.flags &= ~(NIGHT_FLAG | DARK_FLAG); return 0; } oldflags = x10global.flags; if ( x10global.utc0_dawn <= x10global.utc0_dusk ) { if ( utc0 < x10global.utc0_dawn || utc0 >= x10global.utc0_dusk ) x10global.flags |= NIGHT_FLAG; else x10global.flags &= ~NIGHT_FLAG; } else { if ( utc0 >= x10global.utc0_dawn || utc0 < x10global.utc0_dusk ) x10global.flags &= ~NIGHT_FLAG; else x10global.flags |= NIGHT_FLAG; } dawndark = x10global.utc0_dawn - (time_t)60 * (time_t)x10global.isdark_offset; duskdark = x10global.utc0_dusk + (time_t)60 * (time_t)x10global.isdark_offset; if ( dawndark <= duskdark ) { if ( utc0 < dawndark || utc0 >= duskdark ) x10global.flags |= DARK_FLAG; else x10global.flags &= ~DARK_FLAG; } else { if ( utc0 >= dawndark || utc0 < duskdark ) x10global.flags &= ~DARK_FLAG; else x10global.flags |= DARK_FLAG; } if ( i_am_state && ((oldflags ^ x10global.flags) & (NIGHT_FLAG | DARK_FLAG)) ) write_x10state_file(); return 0; } /*---------------------------------------------------------------------+ | Display global Dawn and Dusk in Civil Time | +---------------------------------------------------------------------*/ char *display_global_dawndusk ( void ) { static char buffer[64]; struct tm *tmp; tmp = localtime(&x10global.utc0_dawn); sprintf(buffer, "dawn = %02d:%02d, ", tmp->tm_hour, tmp->tm_min); tmp = localtime(&x10global.utc0_dusk); sprintf(buffer + strlen(buffer), "dusk = %02d:%02d\n", tmp->tm_hour, tmp->tm_min); return buffer; } /*---------------------------------------------------------------------+ | Pass back the times of dawn and dusk expressed as seconds after | | midnight civil (wall clock) time. | +---------------------------------------------------------------------*/ int dawndusk_today ( long *dawn, long *dusk ) { struct tm *tmp; if ( x10global.dawndusk_enable == 0 ) return -1; tmp = localtime(&x10global.utc0_dawn); *dawn = 3600L * (long)tmp->tm_hour + 60L * (long)tmp->tm_min + (long)tmp->tm_sec; tmp = localtime(&x10global.utc0_dusk); *dusk = 3600L * (long)tmp->tm_hour + 60L * (long)tmp->tm_min + (long)tmp->tm_sec; return 0; } /*---------------------------------------------------------------------+ | Print remaining countdown times for active timers, i.e., with | | non-zero countdown times. | +---------------------------------------------------------------------*/ int show_global_timers ( void ) { int j; long count; for ( j = 1; j <= 16; j++ ) { if ( (count = x10global.timer_count[j]) > 0 ) printf("Timer %2d %ld:%02ld:%02ld\n", j, count/3600L, (count % 3600L)/60L, count % 60); } return 0; } /*---------------------------------------------------------------------+ | Print global Dawn and Dusk times for today. | +---------------------------------------------------------------------*/ int show_state_dawndusk ( void ) { struct tm *tmp; extern char *heyu_tzname[2]; extern void fix_tznames ( void ); long lutc0; time_t midnight; static char *sunmodelabel[] = {"Sunrise/Sunset", "Civil Twilight", "Nautical Twilight", "Astronomical Twilight"}; if ( configp->loc_flag != (LONGITUDE | LATITUDE) || x10global.dawndusk_enable == 0 ) { fprintf(stderr, "LONGITUDE and LATITUDE must be specified in config file.\n"); return -1; } fix_tznames(); lutc0 = (long)time(NULL); midnight = (time_t)(lutc0 - ((lutc0 - configp->tzone) % 86400L)); if ( x10global.utc0_dawn < midnight || x10global.utc0_dawn >= (midnight + (time_t)86400) || x10global.utc0_dusk < midnight || x10global.utc0_dusk >= (midnight + (time_t)86400) ) { fprintf(stderr, "Internal error: Dawn/Dusk times are for the wrong date.\n"); return -1; } tmp = localtime(&x10global.utc0_dawn); printf("Dawn = %02d:%02d %s", tmp->tm_hour, tmp->tm_min, heyu_tzname[tmp->tm_isdst]); tmp = localtime(&x10global.utc0_dusk); printf(" Dusk = %02d:%02d %s", tmp->tm_hour, tmp->tm_min, heyu_tzname[tmp->tm_isdst]); printf(" (%s)\n", sunmodelabel[configp->sunmode]); return 0; } /*---------------------------------------------------------------------+ | Compute the UTC0 time for 24:00:00 Std Time tonight, i.e., 00:00:00 | | tomorrow, from the current UTC0 time and configured timezone. | +---------------------------------------------------------------------*/ void set_global_tomorrow ( time_t utc0 ) { long lutc0; lutc0 = (long)utc0; x10global.utc0_tomorrow = (time_t)(lutc0 - ((lutc0 - configp->tzone) % 86400L) + 86400L); return; } /*---------------------------------------------------------------------+ | Return 1 if argument utc0 has reached or exceeded utc0 for 00:00:00 | | hours tomorrow; otherwise return 0. | +---------------------------------------------------------------------*/ int is_tomorrow ( time_t utc0 ) { return (utc0 >= x10global.utc0_tomorrow) ? 1 : 0; } /*---------------------------------------------------------------------+ | Called at heyu_engine startup and at a restart | +---------------------------------------------------------------------*/ int engine_local_setup ( void ) { int check4poll(int, int); time_t tnow; check4poll(0,0); tnow = time(NULL); x10global.dawndusk_enable = (configp->loc_flag == (LONGITUDE | LATITUDE)) ? 1 : 0; x10global.isdark_offset = configp->isdark_offset; set_global_dawndusk(tnow); update_global_nightdark_flags(tnow); set_global_tomorrow(tnow); return 0; } /*---------------------------------------------------------------------+ | Called at midnight, standard time | +---------------------------------------------------------------------*/ void midnight_tick ( time_t utc0 ) { set_global_dawndusk(utc0); return; } /*---------------------------------------------------------------------+ | Called once per minute | +---------------------------------------------------------------------*/ void minute_tick ( time_t utc0 ) { update_global_nightdark_flags(utc0); /* Sanity - in case user has reset system time */ set_global_tomorrow(utc0); return; } /*---------------------------------------------------------------------+ | Called approximately once per second to actuate timer countdown. | | (The time delta is included in case a tick is missed.) | +---------------------------------------------------------------------*/ void second_tick ( time_t utc0, long delta ) { ALIAS *aliasp; int j, index; /* Countdown Arm-pending and user timers */ for ( j = 0; j < NumCountdownTimers; j++ ) { if ( countdown_timer[j].count > 0 && (countdown_timer[j].count -= delta) <= 0 ) { countdown_timer[j].count = 0; countdown_timer[j].elapsed_func(j); } x10global.timer_count[j] = countdown_timer[j].count; } /* Countdown security sensor activity timers */ if ( (aliasp = configp->aliasp) != NULL ) { for ( j = 0; j < num_sec_sensors; j++ ) { index = sensor_alias_indexes[j]; if ( aliasp[index].timeout > 0 && (aliasp[index].timeout -= delta) <= 0 ) { aliasp[index].timeout = configp->inactive_timeout; sensor_elapsed_func(index); } } } return; } /*---------------------------------------------------------------------+ | Display a table of Extended Code groups and levels for housecode | +---------------------------------------------------------------------*/ void show_extended_groups ( unsigned char hcode ) { unsigned char ucode, grpmask, grel; unsigned int bitmap, mask, relmask; int unit, group, found; char outbuf[80]; char minbuf[16]; printf("Group levels (0-63) for Housecode %c\n", code2hc(hcode)); printf(" Unit:"); for ( unit = 1; unit <= 16; unit++ ) printf("%3d", unit); printf("\nGroup "); for ( unit = 1; unit <= 16; unit++ ) printf(" --"); printf("\n"); mask = modmask[Ext3GrpExecMask][hcode]; relmask = modmask[Ext3GrpRelMask][hcode]; for ( group = 0; group < 4; group++ ) { grpmask = 1 << group; sprintf(outbuf, " %d ", group); /* Display the absolute group */ for ( unit = 1; unit <= 16; unit++ ) { ucode = unit2code(unit); bitmap = 1 << ucode; if ( mask & bitmap ) { if ( (x10state[hcode].grpmember[ucode] & grpmask) && ((x10state[hcode].grpaddr[ucode] & 0x80u) == 0 || (x10state[hcode].grpmember[ucode] & 0x80u) != 0) ) { sprintf(minbuf, "%3d", x10state[hcode].grplevel[ucode][group]); } else { sprintf(minbuf, " -"); } } else { sprintf(minbuf, " ."); } strcat(outbuf, minbuf); } printf("%s\n", outbuf); /* Scan for relative groups and display if any */ for ( grel = 0x80u; grel < 0x8fu; grel++ ) { sprintf(outbuf, " %d.%-2d ", group, (grel & 0x7fu) + 1); found = 0; for ( unit = 1; unit <= 16; unit++ ) { ucode = unit2code(unit); bitmap = 1 << ucode; if ( mask & bitmap ) { if ( (x10state[hcode].grpmember[ucode] & grpmask) && x10state[hcode].grpaddr[ucode] == grel ) { found = 1; sprintf(minbuf, "%3d", x10state[hcode].grplevel[ucode][group]); } else { sprintf(minbuf, " -"); } } else { sprintf(minbuf, " ."); } strcat(outbuf, minbuf); } if ( found ) { printf("%s\n", outbuf); } } } printf("\n"); return; } /*---------------------------------------------------------------------+ | Copy xmodule information from x10state table to argument arrays. | +---------------------------------------------------------------------*/ int copy_xmodule_info ( unsigned char hcode, unsigned char grpmember[], unsigned char grpaddr[], unsigned char grplevel[][4], unsigned char *xconfigmode ) { int ucode, group; for ( ucode = 0; ucode < 16; ucode++ ) { grpmember[ucode] = x10state[hcode].grpmember[ucode]; grpaddr[ucode] = x10state[hcode].grpaddr[ucode]; for ( group = 0; group < 4; group++ ) grplevel[ucode][group] = x10state[hcode].grplevel[ucode][group]; } *xconfigmode = x10state[hcode].xconfigmode; return 0; }