Subversion Repositories HomeAutomation

Rev

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

Rev Author Line No. Line
15 arune 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
}