CM15A Plugin: Updated
Posted: Wed May 26, 2010 9:34 am
Update: I have a beta version ready, you can control device, but I have not gotten the recieve work yet.
download the attached zip and unzip the dll into your housebot plugin interfaces folder, restart Housebot.
I will post details soon (and the source).
"back to your regular scheduled programing"
I have been working on getting a CM15a hardware interface plugin.
My problem is I do not know CPP.
If someone could look this over and tell me why it is not working..
Thanks in advance.
download the attached zip and unzip the dll into your housebot plugin interfaces folder, restart Housebot.
I will post details soon (and the source).
"back to your regular scheduled programing"
I have been working on getting a CM15a hardware interface plugin.
My problem is I do not know CPP.
If someone could look this over and tell me why it is not working..
Code: Select all
#include "..\..\stdafx.h"
#include "TemplateInstance.h"
#include "..\..\Common\InterfaceAPI.h"
#include "..\..\Common\DataPack.h"
#include <ole2.h>
#include <objbase.h>
#include <wchar.h>
#include "..\..\ahscript.h"
const GUID CLSID_ActiveHome = {0x001000AF, 0x2DEF, 0x0208, {0x10, 0xB6, 0xDC, 0x5B, 0xA6, 0x92, 0xC8, 0x58}};
const GUID IID_IActiveHome = {0x001000AF, 0x3DEF, 0x0910, {0x10, 0xB6, 0xDC, 0x5B, 0xA6, 0x92, 0xC8, 0x58}};
const GUID IID__IActiveHomeEvents = {0x001000AF, 0x3DEF, 0x0912, {0x10, 0xB6, 0xDC, 0x5B, 0xA6, 0x92, 0xC8, 0x58}};
HRESULT __fastcall AnsiToUnicode(LPCSTR pszA, LPOLESTR *ppszW)
{
ULONG cCharacters;
DWORD dwError;
// If input is null then just return the same.
if (NULL == pszA)
{
ppszW = NULL;
return NOERROR;
}
// Determine number of wide characters to be allocated for the
// Unicode string.
cCharacters = strlen(pszA)+1;
// Use of the OLE allocator is required if the resultant Unicode
// string will be passed to another COM component and if that
// component will free it. Otherwise you can use your own allocator.
*ppszW = (LPOLESTR) CoTaskMemAlloc(cCharacters*2);
if (NULL == *ppszW)
return E_OUTOFMEMORY;
// Covert to Unicode.
if (0 == MultiByteToWideChar(CP_ACP, 0, pszA, cCharacters,
*ppszW, cCharacters))
{
dwError = GetLastError();
CoTaskMemFree(*ppszW);
*ppszW = NULL;
return HRESULT_FROM_WIN32(dwError);
}
return NOERROR;
}
/*
* UnicodeToAnsi converts the Unicode string pszW to an ANSI string
* and returns the ANSI string through ppszA. Space for the
* the converted string is allocated by UnicodeToAnsi.
*/
HRESULT __fastcall UnicodeToAnsi(LPCOLESTR pszW, LPSTR* ppszA)
{
ULONG cbAnsi, cCharacters;
DWORD dwError;
// If input is null then just return the same.
if (pszW == NULL)
{
*ppszA = NULL;
return NOERROR;
}
cCharacters = wcslen(pszW)+1;
// Determine number of bytes to be allocated for ANSI string. An
// ANSI string can have at most 2 bytes per character (for Double
// Byte Character Strings.)
cbAnsi = cCharacters*2;
// Use of the OLE allocator is not required because the resultant
// ANSI string will never be passed to another COM component. You
// can use your own allocator.
*ppszA = (LPSTR) CoTaskMemAlloc(cbAnsi);
if (NULL == *ppszA)
return E_OUTOFMEMORY;
// Convert to ANSI.
if (0 == WideCharToMultiByte(CP_ACP, 0, pszW, cCharacters, *ppszA,
cbAnsi, NULL, NULL))
{
dwError = GetLastError();
CoTaskMemFree(*ppszA);
*ppszA = NULL;
return HRESULT_FROM_WIN32(dwError);
}
return NOERROR;
}
// ====================================================================
// Method Name : CTemplateHardwareInstance
// Description : CTemplateHardwareInstance constructor
// Parameters : hInstance - Hardware Instance Handle passed by HBRegisterProperties()
// pCallBackInfo - Copy of callback info structure passed by HBModuleInit()
// ====================================================================
CTemplateHardwareInstance::CTemplateHardwareInstance( HARDWARE_INSTANCE_HANDLE hInstance, CallBackInfo* pCallBackInfo ) :
CHardwareInstance( hInstance, pCallBackInfo ),
m_bEnabled( FALSE )
{
hInstanceHandle = hInstance;
m_dwStartCount = GetTickCount();
}
// ====================================================================
// Method Name : RegisterProperties
// Description : This method is called by the server to allow the
// instance to register all of its properties.
// Returns : TRUE if successful. FALSE if unsuccessful.
// ====================================================================
BOOL CTemplateHardwareInstance::RegisterProperties()
{
//
// Register the properties.
const char* aValues[] = { "Yes", "No", NULL };
RegisterHardwareModuleProperty( "Enabled", ptToggle, aValues, "No", TRUE );
//
// Get the current state of the property values.
LPCTSTR szEnabled = GetPropertyValueFromName( "Enabled" );
if ((szEnabled) && (0 == strcmp( szEnabled, "Yes")))
{
m_bEnabled = TRUE;
}
else
{
m_bEnabled = FALSE;
}
//
// Add a button to test notifications
RegisterHardwareModuleButton( 2, "Subscription Notification", "Press to send a subscription notification to Devices" );
return( TRUE );
}
// ====================================================================
// Method Name : UpdatePropertyValue
// Description : This method is called by the server when the user
// changes a property value.
// Parameters : szPropertyName - Name of property that has changed.
// szNewValue - New value of the property.
// Returns : Returns TRUE if changed successfully. Returns FALSE if not changed successfully.
// ====================================================================
BOOL CTemplateHardwareInstance::UpdatePropertyValue( const char* szPropertyName, const char* szNewValue )
{
//
// See if it is the "Enabled" property.
if (0 == strcmpi( szPropertyName, "Enabled" ))
{
//
// Set our private member m_bEnabled based on the new value.
if (0 == strcmpi( szNewValue, "Yes" ))
{
m_bEnabled = TRUE;
}
else
{
m_bEnabled = FALSE;
}
}
return( TRUE );
}
// ====================================================================
// Method Name : PropertyButtonClick
// Description : This method is called by the server when the user
// presses a property button
// Parameters : nButtonCallbackID - Button ID passed in RegisterHardwareModuleButton()
// that identifies the button pressed.
// ====================================================================
BOOL CTemplateHardwareInstance::PropertyButtonClick( int nButtonCallbackID )
{
if (nButtonCallbackID == 1)
{
//
// Note: If compiled using MFC and a CDialog is envolked, you must make sure the
// MFC module state is correctly managed before creating the dialog. The comments
// below show how to do this.
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// CMainIRConfigurationDlg dlg( this );
// dlg.DoModal();
// return( TRUE );
//
// The user pressed a button. We will trace all of the server IR
// codes to the error log.
//
// Check to see how large a buffer we need for the IRCode array
DWORD dwBuffSize = 0;
if (GetIRCodes( NULL, &dwBuffSize ) &&
(dwBuffSize))
{
//
// Allocate the array memory
IRCode* pCodes = (IRCode*)malloc( dwBuffSize );
//
// Get fresh data from the server
if (GetIRCodes( pCodes, &dwBuffSize ))
{
//
// Codes were returned
for (int nLoop = 0; pCodes[ nLoop ].m_szDescription; nLoop++)
{
TraceMessage( ttError, "IR Code %d ID = %d; Description [%s]", nLoop, pCodes[ nLoop ].m_nCodeID, pCodes[ nLoop ].m_szDescription );
}
//
// Delete the server memory
free( pCodes );
}
else
{
TraceMessage( ttError, "No IR Codes were found on the server" );
}
}
}
else if (nButtonCallbackID == 2)
{
//
// Button 2 is the subscription notification test.
//
// Setup a DataPack with ANY kind of info we need to send along.
// CDataPacks is simply a container that manages multiple key/value types.
CDataPack DataPack;
DataPack.AddData( "HouseCode", "C" );
DataPack.AddData( "UnitCode", "1" );
DataPack.AddData( "Command", "Off" );
DataPack.AddData( "RepeatCount", "1" );
//
// If the data can be categorized somehow, it's best to specify the category type
// in the filter. For example, this is used in X10 interfaces to specify which HouseCode and
// UnitCode the data is for (H=%d;U=%d). This allows Devices to register to receive only data
// that is specific to their needs.
//
// This example will only be picked up by Devices that have registered for the
// "Sample Notification List 1" list with a "Module=1" filter.
NotifySubscribedDevices( "H=C", "U=1", &DataPack );
//
// This example will broadcast the data to all Devices that have subscribed to the
// "Sample Notification List Broadcast" list.
NotifySubscribedDevices( "X10 Broadcast", NULL, &DataPack );
}
else if (nButtonCallbackID == 3)
{
static int nDeviceNum = 0;
char szDeviceName[ 256 ];
wsprintf( szDeviceName, "Group Main| Group Sub | Dynamically Created Null Device %d", ++nDeviceNum );
// wsprintf( szDeviceName, "xAP|Bathroom|BathroomLight" );
//
// Button 3 will create a new device on the server.
if (FALSE == CreateDevice( "Null Device", szDeviceName, "This device was created from the Template Hardware Interface sample", hInstanceHandle ))
{
TraceMessage( ttError, "Unable to create the new devices. It may already exist." );
}
}
return( FALSE );
}
//
//
BOOL CTemplateHardwareInstance::Init(void)
{
int nReturn = 0;
HRESULT hr;
hr = CoInitialize(NULL);
//IActiveHome* pActiveHome;
hr = CoCreateInstance( CLSID_ActiveHome, NULL, CLSCTX_INPROC|CLSCTX_LOCAL_SERVER, IID_IActiveHome, (LPVOID *) &pActiveHome );
return (true);
}
void CTemplateHardwareInstance::Terminate()
{
pActiveHome->Release();
pActiveHome = NULL;
//CoUninitialize();
}
// ====================================================================
// Method Name : Query
// Description : This function allows a Device to query the interface for
// information. The query and response are pre-established
// format and types.
// Parameters : szQuery - Null terminated string that specifies the query.
// pResponse - Void pointer to a pre-established data type that
// will provide the response data from the query.
// Notes : This function is not meant as an ad-hoc method for
// interrogating the interface. It is only used for Devices
// to communicate with Interfaces that they know well.
// ====================================================================
BOOL CTemplateHardwareInstance::Query( LPCTSTR szQuery, void* pResponse )
{
//
// Check to see what the query is
if (0 == strcmpi( szQuery, "How Long Have You Been Running" ))
{
//
// The void data is actually a char* for this query.
wsprintf( (char*)pResponse, "Running Time: %d Seconds", (GetTickCount() - m_dwStartCount) / 1000 );
return( TRUE );
}
//
// We do not support the query specified.
return( FALSE );
}
HARDWARE_RC CTemplateHardwareInstance::InterfaceCall( InterfaceArgumentPack* pPack )
{
TraceMessage( ttError, "Call made to HBInterface(). Calling function %s", pPack->m_szInterfaceSignature );
::MessageBeep( MB_ICONHAND );
return( hrcSuccess );
}
/*
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//helper function for single byte char to unicode translatation
HRESULT AnsiToUnicode(LPCSTR pszA, LPOLESTR pszW) {
ULONG lALen;
ULONG lWLen;
DWORD dwError;
lALen = strlen(pszA)+11;
lWLen = strlen((char*)pszW) - 2;
if (0 == MultiByteToWideChar(CP_ACP, 0, pszA, lALen,
pszW, lWLen)) {
dwError = GetLastError();
return HRESULT_FROM_WIN32(dwError);
}
return NOERROR;
}
*/
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Template specific exports
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
HARDWARE_RC CTemplateHardwareInstance::SendX10( InterfaceArgumentPack* pPack )
{
//
// Make sure we are enabled.
if (!m_bEnabled)
{
TraceMessage( ttWarning, "SendX10() interface is disabled." );
return( hrcFailureAbort );
}
//
// Verify the interface argument pack.
//
// Make sure we can handle the version of the struct.
if (pPack->m_nArgumentPackVersion != 1)
{
TraceMessage( ttError, "Unsupported Argument Pack Version [%s]. Must be version [%d]", pPack->m_nArgumentPackVersion, 1 );
return( hrcFailureAbort );
}
//
// Make sure there are enough arguments.
if (pPack->m_nNumberOfArguments != 4)
{
TraceMessage( ttError, "Invalid number of arguments supplied to SendX10() interface. Expected 4. Received %d", pPack->m_nNumberOfArguments );
return( hrcFailureAbort );
}
HARDWARE_RC hresult = hrcSuccess;
//
// Get the Values
const char* szHouseCode = TextFromInterfaceArgument( &(pPack->m_aArguments)[ 0 ] );
const char* szUnitCode = TextFromInterfaceArgument( &(pPack->m_aArguments)[ 1 ] );
const char* szCommand = TextFromInterfaceArgument( &(pPack->m_aArguments)[ 2 ] );
const char* szRepeatCount = TextFromInterfaceArgument( &(pPack->m_aArguments)[ 3 ] );
char* chTemp = new char[20];
wchar_t* wchTemp = L"sendplc"; // command to send
char* chParm = new char[20];
wchar_t* wchParm = new wchar_t[50]; // HouseCodeUnitCode Cmd (A1 On)
if (chParm && wchParm && chTemp && wchTemp) {
//concatenate all parameters into one parameter string
VARIANT varReseved1;
VariantInit(&varReseved1);
VARIANT varReseved2;
VariantInit(&varReseved2);
VARIANT varAction;
VariantInit(&varAction);
varAction.vt = VT_BSTR;
VARIANT varParm;
VariantInit(&varParm);
varParm.vt = VT_BSTR;
sprintf(chParm, " %s%s %s\n", szHouseCode, szUnitCode, szCommand);
AnsiToUnicode(chParm, &wchParm);
varAction.bstrVal = ::SysAllocString(wchTemp);
varParm.bstrVal = ::SysAllocString(wchParm);
// Grab Data to send to Trace
UnicodeToAnsi(varAction.bstrVal, &chTemp);
UnicodeToAnsi(varParm.bstrVal, &chParm);
TraceMessage( ttDebug, "SendX10 varAction=%s, varParm=%s", chTemp, chParm);
VARIANT varReturn;
HRESULT hr;
hr = pActiveHome->SendAction(varAction, varParm, varReseved1, varReseved2, &varReturn);
if (FAILED(hr)) {
TraceMessage( ttError, "error: SendAction hr=0x%x\n", (unsigned int)hr);
}
SysFreeString(varAction.bstrVal);
SysFreeString(varParm.bstrVal);
}
delete [] chTemp;
delete [] chParm;
return( hresult );
}