FYI, it seems there was a bug in the .31 that the child devices data parameters weren't populated correctly. That was fixed, and the new code snippet I gave you will be included in .32. Also, I just talked to dan.t and he will try to wrap up integrating all cm11a issues this week so we get a 100% functional cm11a in the .32 release.
> So how do we propagate the incoming X-10 status
It seems the current cm11a doesn't do this (ie doesn't yet address incoming status changes). However this is implemented in a lot of other devices. To do this you need to create a separate thread that will fire the events. The main thread is created automatically by the DCE framework, and is woken up to call the appropriate functions for handling incoming messages: ie ReceivedCommandForChild, or one of the CMD_ functions for commands sent directly to your device. The rest of the time that thread is blocked on a select() waiting for incoming messages. NOTE: DCE connects to the router twice, 1 socket is for incoming messages, the other for outgoing. See
http://plutohome.com/support/right.php?section=documents/documentDisplay&docID=286 for a tutorial showing how messages work.
Normally in such cases you would create something like a map<string,DeviceData_Impl *> m_mapX10HouseCodes which will map X10 house codes back to devices. You don't need this for sending x10 commands--in such cases you already get a pointer to the device in 'ReceivedCommandForChild' and that has the x10 house code. But for the reverse, when you get a state change from x10 you will want a fast way to find out what device corresponds to a house code without having to iterate through all your child devices each time looking for it. So in the GetConfig() function, which is called automatically when the device starts up and is where you should put initialization functions, you would put an iterator that goes through all child devices, gets the house code, and stores this in that map for a reverse lookup.
So you need another thread that, depending on how the cm11a works, will either poll it for incoming events, or will block waiting for incoming data on a serial port/socket, etc. When you get incoming data and have an event you want to fire, lookup the device with that house code as explained above, and send an event message *From* that device. You can create a message and send it to the router:
Message *pMessage_Event = new Message(PK_Device_X10,DEVICEID_EVENTMANAGER,PRIORITY_NORMAL,MESSAGETYPE_EVENT,EVENT_New_PNP_Device_Detected_CONST,
1,EVENTPARAMETER_PK_Device_CONST,StringUtils::itos(PK_Device).c_str());
QueueMessageToRouter(pMessage_Event);
The above sends a message of type event, with the event id "New PNP device detected", and 1 parameter "PK_Device". Note that for a message of type event, the id is the primary key in the pluto_main table Event, and for command the table Command. If it's an event, the parameters are from table EventParameter, if it's a command, from table CommandParameter. The sql2cpp utility automatically creates header files that have #define's with all the constants. So whenever you see a [TABLE_NAME]_[Description]_CONST, like EVENTPARAMETER_PK_Device_CONST, that is defined the file: #include "pluto_main/Define_EventParameter.h"
Also, DCE automatically creates helper functions to automatically form commands and events. If you have a compiler with auto-complete (I use VisualStudio.net), type DCE:: and then it will give you a list of all the commands pluto knows, and auto-complete will automatically give you a constructor that takes all the command parameters, telling you the names and types. Use 'SendCommand' to send a command created this way, like this:
DCE::CMD_Set_Text CMD_Set_Text(m_dwPK_Device,pMessage->m_dwPK_Device_From,"",pLastApplication->m_sName,TEXT_STATUS_CONST);
SendCommand(CMD_Set_Text);
DCEGen also automatically creates helper functions to send events. In the device template for your device add whatever events your device will fire. Then when you re-run DCEGen, it will add member functions to your base classe to automatically fire events, like so:
//<-dceag-h-b->
/*
AUTO-GENERATED SECTION
Do not change the declarations
*/
....
*****DATA***** accessors inherited from base class
string DATA_Get_Path();
int DATA_Get_PK_Users();
....
*****EVENT***** accessors inherited from base class
void EVENT_Touch_or_click(int iX_Position,int iY_Position);
...
So if you want to fire a sensor triggered event, you can either create the message manually (ie new Message( ... EVENT_Sensor_Triggered_CONST )...) and send it with QueueMessageToRouter, or in the device template page in the pluto admin site add the 'sensor triggered' event, and then call the helper function to automatically create the event message and send it:
EVENT_Sensor_Triggered(); Using the pre-built event functions sends the events from yourself (ie cm11a) so for devices like this where you send the events from another device (ie your child sensor) you will send the message using QueueMessageToRouter so you can specify the from device.
With that being done, your device will send events. There are 2 ways these events can be processed. Firstly, another device may intercept them. Security_Plugin is responsible for all the logic of implementing a home security system. Note this:
bool Security_Plugin::Register()
....
RegisterMsgInterceptor((MessageInterceptorFn)(&Security_Plugin::SensorTrippedEvent) ,0,0,0,0,MESSAGETYPE_EVENT,EVENT_Sensor_Tripped_CONST);
That means any time anybody sends a sensor tripped event, the member function SensorTrippedEvent() event will be called and passed a copy of the message. Security_Plugin has all the logic of determining whether the sensor that fired the event is part of the alarm system, whether the sensor is active/bypassed, and when it's time to sound the alarm. That plain text dce link above also includes an example of registering a message interceptor by hand using a socket.
The second way of responding to events is to create an event handler in pluto admin in wizard, events, respond to events. Here is how an end-user says 'when x event comes in, do y'. Pick 'Sensor is tripped' from the pull-down, choose the sensor device, and pick the commands. Then whenever that sensor is tripped, those commands will get executed.
You could also easily add your own events: In the device template choose 'add new event' and create an event 'Coffee Ready'. Then in your cm11a, wherever is the code that handles incoming messages, you would lookup the device that fired the event in m_mapX10HouseCodes and then check the device template. If it's your new 'Coffee Ready' device, send the 'Coffee Ready' event instead of the general purpose 'Sensor Tripped'. Then the user can go in pluto admin to 'respond to events' and pick 'coffee ready' and say what they want to happen in such a case.
Since the device templates are shared across all devices, this is why it may be a good idea to put that 'translation logic' in a separate c++ class that all interfaces devices share. This class could have a 'TranslateIncomingEvent' function that does a:
if( devicetemplate==coffeemaker )
event==coffee ready
And then all interface devices, cm11a, Lutron, gc100, etc., could call that common function and you wouldn't need to build that logic into each one. I will talk to daniel t about doing that so one person one time adds a new 'coffe maker' device and an entry in the translation class, and then every automation interface will know what event to fire when a device 'coffee maker' changes state.