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