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
22 import java.util.*;
23 import java.rmi.RemoteException;
24 import javax.comm.CommPortIdentifier;
25
26
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
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
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
314 this.setupCM11A(bean);
315 break;
316 case CM17A_TRANSMITTER:
317
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
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
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
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
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
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
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
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
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
1011 try
1012 {
1013 timeInitiator.setStartTime(imt.getStartTime().get(Calendar.HOUR_OF_DAY),
1014 imt.getStartTime().get(Calendar.MINUTE));
1015 timeInitiator.setStopTime(imt.getStopTime().get(Calendar.HOUR_OF_DAY),
1016 imt.getStopTime().get(Calendar.MINUTE));
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
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
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
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 }