1 //-----------------------------------------------------------------------------
2 //
3 // WakeUp.cpp
4 //
5 // Implementation of the Z-Wave COMMAND_CLASS_WAKE_UP
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 "command_classes/CommandClasses.h"
29 #include "command_classes/WakeUp.h"
30 #include "command_classes/MultiCmd.h"
31 #include "Defs.h"
32 #include "Msg.h"
33 #include "Driver.h"
34 #include "Node.h"
35 #include "Notification.h"
36 #include "Options.h"
37 #include "platform/Log.h"
38 #include "platform/Mutex.h"
39 #include "value_classes/ValueInt.h"
40
41 using namespace OpenZWave;
42
43 enum WakeUpCmd
44 {
45 WakeUpCmd_IntervalSet = 0x04,
46 WakeUpCmd_IntervalGet = 0x05,
47 WakeUpCmd_IntervalReport = 0x06,
48 WakeUpCmd_Notification = 0x07,
49 WakeUpCmd_NoMoreInformation = 0x08,
50 WakeUpCmd_IntervalCapabilitiesGet = 0x09,
51 WakeUpCmd_IntervalCapabilitiesReport = 0x0A
52 };
53
54
55 //-----------------------------------------------------------------------------
56 // <WakeUp::WakeUp>
57 // Constructor
58 //-----------------------------------------------------------------------------
WakeUp(uint32 const _homeId,uint8 const _nodeId)59 WakeUp::WakeUp
60 (
61 uint32 const _homeId,
62 uint8 const _nodeId
63 ):
64 CommandClass( _homeId, _nodeId ),
65 m_mutex( new Mutex() ),
66 m_awake( true ),
67 m_pollRequired( false )
68 {
69 Options::Get()->GetOptionAsBool("AssumeAwake", &m_awake);
70
71 SetStaticRequest( StaticRequest_Values );
72 }
73
74 //-----------------------------------------------------------------------------
75 // <WakeUp::~WakeUp>
76 // Destructor
77 //-----------------------------------------------------------------------------
~WakeUp()78 WakeUp::~WakeUp
79 (
80 )
81 {
82 m_mutex->Release();
83 while( !m_pendingQueue.empty() )
84 {
85 Driver::MsgQueueItem const& item = m_pendingQueue.front();
86 if( Driver::MsgQueueCmd_SendMsg == item.m_command )
87 {
88 delete item.m_msg;
89 }
90 else if( Driver::MsgQueueCmd_Controller == item.m_command )
91 {
92 delete item.m_cci;
93 }
94 m_pendingQueue.pop_front();
95 }
96 }
97
98 //-----------------------------------------------------------------------------
99 // <WakeUp::Init>
100 // Starts the process of requesting node state from a sleeping device
101 //-----------------------------------------------------------------------------
Init()102 void WakeUp::Init
103 (
104 )
105 {
106 // Request the wake up interval. When we receive the response, we
107 // can send a set interval message with the same interval, but with
108 // the target node id set to that of the controller. This will ensure
109 // that the controller will receive the wake-up notifications from
110 // the device. Once this is done, we can request the rest of the node
111 // state.
112 RequestState( CommandClass::RequestFlag_Session, 1, Driver::MsgQueue_WakeUp );
113 }
114
115 //-----------------------------------------------------------------------------
116 // <WakeUp::RequestState>
117 // Nothing to do for wakeup
118 //-----------------------------------------------------------------------------
RequestState(uint32 const _requestFlags,uint8 const _instance,Driver::MsgQueue const _queue)119 bool WakeUp::RequestState
120 (
121 uint32 const _requestFlags,
122 uint8 const _instance,
123 Driver::MsgQueue const _queue
124 )
125 {
126 bool requests = false;
127 if( ( _requestFlags & RequestFlag_Static ) && HasStaticRequest( StaticRequest_Values ) )
128 {
129 if( GetVersion() > 1 )
130 {
131 requests |= RequestValue( _requestFlags, WakeUpCmd_IntervalCapabilitiesGet, _instance, _queue );
132 }
133 }
134 if( _requestFlags & RequestFlag_Session )
135 {
136 Node* node = GetNodeUnsafe();
137 if( node != NULL && !node->IsController() )
138 {
139 requests |= RequestValue( _requestFlags, 0, _instance, _queue );
140 }
141 }
142
143 return requests;
144 }
145
146 //-----------------------------------------------------------------------------
147 // <WakeUp::RequestValue>
148 // Nothing to do for wakeup
149 //-----------------------------------------------------------------------------
RequestValue(uint32 const _requestFlags,uint8 const _getTypeEnum,uint8 const _instance,Driver::MsgQueue const _queue)150 bool WakeUp::RequestValue
151 (
152 uint32 const _requestFlags,
153 uint8 const _getTypeEnum,
154 uint8 const _instance,
155 Driver::MsgQueue const _queue
156 )
157 {
158 if( _instance != 1 )
159 {
160 // This command class doesn't work with multiple instances
161 return false;
162 }
163
164 if( _getTypeEnum == WakeUpCmd_IntervalCapabilitiesGet )
165 {
166 Msg* msg = new Msg( "WakeUpCmd_IntervalCapabilityGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
167 msg->Append( GetNodeId() );
168 msg->Append( 2 );
169 msg->Append( GetCommandClassId() );
170 msg->Append( WakeUpCmd_IntervalCapabilitiesGet );
171 msg->Append( GetDriver()->GetTransmitOptions() );
172 GetDriver()->SendMsg( msg, _queue );
173 }
174
175
176 if( _getTypeEnum == 0 )
177 {
178 // We won't get a response until the device next wakes up
179 Msg* msg = new Msg( "WakeUpCmd_IntervalGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
180 msg->Append( GetNodeId() );
181 msg->Append( 2 );
182 msg->Append( GetCommandClassId() );
183 msg->Append( WakeUpCmd_IntervalGet );
184 msg->Append( GetDriver()->GetTransmitOptions() );
185 GetDriver()->SendMsg( msg, _queue );
186 return true;
187 }
188
189 return false;
190 }
191
192 //-----------------------------------------------------------------------------
193 // <WakeUp::HandleMsg>
194 // Handle a message from the Z-Wave network
195 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)196 bool WakeUp::HandleMsg
197 (
198 uint8 const* _data,
199 uint32 const _length,
200 uint32 const _instance // = 1
201 )
202 {
203 if( WakeUpCmd_IntervalReport == (WakeUpCmd)_data[0] )
204 {
205 if( ValueInt* value = static_cast<ValueInt*>( GetValue( _instance, 0 ) ) )
206 {
207 // some interval reports received are validly formatted (proper checksum, etc.) but only have length
208 // of 3 (0x84 (classid), 0x06 (IntervalReport), 0x00). Not sure what this means
209 if( _length < 6 )
210 {
211 Log::Write( LogLevel_Warning, "" );
212 Log::Write( LogLevel_Warning, GetNodeId(), "Unusual response: WakeUpCmd_IntervalReport with len = %d. Ignored.", _length );
213 value->Release();
214 return false;
215 }
216
217 uint32 interval = ((uint32)_data[1]) << 16;
218 interval |= (((uint32)_data[2]) << 8);
219 interval |= (uint32)_data[3];
220
221 uint8 targetNodeId = _data[4];
222
223 Log::Write( LogLevel_Info, GetNodeId(), "Received Wakeup Interval report from node %d: Interval=%d, Target Node=%d", GetNodeId(), interval, targetNodeId );
224
225 value->OnValueRefreshed( (int32)interval );
226
227 // Ensure that the target node for wake-up notifications is the controller
228 // but only if node is not a listening device. Hybrid devices that can be
229 // powered by other then batteries shouldn't do this.
230 Node *node = GetNodeUnsafe();
231 if( GetDriver()->GetControllerNodeId() != targetNodeId && ((node) && (!node->IsListeningDevice())) )
232 {
233 SetValue( *value );
234 }
235 value->Release();
236 }
237 return true;
238 }
239 else if( WakeUpCmd_Notification == (WakeUpCmd)_data[0] )
240 {
241 // The device is awake.
242 Log::Write( LogLevel_Info, GetNodeId(), "Received Wakeup Notification from node %d", GetNodeId() );
243 SetAwake( true );
244 return true;
245 }
246 else if( WakeUpCmd_IntervalCapabilitiesReport == (WakeUpCmd)_data[0] )
247 {
248 uint32 mininterval = (((uint32)_data[1]) << 16) | (((uint32)_data[2]) << 8) | ((uint32)_data[3]);
249 uint32 maxinterval = (((uint32)_data[4]) << 16) | (((uint32)_data[5]) << 8) | ((uint32)_data[6]);
250 uint32 definterval = (((uint32)_data[7]) << 16) | (((uint32)_data[8]) << 8) | ((uint32)_data[9]);
251 uint32 stepinterval = (((uint32)_data[10]) << 16) | (((uint32)_data[11]) << 8) | ((uint32)_data[12]);
252 Log::Write( LogLevel_Info, GetNodeId(), "Received Wakeup Interval Capability report from node %d: Min Interval=%d, Max Interval=%d, Default Interval=%d, Interval Step=%d", GetNodeId(), mininterval, maxinterval, definterval, stepinterval );
253 if( ValueInt* value = static_cast<ValueInt*>( GetValue( _instance, 1 ) ) )
254 {
255 value->OnValueRefreshed( (int32)mininterval );
256 value->Release();
257 }
258 if( ValueInt* value = static_cast<ValueInt*>( GetValue( _instance, 2 ) ) )
259 {
260 value->OnValueRefreshed( (int32)maxinterval );
261 value->Release();
262 }
263 if( ValueInt* value = static_cast<ValueInt*>( GetValue( _instance, 3 ) ) )
264 {
265 value->OnValueRefreshed( (int32)definterval );
266 value->Release();
267 }
268 if( ValueInt* value = static_cast<ValueInt*>( GetValue( _instance, 4 ) ) )
269 {
270 value->OnValueRefreshed( (int32)stepinterval );
271 value->Release();
272 }
273 ClearStaticRequest( StaticRequest_Values );
274 return true;
275 }
276
277 return false;
278 }
279
280 //-----------------------------------------------------------------------------
281 // <WakeUp::SetValue>
282 // Set the device's wakeup interval
283 //-----------------------------------------------------------------------------
SetValue(Value const & _value)284 bool WakeUp::SetValue
285 (
286 Value const& _value
287 )
288 {
289 if( ValueID::ValueType_Int == _value.GetID().GetType() )
290 {
291 ValueInt const* value = static_cast<ValueInt const*>(&_value);
292
293 Msg* msg = new Msg( "WakeUpCmd_IntervalSet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
294 msg->Append( GetNodeId() );
295
296 if( GetNodeUnsafe()->GetCommandClass( MultiCmd::StaticGetCommandClassId() ) )
297 {
298 msg->Append( 10 );
299 msg->Append( MultiCmd::StaticGetCommandClassId() );
300 msg->Append( MultiCmd::MultiCmdCmd_Encap );
301 msg->Append( 1 );
302 }
303
304 int32 interval = value->GetValue();
305
306 msg->Append( 6 ); // length of command bytes following
307 msg->Append( GetCommandClassId() );
308 msg->Append( WakeUpCmd_IntervalSet );
309 msg->Append( (uint8)(( interval >> 16 ) & 0xff) );
310 msg->Append( (uint8)(( interval >> 8 ) & 0xff) );
311 msg->Append( (uint8)( interval & 0xff ) );
312 msg->Append( GetDriver()->GetControllerNodeId() );
313 msg->Append( GetDriver()->GetTransmitOptions() );
314 GetDriver()->SendMsg( msg, Driver::MsgQueue_WakeUp );
315 return true;
316 }
317
318 return false;
319 }
320
321 //-----------------------------------------------------------------------------
322 // <WakeUp::SetVersion>
323 // Set the command class version
324 //-----------------------------------------------------------------------------
SetVersion(uint8 const _version)325 void WakeUp::SetVersion
326 (
327 uint8 const _version
328 )
329 {
330 CommandClass::SetVersion( _version );
331 CreateVars( 1 );
332 }
333
334 //-----------------------------------------------------------------------------
335 // <WakeUp::SetAwake>
336 // Set whether the device is likely to be awake
337 //-----------------------------------------------------------------------------
SetAwake(bool _state)338 void WakeUp::SetAwake
339 (
340 bool _state
341 )
342 {
343 if( m_awake != _state )
344 {
345 m_awake = _state;
346 Log::Write( LogLevel_Info, GetNodeId(), " Node %d has been marked as %s", GetNodeId(), m_awake ? "awake" : "asleep" );
347 Notification* notification = new Notification( Notification::Type_Notification );
348 notification->SetHomeAndNodeIds( GetHomeId(), GetNodeId() );
349 notification->SetNotification( m_awake ? Notification::Code_Awake : Notification::Code_Sleep );
350 GetDriver()->QueueNotification( notification );
351
352 }
353
354 if( m_awake )
355 {
356 // If the device is marked for polling, request the current state
357 Node* node = GetNodeUnsafe();
358 if( m_pollRequired )
359 {
360 if( node != NULL )
361 {
362 node->SetQueryStage( Node::QueryStage_Dynamic );
363 }
364 m_pollRequired = false;
365 }
366
367 // Send all pending messages
368 SendPending();
369 }
370 }
371
372 //-----------------------------------------------------------------------------
373 // <WakeUp::QueueMsg>
374 // Add a Z-Wave message to the queue
375 //-----------------------------------------------------------------------------
QueueMsg(Driver::MsgQueueItem const & _item)376 void WakeUp::QueueMsg
377 (
378 Driver::MsgQueueItem const& _item
379 )
380 {
381 m_mutex->Lock();
382
383 // See if there is already a copy of this message in the queue. If so,
384 // we delete it. This is to prevent duplicates building up if the
385 // device does not wake up very often. Deleting the original and
386 // adding the copy to the end avoids problems with the order of
387 // commands such as on and off.
388 list<Driver::MsgQueueItem>::iterator it = m_pendingQueue.begin();
389 while( it != m_pendingQueue.end() )
390 {
391 Driver::MsgQueueItem const& item = *it;
392 if( item == _item )
393 {
394 // Duplicate found
395 if( Driver::MsgQueueCmd_SendMsg == item.m_command )
396 {
397 delete item.m_msg;
398 }
399 else if( Driver::MsgQueueCmd_Controller == item.m_command )
400 {
401 delete item.m_cci;
402 }
403 m_pendingQueue.erase( it++ );
404 }
405 else
406 {
407 ++it;
408 }
409 }
410 /* make sure the SendAttempts is reset to 0 */
411 if (_item.m_command == Driver::MsgQueueCmd_SendMsg)
412 _item.m_msg->SetSendAttempts(0);
413
414 m_pendingQueue.push_back( _item );
415 m_mutex->Unlock();
416 }
417
418 //-----------------------------------------------------------------------------
419 // <WakeUp::SendPending>
420 // The device is awake, so send all the pending messages
421 //-----------------------------------------------------------------------------
SendPending()422 void WakeUp::SendPending
423 (
424 )
425 {
426 m_awake = true;
427
428 m_mutex->Lock();
429 list<Driver::MsgQueueItem>::iterator it = m_pendingQueue.begin();
430 while( it != m_pendingQueue.end() )
431 {
432 Driver::MsgQueueItem const& item = *it;
433 if( Driver::MsgQueueCmd_SendMsg == item.m_command )
434 {
435 GetDriver()->SendMsg( item.m_msg, Driver::MsgQueue_WakeUp );
436 }
437 else if( Driver::MsgQueueCmd_QueryStageComplete == item.m_command )
438 {
439 GetDriver()->SendQueryStageComplete( item.m_nodeId, item.m_queryStage );
440 } else if( Driver::MsgQueueCmd_Controller == item.m_command )
441 {
442 GetDriver()->BeginControllerCommand( item.m_cci->m_controllerCommand, item.m_cci->m_controllerCallback, item.m_cci->m_controllerCallbackContext, item.m_cci->m_highPower, item.m_cci->m_controllerCommandNode, item.m_cci->m_controllerCommandArg );
443 delete item.m_cci;
444 }
445 it = m_pendingQueue.erase( it );
446 }
447 m_mutex->Unlock();
448
449 // Send the device back to sleep, unless we have outstanding queries.
450 bool sendToSleep = m_awake;
451 Node* node = GetNodeUnsafe();
452 if( node != NULL )
453 {
454
455 if( !node->AllQueriesCompleted() )
456 {
457 sendToSleep = false;
458 }
459 }
460
461 if( sendToSleep )
462 {
463 Msg* msg = new Msg( "WakeUpCmd_NoMoreInformation", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
464 msg->Append( GetNodeId() );
465 msg->Append( 2 );
466 msg->Append( GetCommandClassId() );
467 msg->Append( WakeUpCmd_NoMoreInformation );
468 msg->Append( GetDriver()->GetTransmitOptions() );
469 GetDriver()->SendMsg( msg, Driver::MsgQueue_WakeUp );
470 }
471 }
472
473 //-----------------------------------------------------------------------------
474 // <WakeUp::CreateVars>
475 // Create the values managed by this command class
476 //-----------------------------------------------------------------------------
CreateVars(uint8 const _instance)477 void WakeUp::CreateVars
478 (
479 uint8 const _instance
480 )
481 {
482 if( Node* node = GetNodeUnsafe() )
483 {
484 if( !node->IsController() ) // We don't add the interval value for controllers, because they don't appear to ever wake up on their own.
485 {
486 switch( GetVersion() )
487 {
488 case 1:
489 {
490 node->CreateValueInt( ValueID::ValueGenre_System, GetCommandClassId(), _instance, 0, "Wake-up Interval", "Seconds", false, false, 3600, 0 );
491 break;
492 }
493 case 2:
494 {
495 node->CreateValueInt( ValueID::ValueGenre_System, GetCommandClassId(), _instance, 1, "Minimum Wake-up Interval", "Seconds", true, false, 0, 0 );
496 node->CreateValueInt( ValueID::ValueGenre_System, GetCommandClassId(), _instance, 2, "Maximum Wake-up Interval", "Seconds", true, false, 0, 0 );
497 node->CreateValueInt( ValueID::ValueGenre_System, GetCommandClassId(), _instance, 3, "Default Wake-up Interval", "Seconds", true, false, 0, 0 );
498 node->CreateValueInt( ValueID::ValueGenre_System, GetCommandClassId(), _instance, 4, "Wake-up Interval Step", "Seconds", true, false, 0, 0 );
499 break;
500 }
501 }
502 }
503 }
504 }
505