View Javadoc

1   package org.wcb.autohome.factories;
2   /***
3    * Copyright (C) 1999  Walter Bogaardt
4    *
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
18   *
19   */
20  
21  /*standard java classes needed*/
22  import java.util.*;
23  import java.rmi.RemoteException;
24  import javax.comm.CommPortIdentifier;
25  
26  /*special jar file needed for CM11A stuff*/
27  import com.jpeterson.x10.Transmitter;
28  import com.jpeterson.x10.GatewayException;
29  import com.jpeterson.x10.event.*;
30  import com.jpeterson.x10.module.CM11A;
31  import com.jpeterson.x10.module.CM17A;
32  import com.jpeterson.x10.module.Macro;
33  import com.jpeterson.x10.module.MacroInitiator;
34  import com.jpeterson.x10.module.OutOfMacroMemoryException;
35  import com.jpeterson.x10.module.TimerInitiator;
36  
37  /*my stuff needed */
38  import org.wcb.autohome.exceptions.HomeException;
39  import org.wcb.autohome.interfaces.IHAGateway;
40  import org.wcb.autohome.interfaces.IX10Module;
41  import org.wcb.autohome.interfaces.IMacroTrigger;
42  import org.wcb.autohome.interfaces.IMacroEvent;
43  import org.wcb.autohome.interfaces.IMacro;
44  import org.wcb.autohome.util.AliceListener;
45  import org.wcb.autohome.util.AliceX10AddressListener;
46  import org.wcb.autohome.util.HAConfigLoader;
47  import org.wcb.autohome.EventsHandler;
48  import org.wcb.autohome.implementations.EmailHeaderBean;
49  import org.wcb.autohome.implementations.SerialPortBean;
50  import org.wcb.autohome.model.MonitorTableModel;
51  import org.wcb.autohome.interfaces.X10DeviceConstants;
52  import org.wcb.plugins.speech.SpeakPlugin;
53  import org.wcb.common.EmailAuthenticator;
54  
55  /***
56   *
57   *  Project:   Home Automation Interface<br>
58   *  Filename:  $Id: HAGateway.java,v 1.43 2004/06/09 18:49:36 wbogaardt Exp $<br>
59   *  Abstract:  Centralized backend for various components to send events to
60   *             Serial port output.
61   *<p>
62   *This is the main entry point as far as interfacing with the comm port
63   *the JPeterson libary to send appropriate byte streams to the x10 interface.
64   *This class can run either as a remote service wich is instantiated from the JHomeServer
65   *class or directly from the AutoHomeSession class.
66   *
67   *@author wbogaardt
68   *@version 1.0
69   */
70  public class HAGateway extends java.rmi.server.UnicastRemoteObject implements IHAGateway, X10DeviceConstants {
71  
72      private CM11A cm11a = null;
73      private AliceListener aliceListener;
74      private AliceX10AddressListener x10Listener;
75      private CM17A cm17a = null;
76      private static boolean bPORT_ACTIVE = false;
77      private HAConfigLoader prop = null;
78      private EventsHandler eventsDaemon = null;
79      private static boolean EVENTS_DAEMON = false;
80      private static boolean SPEAK = false;
81      private SpeakPlugin voice;
82      private ResourceBundle rbText;
83      private Locale currentLocal;
84  
85      /***
86       * Default constructor which will automatically load properties files from the
87       * user home directory.
88       * @throws RemoteException when running in client server mode
89       */
90      public HAGateway() throws RemoteException {
91          super();
92          prop = new HAConfigLoader("HASconfig.ini");
93          loadInternationalization();
94          loadPlugins();
95      }
96  
97      /***
98       * Constructor which takes a directory information and loads the
99       * properties configuration from the specified directory.
100      * @param sDirectory Directory to load HASConfig.ini from
101      * @throws RemoteException when running in client server mode
102      */
103     public HAGateway(String sDirectory) throws RemoteException {
104         super();
105         prop = new HAConfigLoader("HASconfig.ini", sDirectory);
106         loadInternationalization();
107         loadPlugins();
108     }
109 
110     /***
111      * returns a boolean based on if
112      * the selected port has been activated
113      * or shutdown by the user/application.
114      * @return True if the port is connected to the x10 interface
115      * @throws RemoteException when running in client server mode
116      */
117     public boolean isPortActive() throws RemoteException {
118         if (cm11a != null)
119         {
120             return cm11a.isRunning();
121         }
122         if (cm17a != null)
123         {
124             return cm17a.isRunning();
125         }
126         return bPORT_ACTIVE;
127     }
128 
129     /***
130      * This enumerates all the available ports in the Comm api
131      * A try catch clause has been wrapped around to catch
132      * a possible missing comm API in the user's class path.
133      * A Message dialog box will be shown to the user in this case.
134      * @return Vector of available Ports
135      * @throws RemoteException  when running in client server mode
136      */
137     public Vector getAvailablePorts() throws RemoteException {
138         Vector returnValue = new Vector();
139         try
140         {
141             CommPortIdentifier portId;
142             Enumeration en = CommPortIdentifier.getPortIdentifiers();
143             // iterate through the ports.
144             while (en.hasMoreElements())
145             {
146                 portId = (CommPortIdentifier) en.nextElement();
147                 if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
148                 {
149                     returnValue.addElement(portId.getName());
150                 }
151             }
152         }
153         catch (NoClassDefFoundError err)
154         {
155             System.err.println("Missing comm.jar \n"
156                     + "in your classpath. Exit application\n"
157                     + "and check your java classpath.");
158             returnValue.add("Missing Java COM API classes");
159         }
160         return returnValue;
161     }
162 
163      /***
164      * Saves the setting in jhome.prop with the key
165      * initial.on.start with a string of true or false.
166      * @param bValue Boolean to be saved in property hash.
167      */
168     public void connectSerialOnStartup(boolean bValue) {
169         if (bValue)
170         {
171             prop.setProperty(CONNECT_ON_START, TRUE);
172         }
173         else
174         {
175             prop.setProperty(CONNECT_ON_START, FALSE);
176         }
177     }
178 
179     /***
180      * This sets the speech engine settings if the user
181      * wants to enable it or disable it on startup.
182      * @param bVal true enables the speech engine false is otherwise.
183      */
184     public void enableSpeechEngine(boolean bVal) {
185         SPEAK = bVal;
186         if (bVal)
187         {
188             prop.setProperty(SPEECH_ENGINE, TRUE);
189         }
190         else
191         {
192             prop.setProperty(SPEECH_ENGINE, FALSE);
193         }
194     }
195 
196     /***
197      * Gets the last saved look and feel value. The default is
198      * the sun java (METAL) look and feel.
199      * @return  returns METAL look and feel as default
200      * @throws RemoteException when running in client server mode
201      */
202     public String getLookAndFeel() throws RemoteException {
203         String sReturnValue = prop.getProperty(LOOK_AND_FEEL);
204         if (sReturnValue != null)
205         {
206             return sReturnValue;
207         }
208         return METAL;
209     }
210 
211     /***
212      * Saves the look and feel value in the HASConfig.ini file so that
213      * the gui starts up again the last look and feel can be reloaded.
214      * @param sLook The class name of the look and feel.
215      * @throws RemoteException  when running in client server mode
216      */
217     public void setLookAndFeel(String sLook) throws RemoteException {
218         prop.setProperty(LOOK_AND_FEEL, sLook);
219     }
220 
221     /***
222      * Returns boolean value if the jhome.prop file
223      * connect.on.start has a string of true or false.
224      * @return boolean value of connect.on.start property
225      */
226     public boolean getConnectSerialOnStartup() {
227         String sStart = prop.getProperty(CONNECT_ON_START);
228         if (sStart != null && sStart.equalsIgnoreCase(TRUE))
229         {
230             return true;
231         }
232         return false;
233     }
234 
235     /***
236      * This returns a boolean if the speech engine is to be enabled
237      * or disabled.
238      * @return true is an enabled speech engine.
239      */
240     public boolean isSpeechEnabled() {
241         String sStart = prop.getProperty(SPEECH_ENGINE);
242         if (sStart != null && sStart.equalsIgnoreCase(TRUE))
243         {
244             return true;
245         }
246         return false;
247 
248     }
249 
250     /***
251      * Gets Email information from the property file and puts it into
252      * an object that can be used by the application.
253      * @return A bean with the last saved information.
254      */
255     public EmailHeaderBean getEmailInformation() {
256         EmailHeaderBean emailBean = new EmailHeaderBean(prop.getProperty(EMAIL_TO), prop.getProperty(EMAIL_FROM), prop.getProperty(EMAIL_SMTP));
257         emailBean.setEmailNoticeFreq(prop.getProperty(EMAIL_FREQUENCY));
258         if (this.isEmailAuthorizationRequired())
259         {
260             EmailAuthenticator authenticator = new EmailAuthenticator(prop.getProperty(EMAIL_USERNAME), prop.getProperty(EMAIL_PASSWORD));
261             emailBean.setEmailAuthenticator(authenticator);
262         }
263         return emailBean;
264     }
265 
266     /***
267      * This takes the EmailHeaderBean and saves it into the
268      * HASConfig.ini file for use by the application on its next start up.
269      * @param emailBean Information to be saved
270      */
271     public void setEmailInformation(EmailHeaderBean emailBean) {
272         prop.setProperty(EMAIL_TO, emailBean.getEmailToAddress());
273         prop.setProperty(EMAIL_SMTP, emailBean.getEmailHost());
274         prop.setProperty(EMAIL_FROM, emailBean.getEmailFromAddress());
275         EmailAuthenticator auth = emailBean.getEmailAuthenticator();
276         if (auth != null)
277         {
278             prop.setProperty(EMAIL_AUTHORIZE, getBooleanToString(true));
279             prop.setProperty(EMAIL_USERNAME, auth.getPasswordAuthentication().getUserName());
280             prop.setProperty(EMAIL_PASSWORD, auth.getPasswordAuthentication().getPassword());
281         }
282         else
283         {
284             prop.setProperty(EMAIL_AUTHORIZE, getBooleanToString(false));
285         }
286         prop.setProperty(EMAIL_FREQUENCY, emailBean.getEmailNoticeFreq());
287     }
288 
289     /***
290      * If the HASConfig.ini file indicates that the user
291      * need email authorization then return a true else false.
292       * @return true indicates email requires username password authentication
293      */
294     private boolean isEmailAuthorizationRequired() {
295         Boolean bBoolean = new Boolean(prop.getProperty(EMAIL_AUTHORIZE));
296         return bBoolean.booleanValue();
297     }
298 
299     /***
300      * Based on the user's select it will set the port to
301      * the string variable that is sent.  This first detects
302      * if a current port is open and closes it then it will
303      * intiate connection to the user selected port.
304      * @param bean SerialPortBean object which persists information that will be put in a property file.
305      * @param deviceType Either CM11A or CM17A device
306      * @throws RemoteException when running in client server mode
307      * @throws HomeException Catchable exception sent back to user.
308      */
309     public void connectPortToX10Gateway(SerialPortBean bean, int deviceType) throws RemoteException, HomeException {
310         switch(deviceType)
311         {
312             case CM11A_TRANSMITTER:
313                  /* Active Home Initialization Routines CM11A */
314                 this.setupCM11A(bean);
315                 break;
316             case CM17A_TRANSMITTER:
317                 /* FireCracker port initialization routines */
318                 this.setupCM17A(bean);
319                 break;
320             case TEST_TRANSMITTER:
321                 bPORT_ACTIVE = true;
322         }
323     }
324 
325     /***
326      * This sets up the CM11A device type and is called from connectPortToDevice();
327      *
328      * @param sPort The port name either COM1 or /dev/ttys0
329      * @throws HomeException Catchable exception sent back to user.
330      */
331     private void setupCM11A(SerialPortBean sPort) throws HomeException {
332         printMessage("Connecting CM11A to serial port ");
333         if (sPort.getPort() != null)
334         {
335             /* create and allocate new CM11A connection */
336             if (cm11a == null)
337             {
338                 cm11a = new CM11A();
339                 cm11a.setPortName(sPort.getPort());
340                 cm11a.setBaudRate(sPort.getBaud());
341                 cm11a.setDataBits(sPort.getDataBit());
342                 cm11a.setStopBits(sPort.getStopBit());
343                 bPORT_ACTIVE = true;
344                 try
345                 {
346                     cm11a.allocate();
347                     printMessage(rbText.getString("successful"));
348                 }
349                 catch (GatewayException e)
350                 {
351                     bPORT_ACTIVE = false;
352                     printMessage("Failed!");
353                     throw new HomeException("Gateway Exception for CM11A - " + e);
354                 }
355 
356                 /* CM11A already connected delete it and connect new */
357             }
358             else
359             {
360                 cm11a.deallocate();
361                 cm11a = new CM11A();
362                 bPORT_ACTIVE = true;
363                 cm11a.setPortName(sPort.getPort());
364                 cm11a.setBaudRate(sPort.getBaud());
365                 cm11a.setDataBits(sPort.getDataBit());
366                 cm11a.setStopBits(sPort.getStopBit());
367                 try
368                 {
369                     cm11a.allocate();
370                     printMessage("Successful!");
371                 }
372                 catch (GatewayException ge)
373                 {
374                     ge.printStackTrace();
375                     bPORT_ACTIVE = false;
376                     cm11a = null;
377                     printMessage("Failed!");
378                     throw new HomeException("Gateway Exception for CM11A - " + ge);
379                 }
380             }
381         }
382     }
383 
384     /***
385      * This sets up the CM17A device type and is called from connectPortToDevice();
386      *
387      * @param sPort The port name either COM1 or /dev/ttys0
388      * @throws HomeException Catchable exception
389      */
390     private void setupCM17A(SerialPortBean sPort) throws HomeException {
391         printMessage("Connecting firecracker to serial port ");
392         if (cm17a == null)
393         {
394             bPORT_ACTIVE = true;
395             cm17a = new CM17A();
396             cm17a.setPortName(sPort.getPort());
397             cm17a.setBaudRate(sPort.getBaud());
398             cm17a.setDataBits(sPort.getDataBit());
399             cm17a.setStopBits(sPort.getStopBit());
400             try
401             {
402                 cm17a.allocate();
403                 printMessage(" Successful!");
404             }
405             catch (GatewayException ge)
406             {
407                 bPORT_ACTIVE = false;
408                 printMessage(" Failed!");
409                 throw new HomeException("Gateway Exception for CM17A - " + ge);
410             }
411 
412             // FireCracker is already connected remove and start new connection
413         }
414         else
415         {
416             cm17a.deallocate();
417             cm17a = new CM17A();
418             bPORT_ACTIVE = true;
419             cm17a.setPortName(sPort.getPort());
420             cm17a.setBaudRate(sPort.getBaud());
421             cm17a.setDataBits(sPort.getDataBit());
422             cm17a.setStopBits(sPort.getStopBit());
423             try
424             {
425                 cm17a.allocate();
426                 printMessage(" Successful!");
427             }
428             catch (GatewayException ge)
429             {
430                 bPORT_ACTIVE = false;
431                 cm17a = null;
432                 printMessage(" Failed!");
433                 throw new HomeException("Gateway Exception for CM17A - " + ge);
434             }
435         }
436     }
437 
438     /***
439      * This closes the port that had been
440      * opened by the user to the CM11A or CM17A interface module
441      * @throws RemoteException when running in client server mode
442      * @throws HomeException Catchable exception sent back to user.
443      */
444     public void closeSerialPort() throws RemoteException, HomeException {
445         if (cm11a != null)
446         {
447             try
448             {
449                 cm11a.waitGatewayState(Transmitter.QUEUE_EMPTY);
450                 printMessage("Transmitting all pending X10 commands.");
451                 bPORT_ACTIVE = false;
452             }
453             catch (InterruptedException e)
454             {
455                 printMessage("Unable to transmit last x10 command. Port closed.");
456                 throw new HomeException("Failed to send last commands and close");
457             }
458             cm11a.deallocate();
459             aliceListener = null;
460             x10Listener = null;
461             cm11a = null;
462         }
463         if (cm17a != null)
464         {
465             bPORT_ACTIVE = false;
466             try
467             {
468                 cm17a.waitGatewayState(Transmitter.QUEUE_EMPTY);
469                 printMessage("Transmitting all pending commands.");
470             }
471             catch (InterruptedException e)
472             {
473                 printMessage("Unable to transmit last x10 command. Port closed.");
474                 throw new HomeException("Failed to send last commands and close.");
475             }
476             cm17a.deallocate();
477             cm17a = null;
478         }
479     }
480 
481      /***
482      * These are commands that can be sent to the entire
483      * section of a house indicated by the House code, which is a value between 'A' and 'P'
484       * The comands work for lights and apppliance modules in that section.
485       * @param cHouseCode House section between 'A' and 'P'
486       * @param cmnd Command is 0 all lights off, 1 all lights on, and 2 all units off.
487       * @throws RemoteException when running in client server mode
488       * @throws HomeException Catchable exception sent back to user.
489      */
490     public void allCommandToSection(char cHouseCode, int cmnd) throws RemoteException, HomeException {
491         X10Event[] events = null;
492         switch (cmnd)
493         {
494             case 0:
495                 printMessage("Section " + cHouseCode + " all Lights off");
496                 events = new X10Event[1];
497                 events[0] = new AllLightsOffEvent(this, cHouseCode);
498 
499                 break;
500             case 1:
501                 printMessage("Section " + cHouseCode + " all Lights ON");
502                 events = new X10Event[1];
503                 events[0] = new AllLightsOnEvent(this, cHouseCode);
504                 break;
505             case 2:
506                 printMessage("Section " + cHouseCode + " all units off");
507                 events = new X10Event[1];
508                 events[0] = new AllUnitsOffEvent(this, cHouseCode);
509                 break;
510         }
511         if (events != null && this.isPortActive())
512         {
513             sendCommandToInterface(events);
514             this.printMessage("Confirmed");
515         }
516         else if (events == null)
517         {
518             throw new HomeException("Invalid Command sent.");
519         }
520         else
521         {
522             this.printMessage("X 10 gateway is not activated!");
523         }
524     }
525 
526     /***
527      * This controls the lights for only for dim and brighten events
528      * @param x10Evt x10 module
529      * @param cmnd Either 0 for dim 1 for brighten
530      * @param iPercentage percentation to change between 1 and 100.
531      * @throws RemoteException when running in client server mode
532      * @throws HomeException Catchable exception sent back to user.
533      */
534     public void lampIntensity(IX10Module x10Evt, int cmnd, int iPercentage)
535             throws RemoteException, HomeException {
536         X10Event[] events = null;
537         //calculate percentage to units
538         Double dUnits = new Double(iPercentage * .22);
539         int iUnits = dUnits.intValue();
540         switch (cmnd)
541         {
542             case 0:
543                 events = new X10Event[2];
544                 events[0] = new AddressEvent(this, x10Evt.getHouseCode(), x10Evt.getDeviceCode());
545                 events[1] = new DimEvent(this, x10Evt.getHouseCode(), iUnits, 22);
546                 break;
547             case 1:
548                 events = new X10Event[2];
549                 events[0] = new AddressEvent(this, x10Evt.getHouseCode(), x10Evt.getDeviceCode());
550                 events[1] = new BrightEvent(this, x10Evt.getHouseCode(), iUnits, 22);
551                 break;
552         }
553         if (events != null && this.isPortActive())
554         {
555             sendCommandToInterface(events);
556             this.printMessage("Confirmed");
557         }
558         else if (events == null)
559         {
560             throw new HomeException("Invalid Command!");
561         }
562         else
563         {
564             this.printMessage("X 10 gateway is not activated!");
565         }
566     }
567 
568     /***
569      * This controls the appliance modules for only On/Off events
570      * @param x10evt The X10Module to send the command to
571      * @param cmnd 0 is the off command, 1 is the on command
572      * @throws RemoteException when running in client server mode
573      * @throws HomeException Catchable exception sent back to user.
574      */
575     public void deviceCommands(IX10Module x10evt, int cmnd) throws RemoteException, HomeException {
576         X10Event[] events = null;
577         switch (cmnd)
578         {
579             //Off command
580             case 0:
581                 events = new X10Event[2];
582                 events[0] = new AddressEvent(this, x10evt.getHouseCode(), x10evt.getDeviceCode());
583                 events[1] = new OffEvent(this, x10evt.getHouseCode());
584                 break;
585             // ON command
586             case 1:
587                 events = new X10Event[2];
588                 events[0] = new AddressEvent(this, x10evt.getHouseCode(), x10evt.getDeviceCode());
589                 events[1] = new OnEvent(this, x10evt.getHouseCode());
590                 break;
591         }
592         if (events != null && this.isPortActive())
593         {
594             sendCommandToInterface(events);
595             this.printMessage("Confirmed");
596         }
597         else if (events == null)
598         {
599             throw new HomeException("Invalid Command!");
600         }
601         else
602         {
603             this.printMessage("X 10 gateway is not activated!");
604         }
605     }
606 
607 
608     /***
609      * This method sends the array of events to the CM11A or CM17A
610      * interface via the selected serial port.
611      * @param events The array of X10Events from jesse peterson's x10 api
612      * @throws HomeException Catchable exception sent back to user.
613      */
614     private void sendCommandToInterface(X10Event[] events) throws HomeException {
615         for (int j = 0; j < events.length; j++)
616         {
617             if (cm11a != null)
618             {
619                 cm11a.transmit(events[j]);
620             }
621             else
622             {
623                 cm17a.transmit(events[j]);
624             }
625         }
626         try
627         {
628             if (cm11a != null)
629             {
630                 cm11a.waitGatewayState(Transmitter.QUEUE_EMPTY);
631             }
632             else
633             {
634                 cm17a.waitGatewayState(Transmitter.QUEUE_EMPTY);
635             }
636         }
637         catch (InterruptedException e)
638         {
639             throw new HomeException("Connection failed when sending queue.");
640         }
641     }
642 
643     /***
644      * allows the user to set or send commands to set the CM11A clock settings
645      * This command needs the hours,minutes, seconds, month, day , day of week,
646      * house code character value, whether to reset the battery timer,
647      * clear the CM11A from monitoring the house, or purge the macro timer.
648      * @param calendar The day to set the clock
649      * @param houseCode the house code of the event. Valid codes are 'A' through 'P', uppercase.
650      * @param batteryTimer If true, the interface's battery timer will be cleared.
651      * @param clrMonitored If true, the interface's monitored statuses will be cleared.
652      * @param purgeTimer If true, this will purge the interfaces timer
653      * @throws RemoteException when running in client server mode
654      * @throws HomeException Catchable exception sent back to user.
655      */
656     public void setClock(Calendar calendar, char houseCode,
657                          boolean batteryTimer, boolean clrMonitored,
658                          boolean purgeTimer) throws RemoteException, HomeException
659     {
660         if (cm11a != null)
661         {
662             try
663             {
664                 Thread.sleep(3000);
665                 cm11a.setClock(calendar.get(Calendar.HOUR_OF_DAY),
666                         calendar.get(Calendar.MINUTE),
667                         calendar.get(Calendar.SECOND),
668                         calendar.get(Calendar.MONTH),
669                         calendar.get(Calendar.DAY_OF_MONTH),
670                         calendar.get(Calendar.DAY_OF_WEEK),
671                         houseCode, batteryTimer, clrMonitored, purgeTimer);
672             }
673             catch (Exception e)
674             {
675                 throw new HomeException("Failed to set time.");
676             }
677             printMessage("Set CM11A Clock to " + calendar.get(Calendar.HOUR_OF_DAY)
678                     + ":"
679                     + calendar.get(Calendar.MINUTE)
680                     + ":"
681                     + calendar.get(Calendar.SECOND) + ", "
682                     + calendar.get(Calendar.MONTH) + "/" + calendar.get(Calendar.DAY_OF_MONTH)
683                     + " for house " + houseCode);
684         }
685         else
686         {
687             throw new HomeException("Clock not supported for interface");
688         }
689     }
690 
691     /***
692      * Gets the calendar date from the CM11A/CM12U interface.
693      * @return Current time on the CM11A device
694      */
695     public Calendar getCM11ADate() {
696         GregorianCalendar cal = new GregorianCalendar();
697         if (cm11a != null)
698         {
699             int hours = cm11a.getHours();
700             int minutes = cm11a.getMinutes();
701             int seconds = cm11a.getSeconds();
702             int iDayOfWeek = cm11a.getCurrentDay();
703             int julian = cm11a.getJulianDay();
704             cal.set(Calendar.HOUR_OF_DAY, hours);
705             cal.set(Calendar.MINUTE, minutes);
706             cal.set(Calendar.SECOND, seconds);
707             cal.set(Calendar.DAY_OF_YEAR, julian);
708             cal.set(Calendar.DAY_OF_WEEK, iDayOfWeek);
709         }
710         return cal;
711     }
712 
713     /***
714      * Used to listen to the CM11A gateway.
715      *
716      * @return true indicates able to monitor port
717      * @throws RemoteException when running in client server mode
718      */
719     public boolean monitorCM11A() throws RemoteException {
720         if (this.isPortActive() && cm11a != null)
721         {
722             printMessage("Monitoring CM11A gateway.");
723             cm11a.addAddressListener(x10Listener);
724             cm11a.addFunctionListener(x10Listener);
725             return true;
726         }
727         else
728         {
729             printMessage("CM11A gateway not connected.");
730 
731         }
732         return false;
733     }
734 
735     /***
736      * this disables monitoring of the cm11a gateway by removing
737      * the address and function listeners
738      * @return true indicates can disable monitor to port false indicates cannot.
739      * @throws RemoteException when running in client server mode
740      */
741     public boolean disableMonitorCM11A() throws RemoteException {
742         if (this.isPortActive() && cm11a != null)
743         {
744             printMessage("Disabled monitoring CM11A gateway.");
745             cm11a.removeAddressListener(x10Listener);
746             cm11a.removeFunctionListener(x10Listener);
747             return true;
748         }
749         else
750         {
751             printMessage("CM11A gateway not connected.");
752         }
753         return false;
754     }
755 
756     /***
757      * Sets the monitoring model to the Alice Listener objects
758      * AliceListener and AliceX10AddressListener
759      * @param mModel MonitorTableModel to add to the listeners.
760      */
761     public void setMonitorModel(MonitorTableModel mModel)
762     {
763         x10Listener = new AliceX10AddressListener(mModel);
764     }
765 
766     /***
767      * Indicates that the user either wants the CM11A to autorecover
768      * upon detecting a power failure.  If it is set to true, when the
769      * CM11A detects a power failure signal the command to set the clock will
770      * be sent. if set to false, it is up to another device to set the clock
771      * via a call to setCM11AClock before teh CM11A can be used.
772      * @param bValue true indicates recover CM11A clock
773      * @throws RemoteException when running in client server mode
774      */
775     public void recoverCM11A(boolean bValue) throws RemoteException {
776         if (cm11a != null)
777         {
778             cm11a.setPowerFailureAutoRecover(bValue);
779         }
780         // save the setting to the HASConfig.ini file
781         prop.setProperty(RECOVER_CM11A, this.getBooleanToString(bValue));
782     }
783 
784     public void updateCM11AStatus() {
785          if (cm11a != null)
786         {
787             boolean recover = cm11a.getPowerFailureAutoRecover();
788             prop.setProperty(RECOVER_CM11A, this.getBooleanToString(recover));
789         }
790     }
791 
792     /***
793      * Method call to get the property value of Auto recovering
794      * of the CM11A from the HASConfig.ini file
795      * @return Boolean value of true or false
796      * @throws RemoteException when running in client server mode
797      */
798     public boolean isAutoRecoverCM11A() throws RemoteException {
799         return Boolean.getBoolean(prop.getProperty(RECOVER_CM11A));
800     }
801 
802     /***
803      * Retrieves the CM11A's current battery usage.  The value is is
804      * initialized after a call to updateStatus();
805      * The returned value is the number of minutes.
806      * @return Time in minutes from 0 to 500 minutes
807      * @throws RemoteException when running in client server mode
808      */
809     public int getBatteryUsage() throws RemoteException {
810         if (cm11a != null)
811         {
812             cm11a.updateStatus();
813             isAutoRecoverCM11A();
814             return cm11a.getBatteryUsage();
815         }
816         return 0;
817     }
818 
819     //MACRO SECTION
820     /***
821      * Sends all of the macro created events to the
822      * CM11a device for storage.
823      * @throws HomeException Catchable exception sent back to user.
824      */
825     private void uploadAllInitiators() throws HomeException {
826         if (cm11a != null && cm11a.isRunning())
827         {
828             try
829             {
830                 Thread.sleep(3000);
831                 cm11a.downloadInitiators();
832             }
833             catch (OutOfMacroMemoryException oomme)
834             {
835                 throw new HomeException("Not enought memory on interface ");
836             }
837             catch (InterruptedException ie)
838             {
839                 throw new HomeException(ie.toString());
840             }
841         }
842         else
843         {
844             throw new HomeException("Port Inactive");
845         }
846     }
847 
848     /***
849      * method used to load macro events that have an event type
850      * initiator ie turning on a light module via remote controller
851      * sets off a change of other lights and appliance to turn off or on
852      * example:
853      * <pre>
854      * Macro macro1 = new Macro();
855      * macro1.addCommand(4, new OnEvent(this, 'A'));
856      * MacroInitiator macroInitiator= new MacroInitiator(1, new OnEvent(this, 'A'));
857      * macroInitiator.setMacro(macro1);
858      *       :
859      * AutoHomeAdminSession.getInstance().uploadMacroInitiator(macroInitiator);
860      *</pre>
861      * The above sets up a macro where if A1 is turned on then turn on A4;
862      * @param macroInit macro initiator object to send to CM11A
863      * @throws HomeException Catchable exception sent back to user.
864      */
865     private void uploadMacroInitiator(MacroInitiator macroInit) throws HomeException {
866         if (cm11a.isRunning() && cm11a != null)
867         {
868             cm11a.addMacroInitiator(macroInit);
869         }
870         else
871         {
872             throw new HomeException("Port not Connected or macros not supported");
873         }
874     }
875 
876     /***
877      * This allows a change of events to occur based on a particular
878      * time and day of week that the event is set to go off at.
879      * <pre>
880      * Macro macro1 = new Macro();
881      * macro1.addCommand(4, new OnEvent(this, 'A'));
882      * Macro macro2 = new Macro();
883      * macro2.addCommand(4, new OffEvent(this, 'A'));
884      * TimerInitiator timerInit= new TimerInitiator();
885      * timerInit.addDayOfWeek(Calendar.SUNDAY);
886      * timerInit.addDayOfWeek(Calendar.WEDNESDAY);
887      * timerInit.setStartTime(15,19);
888      * timerInit.setStopTime(06,30);
889      * timerInit.setStartMacro(macro1);
890      * timerInit.setStopMacro(macro2);
891      *       :
892      * AutoHomeAdminSession.getInstance().uploadTimerInitiator(timerInit);
893      *</pre>
894      * The above sets up a macro where if A4 is turned on
895      * every Sunday and Wednesday at 3:19pm and then turned off
896      * at 6:30am.
897      * @param timeInit Time initiator object to send to CM11A device
898      * @throws HomeException Catchable exception sent back to user.
899      */
900     private void uploadTimerInitiator(TimerInitiator timeInit) throws HomeException {
901         if (cm11a.isRunning())
902         {
903             cm11a.addTimerInitiator(timeInit);
904         }
905         else
906         {
907             throw new HomeException("Port Inactive");
908         }
909     }
910 
911     /***
912      * Clears only the timer event driven macros
913      * from the CM11A.
914      * @throws RemoteException when running in client server mode
915      * @throws HomeException Catchable exception sent back to user.
916      */
917     public void clearTimerMacros() throws RemoteException, HomeException {
918         if (isPortActive() && cm11a != null)
919         {
920             cm11a.clearTimerInitiators();
921         }
922         else
923         {
924             throw new HomeException("Port Inactive or macros not supported");
925         }
926     }
927 
928     /***
929      * Clears only the event driven macros
930      * from the CM11A.
931      * @throws RemoteException when running in client server mode
932      * @throws HomeException Catchable exception sent back to user.
933      */
934     public void clearMacroEvents() throws RemoteException, HomeException {
935         if (isPortActive() && cm11a != null)
936         {
937             cm11a.clearMacroInitiators();
938         }
939         else
940         {
941             throw new HomeException("Port Inactive or macros not supported");
942         }
943     }
944 
945     /***
946      * This builds the macro trigger events
947      * for both the timer type triggers and for the
948      * macro module initiators. For CM11A type computer interfaces.
949      *
950      *file format for event triggers is: Trigger.EV1=EV,A1,description,ON or OFF,Macro5,
951      *for time triggers is: Trigger.EV2=TM,A1,05:00/12:00,description,-MTWTF-,Macro1/Macro2
952      *
953      *Trigger maping is Trigger.EV(increase numeric value)
954      *@param triggers are the list of IMacroTrigger objects
955      * @throws RemoteException when running in client server mode
956      * @throws HomeException Catchable exception sent back to user.
957      */
958     public void sendMacrosToInterface(Vector triggers) throws RemoteException, HomeException {
959         Enumeration enumr = triggers.elements();
960         IMacroTrigger itrigger;
961         while (enumr.hasMoreElements())
962         {
963             itrigger = (IMacroTrigger) enumr.nextElement();
964             if (itrigger.getTriggerType() == TRIGGER_EVENT)
965             {
966                 buildTriggerMacro(itrigger);
967             }
968             else if (itrigger.getTriggerType() == TIMER_EVENT)
969             {
970                 buildTimerMacro(itrigger);
971             }
972             else
973             {
974                 throw new HomeException("Invalid trigger assigned");
975             }
976         }
977         this.uploadAllInitiators();
978     }
979 
980     private int buildTriggerMacro(IMacroTrigger imt) {
981         MacroInitiator tmpMacro;
982         IX10Module iX10Module = imt.getX10Module();
983         if (imt.getAction().equalsIgnoreCase("On"))
984         {
985             tmpMacro = new MacroInitiator(iX10Module.getDeviceCode(), new OnEvent(this, iX10Module.getHouseCode()));
986         }
987         else
988         {
989             tmpMacro = new MacroInitiator(iX10Module.getDeviceCode(), new OffEvent(this, iX10Module.getHouseCode()));
990         }
991         IMacro[] macro = imt.getMacros();
992         tmpMacro.setMacro(buildMacros(macro[0]));
993         try
994         {
995             uploadMacroInitiator(tmpMacro);
996         }
997         catch (HomeException e)
998         {
999             return 1;
1000         }
1001         return 0;
1002     }
1003 
1004     private int buildTimerMacro(IMacroTrigger imt)
1005     {
1006         TimerInitiator timeInitiator = new TimerInitiator();
1007         IMacro[] macro = imt.getMacros();
1008         timeInitiator.setStartMacro(buildMacros(macro[0]));
1009         timeInitiator.setStopMacro(buildMacros(macro[1]));
1010         /* Get start time and stop time */
1011         try
1012         {
1013             timeInitiator.setStartTime(imt.getStartTime().get(Calendar.HOUR_OF_DAY),
1014                     imt.getStartTime().get(Calendar.MINUTE)); //start time init
1015             timeInitiator.setStopTime(imt.getStopTime().get(Calendar.HOUR_OF_DAY),
1016                     imt.getStopTime().get(Calendar.MINUTE)); // stop time init
1017             if (imt.getSunday())
1018             {
1019                 timeInitiator.addDayOfWeek(Calendar.SUNDAY);
1020             }
1021             if (imt.getMonday())
1022             {
1023                 timeInitiator.addDayOfWeek(Calendar.MONDAY);
1024             }
1025             if (imt.getTuesday())
1026             {
1027                 timeInitiator.addDayOfWeek(Calendar.TUESDAY);
1028             }
1029             if (imt.getWednesday())
1030             {
1031                 timeInitiator.addDayOfWeek(Calendar.WEDNESDAY);
1032             }
1033             if (imt.getThursday())
1034             {
1035                 timeInitiator.addDayOfWeek(Calendar.THURSDAY);
1036             }
1037             if (imt.getFriday())
1038             {
1039                 timeInitiator.addDayOfWeek(Calendar.FRIDAY);
1040             }
1041             if (imt.getSaturday())
1042             {
1043                 timeInitiator.addDayOfWeek(Calendar.SATURDAY);
1044             }
1045 
1046         }
1047         catch (NumberFormatException err)
1048         {
1049             return 1;
1050             //throw new HomeException("Failed parsing date field");
1051         }
1052         try
1053         {
1054             uploadTimerInitiator(timeInitiator);
1055         }
1056         catch (HomeException e)
1057         {
1058             return 1;
1059         }
1060         return 0;
1061     }
1062 
1063     /***
1064      * Takes a macro name to parse the properties file with
1065      * and finds the macro and associated events to return
1066      * a macro grouping. The macro grouping is then used
1067      * inconjuction with a macro initiation event such as
1068      * a timer event or action on another module to
1069      * start the macro.
1070      *File format is:Macro1.event0=AM,A1,On
1071      *Macro1.delay=1
1072      *
1073      *subsequent macros are done in Macro1.event1=same formate
1074      *, Macro1.event2=same formate
1075      * @param im Macro interface.
1076      * @return Macro object
1077      */
1078     public Macro buildMacros(IMacro im) {
1079         Macro macro;
1080         if (im.getDelay() > 0)
1081         {
1082             macro = new Macro(im.getDelay());
1083         }
1084         else
1085         {
1086             macro = new Macro();
1087         }
1088         Enumeration enumr = im.getEvents().elements();
1089         IMacroEvent event;
1090         IX10Module iX10Device;
1091         while (enumr.hasMoreElements())
1092         {
1093             event = (IMacroEvent) enumr.nextElement();
1094             iX10Device = event.getX10Module();
1095             /* add appropriate action for Devices*/
1096             switch(event.getAction())
1097             {
1098                 case 0:
1099                     macro.addCommand(iX10Device.getDeviceCode(), new OffEvent(this, iX10Device.getHouseCode()));
1100                     break;
1101                 case 1:
1102                     macro.addCommand(iX10Device.getDeviceCode(), new OnEvent(this, iX10Device.getHouseCode()));
1103                     break;
1104                 case 2:
1105                     macro.addCommand(iX10Device.getDeviceCode(), new DimEvent(this, iX10Device.getHouseCode(), 11, 22));
1106                     break;
1107                 case 3:
1108                     macro.addCommand(iX10Device.getDeviceCode(), new BrightEvent(this, iX10Device.getHouseCode(), 11, 22));
1109                     break;
1110             }
1111         }
1112         return macro;
1113     }
1114 
1115     /***
1116      * Get the type of the interface this can either be a CM11A or CM17a wich
1117      * returns an int value representation of either of these devices.
1118      * @return CM11A_TRANSMITER or CM17A_TRANSMITER
1119      * @throws RemoteException when running in client server mode
1120      */
1121     public int getInterfaceType() throws RemoteException {
1122         try
1123         {
1124             return Integer.parseInt(prop.getProperty(INTERFACE_TYPE));
1125         }
1126         catch (NumberFormatException err)
1127         {
1128             return CM17A_TRANSMITTER;
1129         }
1130     }
1131 
1132     /***
1133      * Sets the interface type to either the CM11A or CM17 a device.
1134      * @param type sets the interface type
1135      * @throws RemoteException when running in client server mode
1136      */
1137     public void setInterfaceType(int type) throws RemoteException {
1138         prop.setProperty(INTERFACE_TYPE, type + "");
1139     }
1140 
1141     /***
1142      * This returns a bean of the serial port settings saved in a properties file.
1143      * Normally the properties were key value pairs sent back to the user
1144      * as strings or int values, now this is all wrapped up into a bean.
1145      * @return  SerialPortBean reference.
1146      * @throws RemoteException when running in client server mode
1147      */
1148     public SerialPortBean getSerialPort() throws RemoteException {
1149         SerialPortBean serialBean = new SerialPortBean(prop.getProperty(SERIAL_PORT));
1150         serialBean.setBaud(getBaud());
1151         serialBean.setDataBit(getDataBit());
1152         serialBean.setParity(prop.getProperty(PARITY));
1153         serialBean.setStopBit(getStopBit());
1154         if (prop.getProperty(SERIAL_PORT).startsWith("NO PORTS FOUND"))
1155         {
1156             return serialBean;
1157         }
1158         return serialBean;
1159     }
1160 
1161     private int getBaud() {
1162         try
1163         {
1164             return Integer.parseInt(prop.getProperty(BAUD));
1165         }
1166         catch (NumberFormatException err)
1167         {
1168             return 4800;
1169         }
1170     }
1171 
1172     private int getDataBit() {
1173         try
1174         {
1175             return Integer.parseInt(prop.getProperty(DATA_BIT));
1176         }
1177         catch (NumberFormatException err)
1178         {
1179             return 8;
1180         }
1181     }
1182 
1183     private int getStopBit() {
1184         try
1185         {
1186             return Integer.parseInt(prop.getProperty(STOP_BIT));
1187         }
1188         catch (NumberFormatException err)
1189         {
1190             return 1;
1191         }
1192     }
1193 
1194     public void setSerialPort(SerialPortBean bean) throws RemoteException {
1195         prop.setProperty(SERIAL_PORT, bean.getPort());
1196         prop.setProperty(BAUD, new Integer(bean.getBaud()).toString());
1197         prop.setProperty(DATA_BIT, new Integer(bean.getDataBit()).toString());
1198         prop.setProperty(STOP_BIT, new Integer(bean.getStopBit()).toString());
1199         prop.setProperty(PARITY, bean.getParity());
1200     }
1201 
1202     public void saveServerProperty() throws RemoteException {
1203         prop.saveProperties();
1204     }
1205 
1206     /* Starts and stops the events running daemon */
1207     public boolean runEventsDaemon(Vector table) throws RemoteException {
1208         eventsDaemon = new EventsHandler(this);
1209         eventsDaemon.setModel(table);
1210         eventsDaemon.start();
1211         EVENTS_DAEMON = true;
1212         return true;
1213     }
1214 
1215     public void stopEventsDaemon() throws RemoteException {
1216         eventsDaemon.stop();
1217         eventsDaemon = null;
1218         EVENTS_DAEMON = false;
1219     }
1220 
1221     public boolean isEventDaemonRunning() throws RemoteException {
1222         return EVENTS_DAEMON;
1223     }
1224 
1225     /***
1226      *This loads the classes for the speach plugin.
1227      *Actually this is a simple part in that it just
1228      *says what commands it has run.  Take a look at
1229      *Javaworlds article on <a href="http://www.javaworld.com/javaworld/jw-08-2001/jw-0817-javatalk_p.html">
1230      * http://www.javaworld.com/javaworld/jw-08-2001/jw-0817-javatalk_p.html</a>
1231      *this subject.
1232      */
1233     private void loadPlugins() {
1234         try
1235         {
1236             voice = new SpeakPlugin();
1237             if (this.isSpeechEnabled())
1238             {
1239                 voice.play(rbText.getString("speech.engine"));
1240                 SPEAK = true;
1241             }
1242         }
1243         catch (NoClassDefFoundError ncdfe)
1244         {
1245             SPEAK = false;
1246         }
1247     }
1248 
1249     private void loadInternationalization() {
1250         String sLanguage = prop.getProperty(LANGUAGE);
1251         String sCountry = prop.getProperty(COUNTRY);
1252         if (sLanguage != null && sCountry != null)
1253         {
1254             currentLocal = new Locale(sLanguage, sCountry);
1255             rbText = ResourceBundle.getBundle("org.wcb.plugins.AliceResource", currentLocal, HAGateway.class.getClassLoader());
1256         }
1257         else
1258         {
1259             currentLocal = new Locale("en", "US");
1260             rbText = ResourceBundle.getBundle("org.wcb.plugins.AliceResource", currentLocal);
1261         }
1262     }
1263 
1264     /***
1265      * This gets the Locale I18n settings
1266      * @return The new local i18n setting
1267      * @throws RemoteException when running in client server mode
1268      */
1269     public Locale getLocal() throws RemoteException
1270     {
1271         return this.currentLocal;
1272     }
1273 
1274     /***
1275      * This method allows setting of the internationalization local
1276      * to the value passed and saves this information into the
1277      * HASConfig.ini file
1278      * @param loc Local setting to set and save to file
1279      * @throws RemoteException when running in client server mode
1280      */
1281     public void setLocal(Locale loc) throws RemoteException {
1282         this.currentLocal = loc;
1283         prop.setProperty(LANGUAGE, loc.getLanguage());
1284         prop.setProperty(COUNTRY, loc.getCountry());
1285     }
1286 
1287     public ResourceBundle getI18n() throws RemoteException
1288     {
1289         return rbText;
1290     }
1291 
1292     /***
1293      * Prints a message into the events log or if
1294      * the speech plug in is added to the system then
1295      * the speech will occure
1296      * @param messages message to print and speak.
1297      */
1298     protected void printMessage(String messages) {
1299         if (SPEAK)
1300         {
1301             try
1302             {
1303                 speakMessage(messages);
1304             }
1305             catch (RemoteException re)
1306             {
1307             }
1308         }
1309         System.out.println(messages);
1310     }
1311 
1312     /***
1313      * Prints a message into the events log or if
1314      * the speech plug in is added to the system then
1315      * the speech will occur
1316      * @param messages to speek
1317      * @throws RemoteException when running in client server mode
1318      */
1319     public void speakMessage(String messages) throws RemoteException {
1320         if (SPEAK)
1321         {
1322             voice.play(messages);
1323         }
1324     }
1325 
1326      private String getBooleanToString(boolean bVal) {
1327         if (bVal)
1328         {
1329             return TRUE;
1330         }
1331         return FALSE;
1332     }
1333 }