398 lines
12 KiB
C++
398 lines
12 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// Main.cpp
|
|
//
|
|
// Minimal application to test OpenZWave.
|
|
//
|
|
// Creates an OpenZWave::Driver and the waits. In Debug builds
|
|
// you should see verbose logging to the console, which will
|
|
// indicate that communications with the Z-Wave network are working.
|
|
//
|
|
// Copyright (c) 2010 Mal Lansell <mal@openzwave.com>
|
|
//
|
|
//
|
|
// SOFTWARE NOTICE AND LICENSE
|
|
//
|
|
// This file is part of OpenZWave.
|
|
//
|
|
// OpenZWave is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License,
|
|
// or (at your option) any later version.
|
|
//
|
|
// OpenZWave is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with OpenZWave. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include "Options.h"
|
|
#include "Manager.h"
|
|
#include "Driver.h"
|
|
#include "Node.h"
|
|
#include "Group.h"
|
|
#include "Notification.h"
|
|
#include "value_classes/ValueStore.h"
|
|
#include "value_classes/Value.h"
|
|
#include "value_classes/ValueBool.h"
|
|
#include "platform/Log.h"
|
|
#include "Defs.h"
|
|
|
|
using namespace OpenZWave;
|
|
|
|
bool temp = false;
|
|
|
|
|
|
static uint32 g_homeId = 0;
|
|
static bool g_initFailed = false;
|
|
|
|
typedef struct
|
|
{
|
|
uint32 m_homeId;
|
|
uint8 m_nodeId;
|
|
bool m_polled;
|
|
list<ValueID> m_values;
|
|
}NodeInfo;
|
|
|
|
static list<NodeInfo*> g_nodes;
|
|
static pthread_mutex_t g_criticalSection;
|
|
static pthread_cond_t initCond = PTHREAD_COND_INITIALIZER;
|
|
static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// <GetNodeInfo>
|
|
// Return the NodeInfo object associated with this notification
|
|
//-----------------------------------------------------------------------------
|
|
NodeInfo* GetNodeInfo
|
|
(
|
|
Notification const* _notification
|
|
)
|
|
{
|
|
uint32 const homeId = _notification->GetHomeId();
|
|
uint8 const nodeId = _notification->GetNodeId();
|
|
for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
|
|
{
|
|
NodeInfo* nodeInfo = *it;
|
|
if( ( nodeInfo->m_homeId == homeId ) && ( nodeInfo->m_nodeId == nodeId ) )
|
|
{
|
|
return nodeInfo;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// <OnNotification>
|
|
// Callback that is triggered when a value, group or node changes
|
|
//-----------------------------------------------------------------------------
|
|
void OnNotification
|
|
(
|
|
Notification const* _notification,
|
|
void* _context
|
|
)
|
|
{
|
|
// Must do this inside a critical section to avoid conflicts with the main thread
|
|
pthread_mutex_lock( &g_criticalSection );
|
|
|
|
switch( _notification->GetType() )
|
|
{
|
|
case Notification::Type_ValueAdded:
|
|
{
|
|
if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
|
|
{
|
|
// Add the new value to our list
|
|
nodeInfo->m_values.push_back( _notification->GetValueID() );
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_ValueRemoved:
|
|
{
|
|
if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
|
|
{
|
|
// Remove the value from out list
|
|
for( list<ValueID>::iterator it = nodeInfo->m_values.begin(); it != nodeInfo->m_values.end(); ++it )
|
|
{
|
|
if( (*it) == _notification->GetValueID() )
|
|
{
|
|
nodeInfo->m_values.erase( it );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_ValueChanged:
|
|
{
|
|
// One of the node values has changed
|
|
if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
|
|
{
|
|
nodeInfo = nodeInfo; // placeholder for real action
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_Group:
|
|
{
|
|
// One of the node's association groups has changed
|
|
if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
|
|
{
|
|
nodeInfo = nodeInfo; // placeholder for real action
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_NodeAdded:
|
|
{
|
|
// Add the new node to our list
|
|
NodeInfo* nodeInfo = new NodeInfo();
|
|
nodeInfo->m_homeId = _notification->GetHomeId();
|
|
nodeInfo->m_nodeId = _notification->GetNodeId();
|
|
nodeInfo->m_polled = false;
|
|
g_nodes.push_back( nodeInfo );
|
|
if (temp == true) {
|
|
Manager::Get()->CancelControllerCommand( _notification->GetHomeId() );
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_NodeRemoved:
|
|
{
|
|
// Remove the node from our list
|
|
uint32 const homeId = _notification->GetHomeId();
|
|
uint8 const nodeId = _notification->GetNodeId();
|
|
for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
|
|
{
|
|
NodeInfo* nodeInfo = *it;
|
|
if( ( nodeInfo->m_homeId == homeId ) && ( nodeInfo->m_nodeId == nodeId ) )
|
|
{
|
|
g_nodes.erase( it );
|
|
delete nodeInfo;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_NodeEvent:
|
|
{
|
|
// We have received an event from the node, caused by a
|
|
// basic_set or hail message.
|
|
if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
|
|
{
|
|
nodeInfo = nodeInfo; // placeholder for real action
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_PollingDisabled:
|
|
{
|
|
if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
|
|
{
|
|
nodeInfo->m_polled = false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_PollingEnabled:
|
|
{
|
|
if( NodeInfo* nodeInfo = GetNodeInfo( _notification ) )
|
|
{
|
|
nodeInfo->m_polled = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_DriverReady:
|
|
{
|
|
g_homeId = _notification->GetHomeId();
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_DriverFailed:
|
|
{
|
|
g_initFailed = true;
|
|
pthread_cond_broadcast(&initCond);
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_AwakeNodesQueried:
|
|
case Notification::Type_AllNodesQueried:
|
|
case Notification::Type_AllNodesQueriedSomeDead:
|
|
{
|
|
pthread_cond_broadcast(&initCond);
|
|
break;
|
|
}
|
|
|
|
case Notification::Type_DriverReset:
|
|
case Notification::Type_Notification:
|
|
case Notification::Type_NodeNaming:
|
|
case Notification::Type_NodeProtocolInfo:
|
|
case Notification::Type_NodeQueriesComplete:
|
|
default:
|
|
{
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock( &g_criticalSection );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// <main>
|
|
// Create the driver and then wait
|
|
//-----------------------------------------------------------------------------
|
|
int main( int argc, char* argv[] )
|
|
{
|
|
pthread_mutexattr_t mutexattr;
|
|
|
|
pthread_mutexattr_init ( &mutexattr );
|
|
pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE );
|
|
pthread_mutex_init( &g_criticalSection, &mutexattr );
|
|
pthread_mutexattr_destroy( &mutexattr );
|
|
|
|
pthread_mutex_lock( &initMutex );
|
|
|
|
|
|
printf("Starting MinOZW with OpenZWave Version %s\n", Manager::getVersionAsString().c_str());
|
|
|
|
// Create the OpenZWave Manager.
|
|
// The first argument is the path to the config files (where the manufacturer_specific.xml file is located
|
|
// The second argument is the path for saved Z-Wave network state and the log file. If you leave it NULL
|
|
// the log file will appear in the program's working directory.
|
|
Options::Create( "../../../config/", "", "" );
|
|
Options::Get()->AddOptionInt( "SaveLogLevel", LogLevel_Detail );
|
|
Options::Get()->AddOptionInt( "QueueLogLevel", LogLevel_Debug );
|
|
Options::Get()->AddOptionInt( "DumpTrigger", LogLevel_Error );
|
|
Options::Get()->AddOptionInt( "PollInterval", 500 );
|
|
Options::Get()->AddOptionBool( "IntervalBetweenPolls", true );
|
|
Options::Get()->AddOptionBool("ValidateValueChanges", true);
|
|
Options::Get()->Lock();
|
|
|
|
Manager::Create();
|
|
|
|
// Add a callback handler to the manager. The second argument is a context that
|
|
// is passed to the OnNotification method. If the OnNotification is a method of
|
|
// a class, the context would usually be a pointer to that class object, to
|
|
// avoid the need for the notification handler to be a static.
|
|
Manager::Get()->AddWatcher( OnNotification, NULL );
|
|
|
|
// Add a Z-Wave Driver
|
|
// Modify this line to set the correct serial port for your PC interface.
|
|
|
|
#ifdef DARWIN
|
|
string port = "/dev/cu.usbserial";
|
|
#elif WIN32
|
|
string port = "\\\\.\\COM6";
|
|
#else
|
|
string port = "/dev/ttyUSB0";
|
|
#endif
|
|
if ( argc > 1 )
|
|
{
|
|
port = argv[1];
|
|
}
|
|
if( strcasecmp( port.c_str(), "usb" ) == 0 )
|
|
{
|
|
Manager::Get()->AddDriver( "HID Controller", Driver::ControllerInterface_Hid );
|
|
}
|
|
else
|
|
{
|
|
Manager::Get()->AddDriver( port );
|
|
}
|
|
|
|
// Now we just wait for either the AwakeNodesQueried or AllNodesQueried notification,
|
|
// then write out the config file.
|
|
// In a normal app, we would be handling notifications and building a UI for the user.
|
|
pthread_cond_wait( &initCond, &initMutex );
|
|
|
|
// Since the configuration file contains command class information that is only
|
|
// known after the nodes on the network are queried, wait until all of the nodes
|
|
// on the network have been queried (at least the "listening" ones) before
|
|
// writing the configuration file. (Maybe write again after sleeping nodes have
|
|
// been queried as well.)
|
|
if( !g_initFailed )
|
|
{
|
|
|
|
// The section below demonstrates setting up polling for a variable. In this simple
|
|
// example, it has been hardwired to poll COMMAND_CLASS_BASIC on the each node that
|
|
// supports this setting.
|
|
pthread_mutex_lock( &g_criticalSection );
|
|
for( list<NodeInfo*>::iterator it = g_nodes.begin(); it != g_nodes.end(); ++it )
|
|
{
|
|
NodeInfo* nodeInfo = *it;
|
|
|
|
// skip the controller (most likely node 1)
|
|
if( nodeInfo->m_nodeId == 1) continue;
|
|
|
|
printf("NodeID: %d \n ", nodeInfo->m_nodeId);
|
|
printf("\t NodeName: %s \n ", Manager::Get()->GetNodeName(nodeInfo->m_homeId,nodeInfo->m_nodeId).c_str());
|
|
printf("\t ManufacturerName: %s \n ", Manager::Get()->GetNodeManufacturerName(nodeInfo->m_homeId,nodeInfo->m_nodeId).c_str());
|
|
printf("\t NodeProductName: %s \n ", Manager::Get()->GetNodeProductName(nodeInfo->m_homeId,nodeInfo->m_nodeId).c_str());
|
|
|
|
printf("Values announced by the nodes without polling: \n");
|
|
for( list<ValueID>::iterator it2 = nodeInfo->m_values.begin(); it2 != nodeInfo->m_values.end(); ++it2 )
|
|
{
|
|
ValueID v = *it2;
|
|
printf("\t ValueLabel: %s \n", Manager::Get()->GetValueLabel(v).c_str());
|
|
printf("\t\t ValueType: %d \n", v.GetType());
|
|
printf("\t\t ValueHelp: %s \n", Manager::Get()->GetValueHelp(v).c_str());
|
|
printf("\t\t ValueUnits: %s \n", Manager::Get()->GetValueUnits(v).c_str());
|
|
printf("\t\t ValueMin: %d \n", Manager::Get()->GetValueMin(v));
|
|
printf("\t\t ValueMax: %d \n", Manager::Get()->GetValueMax(v));
|
|
|
|
if( v.GetCommandClassId() == COMMAND_CLASS_BASIC )
|
|
{
|
|
// Manager::Get()->EnablePoll( v, 2 ); // enables polling with "intensity" of 2, though this is irrelevant with only one value polled
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pthread_mutex_unlock( &g_criticalSection );
|
|
|
|
// If we want to access our NodeInfo list, that has been built from all the
|
|
// notification callbacks we received from the library, we have to do so
|
|
// from inside a Critical Section. This is because the callbacks occur on other
|
|
// threads, and we cannot risk the list being changed while we are using it.
|
|
// We must hold the critical section for as short a time as possible, to avoid
|
|
// stalling the OpenZWave drivers.
|
|
// At this point, the program just waits for 3 minutes (to demonstrate polling),
|
|
// then exits
|
|
for( int i = 0; i < 60*3; i++ )
|
|
{
|
|
pthread_mutex_lock( &g_criticalSection );
|
|
// but NodeInfo list and similar data should be inside critical section
|
|
pthread_mutex_unlock( &g_criticalSection );
|
|
sleep(1);
|
|
}
|
|
|
|
Driver::DriverData data;
|
|
Manager::Get()->GetDriverStatistics( g_homeId, &data );
|
|
printf("SOF: %d ACK Waiting: %d Read Aborts: %d Bad Checksums: %d\n", data.m_SOFCnt, data.m_ACKWaiting, data.m_readAborts, data.m_badChecksum);
|
|
printf("Reads: %d Writes: %d CAN: %d NAK: %d ACK: %d Out of Frame: %d\n", data.m_readCnt, data.m_writeCnt, data.m_CANCnt, data.m_NAKCnt, data.m_ACKCnt, data.m_OOFCnt);
|
|
printf("Dropped: %d Retries: %d\n", data.m_dropped, data.m_retries);
|
|
}
|
|
|
|
// program exit (clean up)
|
|
if( strcasecmp( port.c_str(), "usb" ) == 0 )
|
|
{
|
|
Manager::Get()->RemoveDriver( "HID Controller" );
|
|
}
|
|
else
|
|
{
|
|
Manager::Get()->RemoveDriver( port );
|
|
}
|
|
Manager::Get()->RemoveWatcher( OnNotification, NULL );
|
|
Manager::Destroy();
|
|
Options::Destroy();
|
|
pthread_mutex_destroy( &g_criticalSection );
|
|
return 0;
|
|
}
|