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