1 //-----------------------------------------------------------------------------
2 //
3 // SwitchMultilevel.cpp
4 //
5 // Implementation of the Z-Wave COMMAND_CLASS_SWITCH_MULTILEVEL
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/SwitchMultilevel.h"
30 #include "command_classes/WakeUp.h"
31 #include "Defs.h"
32 #include "Msg.h"
33 #include "Driver.h"
34 #include "Node.h"
35 #include "platform/Log.h"
36
37 #include "value_classes/ValueBool.h"
38 #include "value_classes/ValueButton.h"
39 #include "value_classes/ValueByte.h"
40
41 using namespace OpenZWave;
42
43 enum SwitchMultilevelCmd
44 {
45 SwitchMultilevelCmd_Set = 0x01,
46 SwitchMultilevelCmd_Get = 0x02,
47 SwitchMultilevelCmd_Report = 0x03,
48 SwitchMultilevelCmd_StartLevelChange = 0x04,
49 SwitchMultilevelCmd_StopLevelChange = 0x05,
50 SwitchMultilevelCmd_SupportedGet = 0x06,
51 SwitchMultilevelCmd_SupportedReport = 0x07
52 };
53
54 enum
55 {
56 SwitchMultilevelIndex_Level = 0,
57 SwitchMultilevelIndex_Bright,
58 SwitchMultilevelIndex_Dim,
59 SwitchMultilevelIndex_IgnoreStartLevel,
60 SwitchMultilevelIndex_StartLevel,
61 SwitchMultilevelIndex_Duration,
62 SwitchMultilevelIndex_Step,
63 SwitchMultilevelIndex_Inc,
64 SwitchMultilevelIndex_Dec
65 };
66
67 static uint8 c_directionParams[] =
68 {
69 0x00,
70 0x40,
71 0x00,
72 0x40
73 };
74
75 static char const* c_directionDebugLabels[] =
76 {
77 "Up",
78 "Down",
79 "Inc",
80 "Dec"
81 };
82
83 static char const* c_switchLabelsPos[] =
84 {
85 "Undefined",
86 "On",
87 "Up",
88 "Open",
89 "Clockwise",
90 "Right",
91 "Forward",
92 "Push"
93 };
94
95 static char const* c_switchLabelsNeg[] =
96 {
97 "Undefined",
98 "Off",
99 "Down",
100 "Close",
101 "Counter-Clockwise",
102 "Left",
103 "Reverse",
104 "Pull"
105 };
106
107 //-----------------------------------------------------------------------------
108 // <SwitchMultilevel::RequestState>
109 // Request current state from the device
110 //-----------------------------------------------------------------------------
RequestState(uint32 const _requestFlags,uint8 const _instance,Driver::MsgQueue const _queue)111 bool SwitchMultilevel::RequestState
112 (
113 uint32 const _requestFlags,
114 uint8 const _instance,
115 Driver::MsgQueue const _queue
116 )
117 {
118 if( _requestFlags & RequestFlag_Dynamic )
119 {
120 return RequestValue( _requestFlags, 0, _instance, _queue );
121 }
122
123 return false;
124 }
125
126 //-----------------------------------------------------------------------------
127 // <SwitchMultilevel::RequestValue>
128 // Request current value from the device
129 //-----------------------------------------------------------------------------
RequestValue(uint32 const _requestFlags,uint8 const _index,uint8 const _instance,Driver::MsgQueue const _queue)130 bool SwitchMultilevel::RequestValue
131 (
132 uint32 const _requestFlags,
133 uint8 const _index,
134 uint8 const _instance,
135 Driver::MsgQueue const _queue
136 )
137 {
138 if( _index == SwitchMultilevelIndex_Level )
139 {
140 if ( IsGetSupported() )
141 {
142 Msg* msg = new Msg( "SwitchMultilevelCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
143 msg->SetInstance( this, _instance );
144 msg->Append( GetNodeId() );
145 msg->Append( 2 );
146 msg->Append( GetCommandClassId() );
147 msg->Append( SwitchMultilevelCmd_Get );
148 msg->Append( GetDriver()->GetTransmitOptions() );
149 GetDriver()->SendMsg( msg, _queue );
150 return true;
151 } else {
152 Log::Write( LogLevel_Info, GetNodeId(), "SwitchMultilevelCmd_Get Not Supported on this node");
153 }
154 }
155 return false;
156 }
157
158 //-----------------------------------------------------------------------------
159 // <SwitchMultilevel::HandleMsg>
160 // Handle a message from the Z-Wave network
161 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)162 bool SwitchMultilevel::HandleMsg
163 (
164 uint8 const* _data,
165 uint32 const _length,
166 uint32 const _instance // = 1
167 )
168 {
169 if( SwitchMultilevelCmd_Report == (SwitchMultilevelCmd)_data[0] )
170 {
171 Log::Write( LogLevel_Info, GetNodeId(), "Received SwitchMultiLevel report: level=%d", _data[1] );
172
173 if( ValueByte* value = static_cast<ValueByte*>( GetValue( _instance, SwitchMultilevelIndex_Level ) ) )
174 {
175 value->OnValueRefreshed( _data[1] );
176 value->Release();
177 }
178 return true;
179 }
180
181 if( SwitchMultilevelCmd_SupportedReport == (SwitchMultilevelCmd)_data[0] )
182 {
183 uint8 switchType1 = _data[1] & 0x1f;
184 uint8 switchType2 = _data[2] & 0x1f;
185 uint8 switchtype1label = switchType1;
186 uint8 switchtype2label = switchType2;
187 if (switchtype1label > 7) /* size of c_switchLabelsPos, c_switchLabelsNeg */
188 {
189 Log::Write (LogLevel_Warning, GetNodeId(), "switchtype1label Value was greater than range. Setting to Invalid");
190 switchtype1label = 0;
191 }
192 if (switchtype2label > 7) /* sizeof c_switchLabelsPos, c_switchLabelsNeg */
193 {
194 Log::Write (LogLevel_Warning, GetNodeId(), "switchtype2label Value was greater than range. Setting to Invalid");
195 switchtype2label = 0;
196 }
197
198
199 Log::Write( LogLevel_Info, GetNodeId(), "Received SwitchMultiLevel supported report: Switch1=%s/%s, Switch2=%s/%s", c_switchLabelsPos[switchtype1label], c_switchLabelsNeg[switchtype1label], c_switchLabelsPos[switchtype2label], c_switchLabelsNeg[switchtype2label] );
200 ClearStaticRequest( StaticRequest_Version );
201
202 // Set the labels on the values
203 ValueButton* button;
204
205 if( switchType1 )
206 {
207 if( NULL != ( button = static_cast<ValueButton*>( GetValue( _instance, SwitchMultilevelIndex_Bright ) ) ) )
208 {
209 button->SetLabel( c_switchLabelsPos[switchtype1label] );
210 button->Release();
211 }
212 if( NULL != ( button = static_cast<ValueButton*>( GetValue( _instance, SwitchMultilevelIndex_Dim ) ) ) )
213 {
214 button->SetLabel( c_switchLabelsNeg[switchtype1label] );
215 button->Release();
216 }
217 }
218
219 if( switchType2 )
220 {
221 if( NULL != ( button = static_cast<ValueButton*>( GetValue( _instance, SwitchMultilevelIndex_Inc ) ) ) )
222 {
223 button->SetLabel( c_switchLabelsPos[switchtype2label] );
224 button->Release();
225 }
226 if( NULL != ( button = static_cast<ValueButton*>( GetValue( _instance, SwitchMultilevelIndex_Dec ) ) ) )
227 {
228 button->SetLabel( c_switchLabelsNeg[switchtype2label] );
229 button->Release();
230 }
231 }
232 return true;
233 }
234
235 return false;
236 }
237
238 //-----------------------------------------------------------------------------
239 // <SwitchMultilevel::SetVersion>
240 // Set the command class version
241 //-----------------------------------------------------------------------------
SetVersion(uint8 const _version)242 void SwitchMultilevel::SetVersion
243 (
244 uint8 const _version
245 )
246 {
247 CommandClass::SetVersion( _version );
248
249 if( _version == 3 )
250 {
251 // Request the supported switch types
252 Msg* msg = new Msg( "SwitchMultilevelCmd_SupportedGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
253 msg->Append( GetNodeId() );
254 msg->Append( 2 );
255 msg->Append( GetCommandClassId() );
256 msg->Append( SwitchMultilevelCmd_SupportedGet );
257 msg->Append( GetDriver()->GetTransmitOptions() );
258 GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
259
260 // Set the request flag again - it will be cleared when we get a
261 // response to the SwitchMultilevelCmd_SupportedGet message.
262 SetStaticRequest( StaticRequest_Version );
263 }
264 }
265
266 //-----------------------------------------------------------------------------
267 // <SwitchMultilevel::SetValue>
268 // Set the level on a device
269 //-----------------------------------------------------------------------------
SetValue(Value const & _value)270 bool SwitchMultilevel::SetValue
271 (
272 Value const& _value
273 )
274 {
275 bool res = false;
276 uint8 instance = _value.GetID().GetInstance();
277
278 switch( _value.GetID().GetIndex() )
279 {
280 case SwitchMultilevelIndex_Level:
281 {
282 // Level
283 if( ValueByte* value = static_cast<ValueByte*>( GetValue( instance, SwitchMultilevelIndex_Level ) ) )
284 {
285 res = SetLevel( instance, (static_cast<ValueByte const*>(&_value))->GetValue() );
286 value->Release();
287 }
288 break;
289 }
290 case SwitchMultilevelIndex_Bright:
291 {
292 // Bright
293 if( ValueButton* button = static_cast<ValueButton*>( GetValue( instance, SwitchMultilevelIndex_Bright ) ) )
294 {
295 if( button->IsPressed() )
296 {
297 res = StartLevelChange( instance, SwitchMultilevelDirection_Up );
298 }
299 else
300 {
301 res = StopLevelChange( instance );
302 }
303 button->Release();
304 }
305 break;
306 }
307 case SwitchMultilevelIndex_Dim:
308 {
309 // Dim
310 if( ValueButton* button = static_cast<ValueButton*>( GetValue( instance, SwitchMultilevelIndex_Dim ) ) )
311 {
312 if( button->IsPressed() )
313 {
314 res = StartLevelChange( instance, SwitchMultilevelDirection_Down );
315 }
316 else
317 {
318 res = StopLevelChange( instance );
319 }
320 button->Release();
321 }
322 break;
323 }
324 case SwitchMultilevelIndex_IgnoreStartLevel:
325 {
326 if( ValueBool* value = static_cast<ValueBool*>( GetValue( instance, SwitchMultilevelIndex_IgnoreStartLevel ) ) )
327 {
328 value->OnValueRefreshed( (static_cast<ValueBool const*>( &_value))->GetValue() );
329 value->Release();
330 }
331 res = true;
332 break;
333 }
334 case SwitchMultilevelIndex_StartLevel:
335 {
336 if( ValueByte* value = static_cast<ValueByte*>( GetValue( instance, SwitchMultilevelIndex_StartLevel ) ) )
337 {
338 value->OnValueRefreshed( (static_cast<ValueByte const*>( &_value))->GetValue() );
339 value->Release();
340 }
341 res = true;
342 break;
343 }
344 case SwitchMultilevelIndex_Duration:
345 {
346 if( ValueByte* value = static_cast<ValueByte*>( GetValue( instance, SwitchMultilevelIndex_Duration ) ) )
347 {
348 value->OnValueRefreshed( (static_cast<ValueByte const*>( &_value))->GetValue() );
349 value->Release();
350 }
351 res = true;
352 break;
353 }
354 case SwitchMultilevelIndex_Step:
355 {
356 if( ValueByte* value = static_cast<ValueByte*>( GetValue( instance, SwitchMultilevelIndex_Step ) ) )
357 {
358 value->OnValueRefreshed( (static_cast<ValueByte const*>( &_value))->GetValue() );
359 value->Release();
360 }
361 res = true;
362 break;
363 }
364 case SwitchMultilevelIndex_Inc:
365 {
366 // Inc
367 if( ValueButton* button = static_cast<ValueButton*>( GetValue( instance, SwitchMultilevelIndex_Inc ) ) )
368 {
369 if( button->IsPressed() )
370 {
371 res = StartLevelChange( instance, SwitchMultilevelDirection_Inc );
372 }
373 else
374 {
375 res = StopLevelChange( instance );
376 }
377 button->Release();
378 }
379 break;
380 }
381 case SwitchMultilevelIndex_Dec:
382 {
383 // Dec
384 if( ValueButton* button = static_cast<ValueButton*>( GetValue( instance, SwitchMultilevelIndex_Dec ) ) )
385 {
386 if( button->IsPressed() )
387 {
388 res = StartLevelChange( instance, SwitchMultilevelDirection_Dec );
389 }
390 else
391 {
392 res = StopLevelChange( instance );
393 }
394 button->Release();
395 }
396 break;
397 }
398 }
399
400 return res;
401 }
402
403 //-----------------------------------------------------------------------------
404 // <SwitchMultilevel::SetValueBasic>
405 // Update class values based in BASIC mapping
406 //-----------------------------------------------------------------------------
SetValueBasic(uint8 const _instance,uint8 const _value)407 void SwitchMultilevel::SetValueBasic
408 (
409 uint8 const _instance,
410 uint8 const _value
411 )
412 {
413 // Send a request for new value to synchronize it with the BASIC set/report.
414 // In case the device is sleeping, we set the value anyway so the BASIC set/report
415 // stays in sync with it. We must be careful mapping the uint8 BASIC value
416 // into a class specific value.
417 // When the device wakes up, the real requested value will be retrieved.
418 RequestValue( 0, 0, _instance, Driver::MsgQueue_Send );
419 if( Node* node = GetNodeUnsafe() )
420 {
421 if( WakeUp* wakeUp = static_cast<WakeUp*>( node->GetCommandClass( WakeUp::StaticGetCommandClassId() ) ) )
422 {
423 if( !wakeUp->IsAwake() )
424 {
425 if( ValueByte* value = static_cast<ValueByte*>( GetValue( _instance, SwitchMultilevelIndex_Level ) ) )
426 {
427 value->OnValueRefreshed( _value != 0 );
428 value->Release();
429 }
430 }
431 }
432 }
433 }
434
435 //-----------------------------------------------------------------------------
436 // <SwitchMultilevel::SetLevel>
437 // Set a new level for the switch
438 //-----------------------------------------------------------------------------
SetLevel(uint8 const _instance,uint8 const _level)439 bool SwitchMultilevel::SetLevel
440 (
441 uint8 const _instance,
442 uint8 const _level
443 )
444 {
445 Log::Write( LogLevel_Info, GetNodeId(), "SwitchMultilevel::Set - Setting to level %d", _level );
446 Msg* msg = new Msg( "SwitchMultilevelCmd_Set", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
447 msg->SetInstance( this, _instance );
448 msg->Append( GetNodeId() );
449
450 if( ValueByte* durationValue = static_cast<ValueByte*>( GetValue( _instance, SwitchMultilevelIndex_Duration ) ) )
451 {
452 uint8 duration = durationValue->GetValue();
453 durationValue->Release();
454 if( duration == 0xff )
455 {
456 Log::Write( LogLevel_Info, GetNodeId(), " Duration: Default" );
457 }
458 else if( duration >= 0x80 )
459 {
460 Log::Write( LogLevel_Info, GetNodeId(), " Duration: %d minutes", duration - 0x7f );
461 }
462 else
463 {
464 Log::Write( LogLevel_Info, GetNodeId(), " Duration: %d seconds", duration );
465 }
466
467 msg->Append( 4 );
468 msg->Append( GetCommandClassId() );
469 msg->Append( SwitchMultilevelCmd_Set );
470 msg->Append( _level );
471 msg->Append( duration );
472 }
473 else
474 {
475 msg->Append( 3 );
476 msg->Append( GetCommandClassId() );
477 msg->Append( SwitchMultilevelCmd_Set );
478 msg->Append( _level );
479 }
480
481 msg->Append( GetDriver()->GetTransmitOptions() );
482 GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
483 return true;
484 }
485
486 //-----------------------------------------------------------------------------
487 // <SwitchMultilevel::SwitchMultilevelCmd_StartLevelChange>
488 // Start the level changing
489 //-----------------------------------------------------------------------------
StartLevelChange(uint8 const _instance,SwitchMultilevelDirection const _direction)490 bool SwitchMultilevel::StartLevelChange
491 (
492 uint8 const _instance,
493 SwitchMultilevelDirection const _direction
494 )
495 {
496 Log::Write( LogLevel_Info, GetNodeId(), "SwitchMultilevel::StartLevelChange - Starting a level change" );
497
498 uint8 length = 4;
499 if (_direction > 3) /* size of c_directionParams, c_directionDebugLabels */
500 {
501 Log::Write (LogLevel_Warning, GetNodeId(), "_direction Value was greater than range. Dropping");
502 return false;
503 }
504 uint8 direction = c_directionParams[_direction];
505 Log::Write( LogLevel_Info, GetNodeId(), " Direction: %s", c_directionDebugLabels[_direction] );
506
507 if( ValueBool* ignoreStartLevel = static_cast<ValueBool*>( GetValue( _instance, SwitchMultilevelIndex_IgnoreStartLevel ) ) )
508 {
509 if( ignoreStartLevel->GetValue() )
510 {
511 // Set the ignore start level flag
512 direction |= 0x20;
513 }
514 ignoreStartLevel->Release();
515 }
516 Log::Write( LogLevel_Info, GetNodeId(), " Ignore Start Level: %s", (direction & 0x20) ? "True" : "False" );
517
518 uint8 startLevel = 0;
519 if( ValueByte* startLevelValue = static_cast<ValueByte*>( GetValue( _instance, SwitchMultilevelIndex_StartLevel ) ) )
520 {
521 startLevel = startLevelValue->GetValue();
522 startLevelValue->Release();
523 }
524 Log::Write( LogLevel_Info, GetNodeId(), " Start Level: %d", startLevel );
525
526 uint8 duration = 0;
527 if( ValueByte* durationValue = static_cast<ValueByte*>( GetValue( _instance, SwitchMultilevelIndex_Duration ) ) )
528 {
529 length = 5;
530 duration = durationValue->GetValue();
531 durationValue->Release();
532 Log::Write( LogLevel_Info, GetNodeId(), " Duration: %d", duration );
533 }
534
535 uint8 step = 0;
536 if( ( SwitchMultilevelDirection_Inc == _direction ) || ( SwitchMultilevelDirection_Dec == _direction ) )
537 {
538 if( ValueByte* stepValue = static_cast<ValueByte*>( GetValue( _instance, SwitchMultilevelIndex_Step ) ) )
539 {
540 length = 6;
541 step = stepValue->GetValue();
542 stepValue->Release();
543 Log::Write( LogLevel_Info, GetNodeId(), " Step Size: %d", step );
544 }
545 }
546
547 Msg* msg = new Msg( "SwitchMultilevelCmd_StartLevelChange", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
548 msg->SetInstance( this, _instance );
549 msg->Append( GetNodeId() );
550 msg->Append( length );
551 msg->Append( GetCommandClassId() );
552 msg->Append( SwitchMultilevelCmd_StartLevelChange );
553 if (GetVersion() == 2) {
554 direction &= 0x60;
555 } else if (GetVersion() >= 3) {
556 /* we don't support secondary switch, so we mask that out as well */
557 direction &= 0xE0;
558 }
559
560 msg->Append( direction );
561 msg->Append( startLevel );
562
563 if( length >= 5 )
564 {
565 msg->Append( duration );
566 }
567
568 if( length == 6 )
569 {
570 msg->Append( step );
571 }
572
573 msg->Append( GetDriver()->GetTransmitOptions() );
574 GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
575 return true;
576 }
577
578 //-----------------------------------------------------------------------------
579 // <SwitchMultilevel::StopLevelChange>
580 // Stop the level changing
581 //-----------------------------------------------------------------------------
StopLevelChange(uint8 const _instance)582 bool SwitchMultilevel::StopLevelChange
583 (
584 uint8 const _instance
585 )
586 {
587 Log::Write( LogLevel_Info, GetNodeId(), "SwitchMultilevel::StopLevelChange - Stopping the level change" );
588 Msg* msg = new Msg( "SwitchMultilevelCmd_StopLevelChange", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
589 msg->SetInstance( this, _instance );
590 msg->Append( GetNodeId() );
591 msg->Append( 2 );
592 msg->Append( GetCommandClassId() );
593 msg->Append( SwitchMultilevelCmd_StopLevelChange );
594 msg->Append( GetDriver()->GetTransmitOptions() );
595 GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
596 return true;
597 }
598
599 //-----------------------------------------------------------------------------
600 // <SwitchMultilevel::CreateVars>
601 // Create the values managed by this command class
602 //-----------------------------------------------------------------------------
CreateVars(uint8 const _instance)603 void SwitchMultilevel::CreateVars
604 (
605 uint8 const _instance
606 )
607 {
608 if( Node* node = GetNodeUnsafe() )
609 {
610 switch( GetVersion() )
611 {
612 case 3:
613 {
614 node->CreateValueByte( ValueID::ValueGenre_User, GetCommandClassId(), _instance, SwitchMultilevelIndex_Step, "Step Size", "", false, false, 0, 0 );
615 node->CreateValueButton( ValueID::ValueGenre_User, GetCommandClassId(), _instance, SwitchMultilevelIndex_Inc, "Inc", 0 );
616 node->CreateValueButton( ValueID::ValueGenre_User, GetCommandClassId(), _instance, SwitchMultilevelIndex_Dec, "Dec", 0 );
617 // Fall through to version 2
618 }
619 case 2:
620 {
621 node->CreateValueByte( ValueID::ValueGenre_System, GetCommandClassId(), _instance, SwitchMultilevelIndex_Duration, "Dimming Duration", "", false, false, 0xff, 0 );
622 // Fall through to version 1
623 }
624 case 1:
625 {
626 node->CreateValueByte( ValueID::ValueGenre_User, GetCommandClassId(), _instance, SwitchMultilevelIndex_Level, "Level", "", false, false, 0, 0 );
627 node->CreateValueButton( ValueID::ValueGenre_User, GetCommandClassId(), _instance, SwitchMultilevelIndex_Bright, "Bright", 0 );
628 node->CreateValueButton( ValueID::ValueGenre_User, GetCommandClassId(), _instance, SwitchMultilevelIndex_Dim, "Dim", 0 );
629 node->CreateValueBool( ValueID::ValueGenre_System, GetCommandClassId(), _instance, SwitchMultilevelIndex_IgnoreStartLevel, "Ignore Start Level", "", false, false, true, 0 );
630 node->CreateValueByte( ValueID::ValueGenre_System, GetCommandClassId(), _instance, SwitchMultilevelIndex_StartLevel, "Start Level", "", false, false, 0, 0 );
631 break;
632 }
633 }
634 }
635 }
636
637
638
639