//----------------------------------------------------------------------------- // // 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 // // // 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 . // //----------------------------------------------------------------------------- #include #include #include #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" 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 m_values; }NodeInfo; static list g_nodes; static pthread_mutex_t g_criticalSection; static pthread_cond_t initCond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; //----------------------------------------------------------------------------- // // 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::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; } //----------------------------------------------------------------------------- // // 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::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::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 ); } //----------------------------------------------------------------------------- //
// 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::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; for( list::iterator it2 = nodeInfo->m_values.begin(); it2 != nodeInfo->m_values.end(); ++it2 ) { ValueID v = *it2; if( v.GetCommandClassId() == 0x20 ) { // 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; }