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 );
}