Subversion Repositories HomeAutomation

Rev

Go to most recent revision | Blame | Last modification | View Log | SVN | RSS feed

  1. /*
  2.  * File created by Jimmy
  3.  * at 2003-sep-06 15:09:02
  4.  */
  5. package Macbeth.Modules.modRemoteControl;
  6.  
  7. import java.util.*;
  8. import Macbeth.System.*;
  9. import Macbeth.Utilities.*;
  10. import Macbeth.XML.XMLDataHandler;
  11.  
  12. /**
  13.  * A Macbeth module that receives remote control data from
  14.  * different sources. It will then try to interprete this
  15.  * data in order to find out which button was pressed
  16.  * (buttons are mapped to byte combinations in its XML-
  17.  * configuration file). If the button was known, then this
  18.  * module will also send a notification to all other modules
  19.  * that have requested to be notified about this specific
  20.  * button.
  21.  * @author Jimmy
  22.  */
  23. public class modRemoteControl extends MbModule {
  24.     //The remote control bytes that we receive via XML
  25.     private UByte[] receivedBytes;
  26.     //Are we currently parsing valid ARNE remote control XML?
  27.     private boolean parsingARNEXML;
  28.     //A table containing the name of every known button, each with its corresponding bytes as key
  29.     private HashMap remoteControlButtons;
  30.     //A table containing all button requests made by other modules
  31.     private HashMap buttonRequests;
  32.  
  33.     /**
  34.      * Creates a new instance of modRemoteControl.
  35.      */
  36.     public modRemoteControl() {
  37.         //construct MbModule
  38.         super();
  39.         receivedBytes = new UByte[3];
  40.         parsingARNEXML = false;
  41.         //mapping tables
  42.         remoteControlButtons = new HashMap(256);
  43.         buttonRequests = new HashMap(256);
  44.         //register our packet data handler
  45.         setPacketDataHandler(new PacketDataHandler());
  46.         //register our HTML interface
  47.         setHTMLInterface(new HTMLInterface());
  48.     }
  49.  
  50.     public String name() {
  51.         return "RemoteControl";
  52.     }
  53.  
  54.     public String description() {
  55.         return "Sends remote control notifications to other modules";
  56.     }
  57.  
  58.     /**
  59.      * This method sets default values on all _required_ data
  60.      * fields in the data repository.
  61.      */
  62.     protected void initDataFields() {
  63.         //this module doesn't require any data fields
  64.     }
  65.  
  66.     public void startup() throws MbStartupException {
  67.         //start up MbModule
  68.         super.startup();
  69.  
  70.         //get all remote control definitions from the data repository
  71.         DataRepository.DataList list = dataRepository.getList("remotecontrolbuttons");
  72.         if (list != null) {
  73.             Iterator items = list.items();
  74.             while (items.hasNext()) {
  75.                 DataRepository.DataListItem item = (DataRepository.DataListItem) items.next();
  76.                 String buttonname = (String) item.getField("buttonname");
  77.                 //read the unsigned bytes
  78.                 UByte b1 = UByte.parseUByte((String) item.getField("byte1"));
  79.                 UByte b2 = UByte.parseUByte((String) item.getField("byte2"));
  80.                 //create a key string from the two bytes
  81.                 String keyString = b1.toString() + ":" + b2.toString();
  82.                 //store all key definitions in a list
  83.                 remoteControlButtons.put(keyString, buttonname);
  84.             }
  85.         }
  86.         //no remote control definitions found in data repository
  87.         else {
  88.             _info.println("remotecontrolbuttons-list is missing. no remote control buttons defined!");
  89.         }
  90.     }
  91.  
  92.     public void shutdown() {
  93.         //shut down MbModule
  94.         super.shutdown();
  95.     }
  96.  
  97.     /**
  98.      * Takes care of successfully received bytes from any remote control.
  99.      * @param bytes The bytes that have been received.
  100.      */
  101.     private void handleBytes(UByte[] bytes) {
  102.         //first byte is status
  103.         UByte status = bytes[0];
  104.         String state = (status.byteValue() & 0xF0)==0xF0 ? "DOWN" : "UP";
  105.         //next two bytes are button ID
  106.         UByte[] buttonID = new UByte[2];
  107.         buttonID[0] = bytes[1];
  108.         buttonID[1] = bytes[2];
  109.         String keyString = buttonID[0].toString() + ":" + buttonID[1].toString();
  110.         //do we know of this button?
  111.         if (remoteControlButtons.containsKey(keyString)) {
  112.             String buttonName = (String)remoteControlButtons.get(keyString);
  113.             sendButtonNotification(buttonName,state);
  114.         } else {
  115.             _debug.println("Unknown remote control button pressed, bytes were:");
  116.             _debug.println("  status: " + status.toString());
  117.             _debug.println("  ID byte1: " + buttonID[0].toString());
  118.             _debug.println("  ID byte2: " + buttonID[1].toString());
  119.         }
  120.     }
  121.  
  122.     /**
  123.      * Call this when the state changes for a remote control button.
  124.      * Notifications will be sent to the modules that have requested
  125.      * notifications about this button.
  126.      * @param buttonName The button name.
  127.      * @param buttonState The button state ("UP" or "DOWN").
  128.      */
  129.     private void sendButtonNotification(String buttonName, String buttonState) {
  130.         //has any module requested notifications about this button?
  131.         if (buttonRequests.containsKey(buttonName)) {
  132.             //find out which modules have requested notifications about this button
  133.             ButtonRequests br = (ButtonRequests) buttonRequests.get(buttonName);
  134.             String[] modules = br.getModuleNames();
  135.             //and notify each of them
  136.             for (int i = 0; i < modules.length; i++) {
  137.                 MbPacket p = new MbPacket();
  138.                 p.setContents("<remotecontrol button=\"" + buttonName + "\" state=\"" + buttonState + "\" />");
  139.                 p.setDestination(new MbLocation("self", modules[i]));
  140.                 sendPacket(p);
  141.             }
  142.         }
  143.     }
  144.  
  145.     /**
  146.      * A HTML-interface for this module.
  147.      */
  148.     private class HTMLInterface implements MbHTMLInterface {
  149.         /**
  150.          * Retreives the full code for your HTML-page. This is
  151.          * formatted as plain text and must be valid HTML-code.
  152.          * @return Full HTML-code for the page.
  153.          */
  154.         public String getHTMLPage() {
  155.             String s = "<html><body>" + MySystem.lineBreak;
  156.             s += "<h1>Remote Control module</h1>" + MySystem.lineBreak;
  157.             s += "<p>This module receives data from Macbeth remote controls and notifies other modules about button events on such remote controls.</p>" + MySystem.lineBreak;
  158.             s += "<p>The table below shows all known remote control buttons and which modules have requested to use them. You can also press the button names to emulate a button press/button release sequence for that button.</p>";
  159.             s += "<center><p><table border=\"1\" cellpadding=\"3\" cellspacing=\"0\"><tr><td><b>Remote control button</b></td><td><b>Modules that use the button</b></td></tr>";
  160.             //iterate through the known button names
  161.             Iterator it = remoteControlButtons.values().iterator();
  162.             while (it.hasNext()) {
  163.                 String button = (String)it.next();
  164.                 if (button != null) {
  165.                     s += "<tr><td><a href=\"button:" + button + "\">" + button + "</a></td><td>";
  166.                     //check if there are any requests for this button
  167.                     String modulelist = "";
  168.                     ButtonRequests requests = (ButtonRequests)buttonRequests.get(button);
  169.                     if (requests != null) {
  170.                         String[] modules = requests.getModuleNames();
  171.                         for (int i = 0; i < modules.length; i++) {
  172.                             modulelist += modules[i];
  173.                             if (i + 1 < modules.length) modulelist += "<br>";
  174.                         }
  175.                     }
  176.                     s += modulelist + "</td></tr>";
  177.                 }
  178.             }
  179.             s += "</table></p></center>";
  180.             s += "</body></html>";
  181.             return s;
  182.         }
  183.  
  184.         /**
  185.          * This method is invoked when a link is pressed on your
  186.          * HTML-page.
  187.          * @param href The href-attribute of the link.
  188.          */
  189.         public void linkPressed(String href) {
  190.             String[] s = href.split(":");
  191.             if (s.length>=2) {
  192.                 /*  Ex: "button:PLAY"
  193.                     this means we should emulate the PLAY button. */
  194.                 if (s[0].equals("button")) {
  195.                     sendButtonNotification(s[1], "DOWN");
  196.                     sendButtonNotification(s[1], "UP");
  197.                 }
  198.             }
  199.         }
  200.     }
  201.  
  202.     /**
  203.      * Takes care of XML-data found in incoming packets.
  204.      */
  205.     private class PacketDataHandler implements XMLDataHandler {
  206.         /**
  207.          * Called when start of a new element is found in the XML-data.
  208.          * Ex: <name attr1="value1" attr2="value2">
  209.          * Element name would then be "name" and attribute list
  210.          * would contain "value1" and "value2" mapped to the attribute
  211.          * names "attr1" and "attr2".
  212.          * @param element The name of the element.
  213.          * @param attributes The element attributes.
  214.          */
  215.         public void XMLstartElement(String element, HashMap attributes) {
  216.             /* Ex: <arnepacket bytes="2">
  217.              * This is the start of an ARNE packet. We receive those from
  218.              * ARNE remote controls when buttons have been pressed on them. */
  219.             if (element.equals("arnepacket") && attributes.containsKey("bytes")) {
  220.                 int bytes = Integer.parseInt((String)attributes.get("bytes"));
  221.                 //we should always get three bytes from an ARNE remote control (1 status + 2 ident)
  222.                 if (bytes==3) {
  223.                     parsingARNEXML = true;
  224.                 } else {
  225.                     _errors.println("Invalid number of bytes in ARNE packet");
  226.                 }
  227.             }
  228.             /* Ex: <byte id="1" value="5" />
  229.              * This is a byte inside an ARNE packet. */
  230.             else if (element.equals("byte") && attributes.containsKey("id") && attributes.containsKey("value")) {
  231.                 if (parsingARNEXML) {
  232.                     short id = Short.parseShort(attributes.get("id").toString());
  233.                     //if id is valid
  234.                     if (id>0 && id<=3) {
  235.                         //remember byte
  236.                         receivedBytes[id-1] = UByte.parseUByte((String)attributes.get("value"));
  237.                     } else {
  238.                         _errors.println("Invalid byte IDs specified");
  239.                         parsingARNEXML = false;
  240.                     }
  241.                 } else {
  242.                     _errors.println("Byte-attribute found outside arnepacket-attribute");
  243.                 }
  244.             }
  245.             /* Ex: <buttonrequest button="NUMERIC4" module="RoomControl" />
  246.              * This is a request from another macbeth module about receiving
  247.              * notifications about one of our remote control buttons. We need
  248.              * to save all button requests in a list so we can notify these
  249.              * modules when the requested buttons are pressed. */
  250.             else if (element.equals("buttonrequest") && attributes.containsKey("button") && attributes.containsKey("module")) {
  251.                 String button = (String)attributes.get("button");
  252.                 String module = (String)attributes.get("module");
  253.                 //traverse all remote control buttons to find out whether this button is known
  254.                 boolean buttonKnown = false;
  255.                 Iterator it = remoteControlButtons.values().iterator();
  256.                 while (it.hasNext()) {
  257.                     String buttonName = (String)it.next();
  258.                     if (buttonName!=null){
  259.                         if (buttonName.equals(button)) {
  260.                             buttonKnown = true;
  261.                             break;
  262.                         }
  263.                     }
  264.                 }
  265.                 //if the button is known, lets remember this button request
  266.                 if (buttonKnown) {
  267.                     //has this button been requested before?
  268.                     if (buttonRequests.containsKey(button)) {
  269.                         //if so, get its previous requests
  270.                         ButtonRequests br = (ButtonRequests)buttonRequests.get(button);
  271.                         //and att this request as well
  272.                         br.addRequest(module);
  273.                         buttonRequests.put(button,br);
  274.                         //else, this is the first request for this button
  275.                     } else {
  276.                         //so create a new request object
  277.                         ButtonRequests br = new ButtonRequests();
  278.                         br.addRequest(module);
  279.                         buttonRequests.put(button,br);
  280.                     }
  281.                 }
  282.                 //else, if button was unknown
  283.                 else {
  284.                     _errors.println("The button '" + button + "' was requested by '" + module + "', but this " +
  285.                             "button is unknown! The request is denied");
  286.                 }
  287.             }
  288.         }
  289.  
  290.         /**
  291.          * Called when end of an element was found in the XML-data.
  292.          * Ex: </name> or <test attr="value" />
  293.          * @param element The name of the element.
  294.          */
  295.         public void XMLendElement(String element) {
  296.             /* Ex: </arnepacket>
  297.              * This is the end of an ARNE packet. This means we have now
  298.              * (probably) received an ARNE remote control button press. */
  299.             if (element.equals("arnepacket")) {
  300.                 if (parsingARNEXML) {
  301.                     parsingARNEXML = false;
  302.                     //handle our received bytes now
  303.                     handleBytes(receivedBytes);
  304.                 }
  305.             }
  306.         }
  307.  
  308.         public void XMLelementData(String data) {}
  309.         public void XMLdocumentStart() {}
  310.         public void XMLdocumentEnd() {}
  311.     }
  312. }
  313.