1 //-----------------------------------------------------------------------------
2 //
3 //	MultiInstance.cpp
4 //
5 //	Implementation of the Z-Wave COMMAND_CLASS_MULTI_INSTANCE
6 //
7 //	Copyright (c) 2010 Mal Lansell <openzwave@lansell.org>
8 //
9 //	SOFTWARE NOTICE AND LICENSE
10 //
11 //	This file is part of OpenZWave.
12 //
13 //	OpenZWave is free software: you can redistribute it and/or modify
14 //	it under the terms of the GNU Lesser General Public License as published
15 //	by the Free Software Foundation, either version 3 of the License,
16 //	or (at your option) any later version.
17 //
18 //	OpenZWave is distributed in the hope that it will be useful,
19 //	but WITHOUT ANY WARRANTY; without even the implied warranty of
20 //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 //	GNU Lesser General Public License for more details.
22 //
23 //	You should have received a copy of the GNU Lesser General Public License
24 //	along with OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
25 //
26 //-----------------------------------------------------------------------------
27 
28 #include "tinyxml.h"
29 #include "command_classes/CommandClasses.h"
30 #include "command_classes/Basic.h"
31 #include "command_classes/MultiInstance.h"
32 #include "command_classes/NoOperation.h"
33 #include "Defs.h"
34 #include "Msg.h"
35 #include "Driver.h"
36 #include "Node.h"
37 #include "platform/Log.h"
38 
39 using namespace OpenZWave;
40 
41 // Reduced set of Generic Device classes sorted to reduce
42 // the likely number of calls to MultiChannelCmd_EndPointFind.
43 uint8 const c_genericClass[] =
44 {
45 		0x21,		// Multilevel Sensor
46 		0x20,		// Binary Sensor
47 		0x31,		// Meter
48 		0x08,		// Thermostat
49 		0x11,		// Multilevel Switch
50 		0x10,		// Binary Switch
51 		0x12,		// Remote Switch
52 		0xa1,		// Alarm Sensor
53 		0x16,		// Ventilation
54 		0x30,		// Pulse Meter
55 		0x40,		// Entry Control
56 		0x13,		// Toggle Switch
57 		0x03,		// AV Control Point
58 		0x04,		// Display
59 		0x00		// End of list
60 };
61 
62 char const* c_genericClassName[] =
63 {
64 		"Multilevel Sensor",
65 		"Binary Sensor",
66 		"Meter",
67 		"Thermostat",
68 		"Multilevel Switch",
69 		"Binary Switch",
70 		"Remote Switch",
71 		"Alarm Sensor",
72 		"Ventilation",
73 		"Pulse Meter",
74 		"Entry Control",
75 		"Toggle Switch",
76 		"AV Control Point",
77 		"Display",
78 		"Unknown"
79 };
80 
81 //-----------------------------------------------------------------------------
82 // <MultiInstance::MultiInstance>
83 // Constructor
84 //-----------------------------------------------------------------------------
MultiInstance(uint32 const _homeId,uint8 const _nodeId)85 MultiInstance::MultiInstance
86 (
87 		uint32 const _homeId,
88 		uint8 const _nodeId
89 ):
90 CommandClass( _homeId, _nodeId ),
91 m_numEndPoints( 0 ),
92 m_numEndPointsHint( 0 ),
93 m_endPointMap( MultiInstanceMapAll ),
94 m_endPointFindSupported( false ),
95 m_uniqueendpoints( false )
96 {
97 }
98 
99 //-----------------------------------------------------------------------------
100 // <MultiInstance::ReadXML>
101 // Class specific configuration
102 //-----------------------------------------------------------------------------
ReadXML(TiXmlElement const * _ccElement)103 void MultiInstance::ReadXML
104 (
105 		TiXmlElement const* _ccElement
106 )
107 {
108 	int32 intVal;
109 	char const* str;
110 
111 	CommandClass::ReadXML( _ccElement );
112 
113 	if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "endpoints", &intVal ) )
114 	{
115 		m_numEndPointsHint = (uint8)intVal;
116 	}
117 
118 	str = _ccElement->Attribute("mapping");
119 	if( str )
120 	{
121 		if( strcmp( str, "all") == 0 )
122 		{
123 			m_endPointMap = MultiInstanceMapAll;
124 		}
125 		else if( strcmp( str, "endpoints") == 0 )
126 		{
127 			m_endPointMap = MultiInstanceMapEndPoints;
128 		}
129 		else
130 		{
131 			Log::Write( LogLevel_Info, GetNodeId(), "Bad value for mapping: %s", str);
132 		}
133 	}
134 
135 	str = _ccElement->Attribute("findsupport");
136 	if( str )
137 	{
138 		m_endPointFindSupported = !strcmp( str, "true");
139 	}
140 	str = _ccElement->Attribute("ignoreUnsolicitedMultiChnCapReport");
141 	if( str )
142 	{
143 		m_ignoreUnsolicitedMultiChannelCapabilityReport = !strcmp( str, "true");
144 	}
145 	str = _ccElement->Attribute("forceUniqueEndpoints");
146 	if( str )
147 	{
148 		m_uniqueendpoints = !strcmp( str, "true");
149 	}
150 }
151 
152 //-----------------------------------------------------------------------------
153 // <MultiInstance::WriteXML>
154 // Class specific configuration
155 //-----------------------------------------------------------------------------
WriteXML(TiXmlElement * _ccElement)156 void MultiInstance::WriteXML
157 (
158 		TiXmlElement* _ccElement
159 )
160 {
161 	char str[32];
162 
163 	CommandClass::WriteXML( _ccElement );
164 	if( m_numEndPointsHint != 0 )
165 	{
166 		snprintf( str, sizeof(str), "%d", m_numEndPointsHint );
167 		_ccElement->SetAttribute( "endpoints", str);
168 	}
169 
170 	if( m_endPointMap == MultiInstanceMapEndPoints )
171 	{
172 		_ccElement->SetAttribute( "mapping", "endpoints" );
173 	}
174 
175 	if( m_endPointFindSupported )
176 	{
177 		_ccElement->SetAttribute( "findsupport", "true" );
178 	}
179 
180 	if( m_uniqueendpoints )
181 	{
182 		_ccElement->SetAttribute( "forceUniqueEndpoints", "true" );
183 	}
184 
185 }
186 
187 //-----------------------------------------------------------------------------
188 // <MultiInstance::RequestInstances>
189 // Request number of instances of the specified command class from the device
190 //-----------------------------------------------------------------------------
RequestInstances()191 bool MultiInstance::RequestInstances
192 (
193 )
194 {
195 	bool res = false;
196 
197 	if( GetVersion() == 1 )
198 	{
199 		if( Node* node = GetNodeUnsafe() )
200 		{
201 			// MULTI_INSTANCE
202 			for( map<uint8,CommandClass*>::const_iterator it = node->m_commandClassMap.begin(); it != node->m_commandClassMap.end(); ++it )
203 			{
204 				CommandClass* cc = it->second;
205 				if( cc->GetCommandClassId() == NoOperation::StaticGetCommandClassId() )
206 				{
207 					continue;
208 				}
209 				if( cc->HasStaticRequest( StaticRequest_Instances ) )
210 				{
211 					Log::Write( LogLevel_Info, GetNodeId(), "MultiInstanceCmd_Get for %s", cc->GetCommandClassName().c_str() );
212 
213 					Msg* msg = new Msg( "MultiInstanceCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
214 					msg->Append( GetNodeId() );
215 					msg->Append( 3 );
216 					msg->Append( GetCommandClassId() );
217 					msg->Append( MultiInstanceCmd_Get );
218 					msg->Append( cc->GetCommandClassId() );
219 					msg->Append( GetDriver()->GetTransmitOptions() );
220 					GetDriver()->SendMsg( msg, Driver::MsgQueue_Query );
221 					res = true;
222 				}
223 			}
224 		}
225 	}
226 	else
227 	{
228 		// MULTI_CHANNEL
229 
230 		Log::Write( LogLevel_Info, GetNodeId(), "MultiChannelCmd_EndPointGet for node %d", GetNodeId() );
231 
232 		Msg* msg = new Msg( "MultiChannelCmd_EndPointGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
233 		msg->Append( GetNodeId() );
234 		msg->Append( 2 );
235 		msg->Append( GetCommandClassId() );
236 		msg->Append( MultiChannelCmd_EndPointGet );
237 		msg->Append( GetDriver()->GetTransmitOptions() );
238 		GetDriver()->SendMsg( msg, Driver::MsgQueue_Query );
239 		res = true;
240 	}
241 
242 	return res;
243 }
244 
245 //-----------------------------------------------------------------------------
246 // <MultiInstance::HandleMsg>
247 // Handle a message from the Z-Wave network
248 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)249 bool MultiInstance::HandleMsg
250 (
251 		uint8 const* _data,
252 		uint32 const _length,
253 		uint32 const _instance	// = 1
254 )
255 {
256 	bool handled = false;
257 	Node* node = GetNodeUnsafe();
258 	if( node != NULL )
259 	{
260 		handled = true;
261 		switch( (MultiInstanceCmd)_data[0] )
262 		{
263 			case MultiInstanceCmd_Report:
264 			{
265 				HandleMultiInstanceReport( _data, _length );
266 				break;
267 			}
268 			case MultiInstanceCmd_Encap:
269 			{
270 				HandleMultiInstanceEncap( _data, _length );
271 				break;
272 			}
273 			case MultiChannelCmd_EndPointReport:
274 			{
275 				HandleMultiChannelEndPointReport( _data, _length );
276 				break;
277 			}
278 			case MultiChannelCmd_CapabilityReport:
279 			{
280 				HandleMultiChannelCapabilityReport( _data, _length );
281 				break;
282 			}
283 			case MultiChannelCmd_EndPointFindReport:
284 			{
285 				HandleMultiChannelEndPointFindReport( _data, _length );
286 				break;
287 			}
288 			case MultiChannelCmd_Encap:
289 			{
290 				HandleMultiChannelEncap( _data, _length );
291 				break;
292 			}
293 			default:
294 			{
295 				handled = false;
296 				break;
297 			}
298 		}
299 	}
300 
301 	return handled;
302 }
303 
304 //-----------------------------------------------------------------------------
305 // <MultiInstance::HandleMultiInstanceReport>
306 // Handle a message from the Z-Wave network
307 //-----------------------------------------------------------------------------
HandleMultiInstanceReport(uint8 const * _data,uint32 const _length)308 void MultiInstance::HandleMultiInstanceReport
309 (
310 		uint8 const* _data,
311 		uint32 const _length
312 )
313 {
314 	if( Node* node = GetNodeUnsafe() )
315 	{
316 		uint8 commandClassId = _data[1];
317 		uint8 instances = _data[2];
318 
319 		if( CommandClass* pCommandClass = node->GetCommandClass( commandClassId ) )
320 		{
321 			Log::Write( LogLevel_Info, GetNodeId(), "Received MultiInstanceReport from node %d for %s: Number of instances = %d", GetNodeId(), pCommandClass->GetCommandClassName().c_str(), instances );
322 			pCommandClass->SetInstances( instances );
323 			pCommandClass->ClearStaticRequest( StaticRequest_Instances );
324 		}
325 	}
326 }
327 
328 //-----------------------------------------------------------------------------
329 // <MultiInstance::HandleMultiInstanceEncap>
330 // Handle a message from the Z-Wave network
331 //-----------------------------------------------------------------------------
HandleMultiInstanceEncap(uint8 const * _data,uint32 const _length)332 void MultiInstance::HandleMultiInstanceEncap
333 (
334 		uint8 const* _data,
335 		uint32 const _length
336 )
337 {
338 	if( Node* node = GetNodeUnsafe() )
339 	{
340 		uint8 instance = _data[1];
341 		if( GetVersion() > 1 )
342 		{
343 			instance &= 0x7f;
344 		}
345 		uint8 commandClassId = _data[2];
346 
347 		if( CommandClass* pCommandClass = node->GetCommandClass( commandClassId ) )
348 		{
349 			Log::Write( LogLevel_Info, GetNodeId(), "Received a MultiInstanceEncap from node %d, instance %d, for Command Class %s", GetNodeId(), instance, pCommandClass->GetCommandClassName().c_str() );
350 			pCommandClass->ReceivedCntIncr();
351 			pCommandClass->HandleMsg( &_data[3], _length-3, instance );
352 		}
353 		else
354 		{
355 			Log::Write( LogLevel_Warning, GetNodeId(), "Received invalid MultiInstanceReport from node %d. Attempting to process as MultiChannel", GetNodeId());
356 			HandleMultiChannelEncap( _data, _length );
357 		}
358 	}
359 }
360 
361 //-----------------------------------------------------------------------------
362 // <MultiInstance::HandleMultiChannelEndPointReport>
363 // Handle a message from the Z-Wave network
364 //-----------------------------------------------------------------------------
HandleMultiChannelEndPointReport(uint8 const * _data,uint32 const _length)365 void MultiInstance::HandleMultiChannelEndPointReport
366 (
367 		uint8 const* _data,
368 		uint32 const _length
369 )
370 {
371 	int len;
372 
373 	if( m_numEndPoints != 0 )
374 	{
375 		return;
376 	}
377 
378 	m_numEndPointsCanChange = (( _data[1] & 0x80 ) != 0 );	// Number of endpoints can change.
379 	m_endPointsAreSameClass = (( _data[1] & 0x40 ) != 0 );	// All endpoints are the same command class.
380 
381 	/* some devices (eg, Aeotec Smart Dimmer 6 incorrectly report all endpoints are the same */
382 	if( m_uniqueendpoints )
383 		m_endPointsAreSameClass = false;
384 
385 
386 	m_numEndPoints = _data[2] & 0x7f;
387 	if( m_numEndPointsHint != 0 )
388 	{
389 		m_numEndPoints = m_numEndPointsHint;		// don't use device's number
390 	}
391 
392 	len = m_numEndPoints;
393 	if( m_endPointsAreSameClass ) // only need to check single end point
394 	{
395 		len = 1;
396 		Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelEndPointReport from node %d. All %d endpoints are the same.", GetNodeId(), m_numEndPoints );
397 	}
398 	else
399 	{
400 		Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelEndPointReport from node %d. %d endpoints are not all the same.", GetNodeId(), m_numEndPoints );
401 	}
402 
403 	// This code assumes the endpoints are all in numeric sequential order.
404 	// Since the end point finds do not appear to work this is the best estimate.
405 	for( uint8 i = 1; i <= len; i++ )
406 	{
407 
408 		// Send a single capability request to each endpoint
409 		Log::Write( LogLevel_Info, GetNodeId(), "MultiChannelCmd_CapabilityGet for endpoint %d", i );
410 		Msg* msg = new Msg( "MultiChannelCmd_CapabilityGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
411 		msg->Append( GetNodeId() );
412 		msg->Append( 3 );
413 		msg->Append( GetCommandClassId() );
414 		msg->Append( MultiChannelCmd_CapabilityGet );
415 		msg->Append( i );
416 		msg->Append( GetDriver()->GetTransmitOptions() );
417 		GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
418 	}
419 }
420 
421 //-----------------------------------------------------------------------------
422 // <MultiInstance::HandleMultiChannelCapabilityReport>
423 // Handle a message from the Z-Wave network
424 //-----------------------------------------------------------------------------
HandleMultiChannelCapabilityReport(uint8 const * _data,uint32 const _length)425 void MultiInstance::HandleMultiChannelCapabilityReport
426 (
427 		uint8 const* _data,
428 		uint32 const _length
429 )
430 {
431 
432 	bool dynamic = ((_data[1] & 0x80)!=0);
433 
434 
435 	if( Node* node = GetNodeUnsafe() )
436 	{
437 		/* if you having problems with Dynamic Devices not correctly
438 		 * updating the commandclasses, see this email thread:
439 		 * https://groups.google.com/d/topic/openzwave/IwepxScRAVo/discussion
440 		 */
441 		if ((m_ignoreUnsolicitedMultiChannelCapabilityReport && (node->GetCurrentQueryStage() != Node::QueryStage_Instances))
442 				&& !dynamic && m_endPointCommandClasses.size() > 0) {
443 			Log::Write(LogLevel_Error, GetNodeId(), "Received a Unsolicited MultiChannelEncap when we are not in QueryState_Instances");
444 			return;
445 		}
446 
447 		uint8 endPoint = _data[1] & 0x7f;
448 
449 		Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelCapabilityReport from node %d for endpoint %d", GetNodeId(), endPoint );
450 		Log::Write( LogLevel_Info, GetNodeId(), "    Endpoint is%sdynamic, and is a %s", dynamic ? " " : " not ", node->GetEndPointDeviceClassLabel( _data[2], _data[3] ).c_str() );
451 		Log::Write( LogLevel_Info, GetNodeId(), "    Command classes supported by the endpoint are:" );
452 
453 		// Store the command classes for later use
454 		bool afterMark = false;
455 		m_endPointCommandClasses.clear();
456 		uint8 numCommandClasses = _length - 5;
457 		for( uint8 i = 0; i < numCommandClasses; ++i )
458 		{
459 			uint8 commandClassId = _data[i+4];
460 			if( commandClassId == 0xef )
461 			{
462 				afterMark = true;
463 				continue;
464 			}
465 
466 			m_endPointCommandClasses.insert( commandClassId );
467 
468 			// Ensure the node supports this command class
469 			CommandClass* cc = node->GetCommandClass( commandClassId );
470 			if( !cc )
471 			{
472 				cc = node->AddCommandClass( commandClassId );
473 				if( cc && afterMark )
474 				{
475 					cc->SetAfterMark();
476 				}
477 			}
478 			if( cc )
479 			{
480 				Log::Write( LogLevel_Info, GetNodeId(), "        %s", cc->GetCommandClassName().c_str() );
481 			}
482 		}
483 
484 		// Create internal library instances for each command class in the list
485 		// Also set up mapping from intances to endpoints for encapsulation
486 		Basic* basic = static_cast<Basic*>( node->GetCommandClass( Basic::StaticGetCommandClassId() ) );
487 		if( m_endPointsAreSameClass )				// Create all the same instances here
488 		{
489 			int len;
490 
491 			if( m_endPointMap == MultiInstanceMapAll )	// Include the non-endpoint instance
492 			{
493 				endPoint = 0;
494 				len = m_numEndPoints + 1;
495 			}
496 			else
497 			{
498 				endPoint = 1;
499 				len = m_numEndPoints;
500 			}
501 
502 			// Create all the command classes for all the endpoints
503 			for( uint8 i = 1; i <= len; i++ )
504 			{
505 				//std::cout << "Num Instances: " << len << std::endl;
506 				for( set<uint8>::iterator it = m_endPointCommandClasses.begin(); it != m_endPointCommandClasses.end(); ++it )
507 				{
508 					uint8 commandClassId = *it;
509 					CommandClass* cc = node->GetCommandClass( commandClassId );
510 					if( cc )
511 					{
512 						cc->SetInstance( i );
513 						if( m_endPointMap != MultiInstanceMapAll || i != 1 )
514 						{
515 							cc->SetEndPoint( i, endPoint );
516 						}
517 						// If we support the BASIC command class and it is mapped to a command class
518 						// assigned to this end point, make sure the BASIC command class is also associated
519 						// with this end point.
520 						if( basic != NULL && basic->GetMapping() == commandClassId )
521 						{
522 							basic->SetInstance( i );
523 							if( m_endPointMap != MultiInstanceMapAll || i != 1 )
524 							{
525 								basic->SetEndPoint( i, endPoint );
526 							}
527 						}
528 					}
529 				}
530 				endPoint++;
531 			}
532 		}
533 		else							// Endpoints are different
534 		{
535 			for( set<uint8>::iterator it = m_endPointCommandClasses.begin(); it != m_endPointCommandClasses.end(); ++it )
536 			{
537 				uint8 commandClassId = *it;
538 				CommandClass* cc = node->GetCommandClass( commandClassId );
539 				if( cc )
540 				{
541 					// get instance gets an instance for an endpoint
542 					// but i'm only interested if there is a related instance for an endpoint and not in the actual result
543 					// soo if the result is != 0, the endpoint is already handled
544 					bool endpointAlreadyHandled = cc->GetInstance( endPoint ) != 0 ;
545 					if ( endpointAlreadyHandled )
546 					{
547 						Log::Write( LogLevel_Warning, GetNodeId(), "Received MultiChannelCapabilityReport from node %d for endpoint %d - Endpoint already handled for CommandClass %d", GetNodeId(), endPoint, cc->GetCommandClassId() );
548 						continue;
549 					}
550 					uint8 i;
551 					// Find the next free instance of this class
552 					for( i = 1; i <= 127; i++ )
553 					{
554 						if( m_endPointMap == MultiInstanceMapAll ) // Include the non-endpoint instance
555 						{
556 							if( !cc->GetInstances()->IsSet( i ) )
557 							{
558 								break;
559 							}
560 						}
561 						// Reuse non-endpoint instances first time we see it
562 						else if( i == 1 && cc->GetInstances()->IsSet( i ) && cc->GetEndPoint( i ) == 0 )
563 						{
564 							break;
565 						}
566 						// Find the next free instance
567 						else if( !cc->GetInstances()->IsSet( i ) )
568 						{
569 							break;
570 						}
571 					}
572 					cc->SetInstance( i );
573 					cc->SetEndPoint( i, endPoint );
574 					// If we support the BASIC command class and it is mapped to a command class
575 					// assigned to this end point, make sure the BASIC command class is also associated
576 					// with this end point.
577 					if( basic != NULL && basic->GetMapping() == commandClassId )
578 					{
579 						basic->SetInstance( i );
580 						basic->SetEndPoint( i, endPoint );
581 					}
582 				}
583 			}
584 		}
585 	}
586 }
587 
588 //-----------------------------------------------------------------------------
589 // <MultiInstance::HandleMultiChannelEndPointFindReport>
590 // Handle a message from the Z-Wave network
591 //-----------------------------------------------------------------------------
HandleMultiChannelEndPointFindReport(uint8 const * _data,uint32 const _length)592 void MultiInstance::HandleMultiChannelEndPointFindReport
593 (
594 		uint8 const* _data,
595 		uint32 const _length
596 )
597 {
598 	Log::Write( LogLevel_Info, GetNodeId(), "Received MultiChannelEndPointFindReport from node %d", GetNodeId() );
599 	uint8 numEndPoints = _length - 5;
600 	for( uint8 i=0; i<numEndPoints; ++i )
601 	{
602 		uint8 endPoint = _data[i+4] & 0x7f;
603 
604 		if( m_endPointsAreSameClass )
605 		{
606 			// Use the stored command class list to set up the endpoint.
607 			if( Node* node = GetNodeUnsafe() )
608 			{
609 				for( set<uint8>::iterator it=m_endPointCommandClasses.begin(); it!=m_endPointCommandClasses.end(); ++it )
610 				{
611 					uint8 commandClassId = *it;
612 					CommandClass* cc = node->GetCommandClass( commandClassId );
613 					if( cc )
614 					{
615 						Log::Write( LogLevel_Info, GetNodeId(), "    Endpoint %d: Adding %s", endPoint, cc->GetCommandClassName().c_str() );
616 						cc->SetInstance( endPoint );
617 					}
618 				}
619 			}
620 		}
621 		else
622 		{
623 			// Endpoints are different, so request the capabilities
624 			Log::Write(LogLevel_Info, GetNodeId(), "MultiChannelCmd_CapabilityGet for node %d, endpoint %d", GetNodeId(), endPoint );
625 			Msg* msg = new Msg( "MultiChannelCmd_CapabilityGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
626 			msg->Append( GetNodeId() );
627 			msg->Append( 3 );
628 			msg->Append( GetCommandClassId() );
629 			msg->Append( MultiChannelCmd_CapabilityGet );
630 			msg->Append( endPoint );
631 			msg->Append( GetDriver()->GetTransmitOptions() );
632 			GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
633 		}
634 	}
635 
636 	m_numEndPointsFound += numEndPoints;
637 	if( !m_endPointsAreSameClass )
638 	{
639 		if( _data[1] == 0 )
640 		{
641 			// No more reports to follow this one, so we can continue the search.
642 			if( m_numEndPointsFound < numEndPoints )
643 			{
644 				// We have not yet found all the endpoints, so move to the next generic class request
645 				++m_endPointFindIndex;
646 				if (m_endPointFindIndex <= 13) /* we are finished */
647 				{
648 					if( c_genericClass[m_endPointFindIndex] > 0 )
649 					{
650 						if (m_endPointFindIndex > 13) /* size of c_genericClassName minus Unknown Entry */
651 						{
652 							Log::Write (LogLevel_Warning, GetNodeId(), "m_endPointFindIndex Value was greater than range. Setting to Unknown");
653 							m_endPointFindIndex = 14;
654 						}
655 
656 						Log::Write(LogLevel_Info, GetNodeId(), "MultiChannelCmd_EndPointFind for generic device class 0x%.2x (%s)", c_genericClass[m_endPointFindIndex], c_genericClassName[m_endPointFindIndex] );
657 						Msg* msg = new Msg( "MultiChannelCmd_EndPointFind", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
658 						msg->Append( GetNodeId() );
659 						msg->Append( 4 );
660 						msg->Append( GetCommandClassId() );
661 						msg->Append( MultiChannelCmd_EndPointFind );
662 						msg->Append( c_genericClass[m_endPointFindIndex] );		// Generic device class
663 						msg->Append( 0xff );									// Any specific device class
664 						msg->Append( GetDriver()->GetTransmitOptions() );
665 						GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
666 					}
667 				} else {
668 					Log::Write (LogLevel_Warning, GetNodeId(), "m_endPointFindIndex is higher than range. Not Sending MultiChannelCmd_EndPointFind message");
669 				}
670 			}
671 		}
672 	}
673 }
674 
675 //-----------------------------------------------------------------------------
676 // <MultiInstance::HandleMultiChannelEncap>
677 // Handle a message from the Z-Wave network
678 //-----------------------------------------------------------------------------
HandleMultiChannelEncap(uint8 const * _data,uint32 const _length)679 void MultiInstance::HandleMultiChannelEncap
680 (
681 		uint8 const* _data,
682 		uint32 const _length
683 )
684 {
685 	if( Node* node = GetNodeUnsafe() )
686 	{
687 		uint8 endPoint = _data[1] & 0x7f;
688 		uint8 commandClassId = _data[3];
689 		if( CommandClass* pCommandClass = node->GetCommandClass( commandClassId ) )
690 		{
691 			/* 4.85.13 - If the Root Device is originating a command to an End Point in another node, the Source End Point MUST be set to 0.
692 			 *
693 			 */
694 			if (endPoint == 0) {
695 				Log::Write( LogLevel_Error, GetNodeId(), "MultiChannelEncap with endpoint set to 0 - Send to Root Device");
696 				pCommandClass->HandleMsg(&_data[4], _length-4);
697 				return;
698 			}
699 
700 			uint8 instance = pCommandClass->GetInstance( endPoint );
701 			if( instance == 0 )
702 			{
703 				Log::Write( LogLevel_Error, GetNodeId(), "Cannot find endpoint map to instance for Command Class %s endpoint %d", pCommandClass->GetCommandClassName().c_str(), endPoint );
704 			}
705 			else
706 			{
707 				Log::Write( LogLevel_Info, GetNodeId(), "Received a MultiChannelEncap from node %d, endpoint %d for Command Class %s", GetNodeId(), endPoint, pCommandClass->GetCommandClassName().c_str() );
708 				pCommandClass->HandleMsg( &_data[4], _length-4, instance );
709 			}
710 		} else {
711 			Log::Write(LogLevel_Error, GetNodeId(), "Received a MultiChannelEncap for endpoint %d for Command Class %d, which we can't find", endPoint, commandClassId);
712 		}
713 	}
714 }
715