1 //----------------------------------------------------------------------------- 2 // 3 // Value.cpp 4 // 5 // Base class for all OpenZWave Value Classes 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 "Manager.h" 30 #include "Driver.h" 31 #include "Localization.h" 32 #include "Node.h" 33 #include "Notification.h" 34 #include "Msg.h" 35 #include "Bitfield.h" 36 #include "value_classes/Value.h" 37 #include "platform/Log.h" 38 #include "command_classes/CommandClass.h" 39 #include <ctime> 40 #include "Options.h" 41 42 namespace OpenZWave 43 { 44 namespace Internal 45 { 46 namespace VC 47 { 48 49 static char const* c_genreName[] = 50 { "basic", "user", "config", "system", "invalid" }; 51 52 static char const* c_typeName[] = 53 { "bool", "byte", "decimal", "int", "list", "schedule", "short", "string", "button", "raw", "bitset", "invalid type" }; 54 55 //----------------------------------------------------------------------------- 56 // <Value::Value> 57 // Constructor 58 //----------------------------------------------------------------------------- Value(uint32 const _homeId,uint8 const _nodeId,ValueID::ValueGenre const _genre,uint8 const _commandClassId,uint8 const _instance,uint16 const _index,ValueID::ValueType const _type,string const & _label,string const & _units,bool const _readOnly,bool const _writeOnly,bool const _isSet,uint8 const _pollIntensity)59 Value::Value(uint32 const _homeId, uint8 const _nodeId, ValueID::ValueGenre const _genre, uint8 const _commandClassId, uint8 const _instance, uint16 const _index, ValueID::ValueType const _type, string const& _label, string const& _units, bool const _readOnly, bool const _writeOnly, bool const _isSet, uint8 const _pollIntensity) : 60 m_min(0), m_max(0), m_refreshTime(0), m_verifyChanges(false), m_refreshAfterSet(true), m_id(_homeId, _nodeId, _genre, _commandClassId, _instance, _index, _type), m_units(_units), m_readOnly(_readOnly), m_writeOnly(_writeOnly), m_isSet(_isSet), m_affectsLength(0), m_affects(), m_affectsAll(false), m_checkChange(false), m_pollIntensity(_pollIntensity) 61 { 62 SetLabel(_label); 63 } 64 65 //----------------------------------------------------------------------------- 66 // <Value::Value> 67 // Constructor (from XML) 68 //----------------------------------------------------------------------------- Value()69 Value::Value() : 70 m_min(0), m_max(0), m_refreshTime(0), m_verifyChanges(false), m_refreshAfterSet(true), m_readOnly(false), m_writeOnly(false), m_isSet(false), m_affectsLength(0), m_affects(), m_affectsAll(false), m_checkChange(false), m_pollIntensity(0) 71 { 72 } 73 74 //----------------------------------------------------------------------------- 75 // <Value::~Value> 76 // Destructor 77 //----------------------------------------------------------------------------- ~Value()78 Value::~Value() 79 { 80 if (m_affectsLength > 0) 81 { 82 delete[] m_affects; 83 } 84 } 85 86 //----------------------------------------------------------------------------- 87 // <Value::ReadXML> 88 // Apply settings from XML 89 //----------------------------------------------------------------------------- ReadXML(uint32 const _homeId,uint8 const _nodeId,uint8 const _commandClassId,TiXmlElement const * _valueElement)90 void Value::ReadXML(uint32 const _homeId, uint8 const _nodeId, uint8 const _commandClassId, TiXmlElement const* _valueElement) 91 { 92 int intVal; 93 94 ValueID::ValueGenre genre = Value::GetGenreEnumFromName(_valueElement->Attribute("genre")); 95 ValueID::ValueType type = Value::GetTypeEnumFromName(_valueElement->Attribute("type")); 96 97 uint8 instance = 1; 98 if (TIXML_SUCCESS == _valueElement->QueryIntAttribute("instance", &intVal)) 99 { 100 instance = (uint8) intVal; 101 } 102 103 uint16 index = 0; 104 if (TIXML_SUCCESS == _valueElement->QueryIntAttribute("index", &intVal)) 105 { 106 /* index is only 10 bytes in the ValueID class */ 107 index = (uint16) (intVal & 0x3FF); 108 } 109 110 m_id = ValueID(_homeId, _nodeId, genre, _commandClassId, instance, index, type); 111 112 char const* label = _valueElement->Attribute("label"); 113 if (label) 114 { 115 SetLabel(label); 116 } 117 118 char const* units = _valueElement->Attribute("units"); 119 if (units) 120 { 121 m_units = units; 122 } 123 124 char const* readOnly = _valueElement->Attribute("read_only"); 125 if (readOnly) 126 { 127 m_readOnly = !strcmp(readOnly, "true"); 128 } 129 130 char const* writeOnly = _valueElement->Attribute("write_only"); 131 if (writeOnly) 132 { 133 m_writeOnly = !strcmp(writeOnly, "true"); 134 } 135 136 if (TIXML_SUCCESS == _valueElement->QueryIntAttribute("poll_intensity", &intVal)) 137 { 138 m_pollIntensity = (uint8) intVal; 139 } 140 141 char const* affects = _valueElement->Attribute("affects"); 142 if (affects) 143 { 144 if (m_affectsLength != 0) 145 { 146 delete[] m_affects; 147 } 148 m_affectsLength = 0; 149 if (!strcmp(affects, "all")) 150 { 151 m_affectsAll = true; 152 } 153 else 154 { 155 size_t len = strlen(affects); 156 if (len > 0) 157 { 158 for (size_t i = 0; i < len; i++) 159 { 160 if (affects[i] == ',') 161 { 162 m_affectsLength++; 163 } 164 else if (affects[i] < '0' || affects[i] > '9') 165 { 166 Log::Write(LogLevel_Info, "Improperly formatted affects data: \"%s\"", affects); 167 break; 168 } 169 } 170 m_affectsLength++; 171 m_affects = new uint8[m_affectsLength]; 172 unsigned int j = 0; 173 for (int i = 0; i < m_affectsLength; i++) 174 { 175 m_affects[i] = atoi(&affects[j]); 176 while (j < len && affects[j] != ',') 177 { 178 j++; 179 } 180 j++; 181 } 182 } 183 } 184 } 185 186 char const* verifyChanges = _valueElement->Attribute("verify_changes"); 187 if (verifyChanges) 188 { 189 m_verifyChanges = !strcmp(verifyChanges, "true"); 190 } 191 192 if (TIXML_SUCCESS == _valueElement->QueryIntAttribute("min", &intVal)) 193 { 194 m_min = intVal; 195 } 196 197 if (TIXML_SUCCESS == _valueElement->QueryIntAttribute("max", &intVal)) 198 { 199 m_max = intVal; 200 } 201 202 TiXmlElement const* helpElement = _valueElement->FirstChildElement(); 203 while (helpElement) 204 { 205 char const* str = helpElement->Value(); 206 if (str && !strcmp(str, "Help")) 207 { 208 Localization::Get()->ReadXMLVIDHelp(m_id.GetNodeId(), _commandClassId, index, -1, helpElement); 209 } 210 if (str && !strcmp(str, "Label")) 211 { 212 Localization::Get()->ReadXMLVIDLabel(m_id.GetNodeId(), _commandClassId, index, -1, helpElement); 213 } 214 helpElement = helpElement->NextSiblingElement(); 215 } 216 } 217 218 //----------------------------------------------------------------------------- 219 // <Value::WriteXML> 220 // Write ourselves to an XML document 221 //----------------------------------------------------------------------------- WriteXML(TiXmlElement * _valueElement)222 void Value::WriteXML(TiXmlElement* _valueElement) 223 { 224 char str[16]; 225 226 _valueElement->SetAttribute("type", GetTypeNameFromEnum(m_id.GetType())); 227 _valueElement->SetAttribute("genre", GetGenreNameFromEnum(m_id.GetGenre())); 228 229 snprintf(str, sizeof(str), "%d", m_id.GetInstance()); 230 _valueElement->SetAttribute("instance", str); 231 232 snprintf(str, sizeof(str), "%d", (m_id.GetIndex() & 0x3FF)); 233 _valueElement->SetAttribute("index", str); 234 235 _valueElement->SetAttribute("label", GetLabel().c_str()); 236 _valueElement->SetAttribute("units", m_units.c_str()); 237 _valueElement->SetAttribute("read_only", m_readOnly ? "true" : "false"); 238 _valueElement->SetAttribute("write_only", m_writeOnly ? "true" : "false"); 239 _valueElement->SetAttribute("verify_changes", m_verifyChanges ? "true" : "false"); 240 241 snprintf(str, sizeof(str), "%d", m_pollIntensity); 242 _valueElement->SetAttribute("poll_intensity", str); 243 244 snprintf(str, sizeof(str), "%d", m_min); 245 _valueElement->SetAttribute("min", str); 246 247 snprintf(str, sizeof(str), "%d", m_max); 248 _valueElement->SetAttribute("max", str); 249 250 if (m_affectsAll) 251 { 252 _valueElement->SetAttribute("affects", "all"); 253 } 254 else if (m_affectsLength > 0) 255 { 256 string s; 257 for (int i = 0; i < m_affectsLength; i++) 258 { 259 snprintf(str, sizeof(str), "%d", m_affects[i]); 260 s = s + str; 261 if (i + 1 < m_affectsLength) 262 { 263 s = s + ","; 264 } 265 266 } 267 _valueElement->SetAttribute("affects", s.c_str()); 268 } 269 Localization::Get()->WriteXMLVIDHelp(m_id.GetNodeId(), m_id.GetCommandClassId(), m_id.GetIndex(), -1, _valueElement); 270 } 271 272 //----------------------------------------------------------------------------- 273 // <Value::Set> 274 // Set a new value in the device 275 //----------------------------------------------------------------------------- Set()276 bool Value::Set() 277 { 278 // nothing to do if this is a read-only value (return false to indicate an error) 279 if (IsReadOnly()) 280 { 281 return false; 282 } 283 284 // retrieve the driver, node and commandclass object for this value 285 bool res = false; 286 Node* node = NULL; 287 if (Driver* driver = Manager::Get()->GetDriver(m_id.GetHomeId())) 288 { 289 node = driver->GetNodeUnsafe(m_id.GetNodeId()); 290 if (node != NULL) 291 { 292 if (Internal::CC::CommandClass* cc = node->GetCommandClass(m_id.GetCommandClassId())) 293 { 294 Log::Write(LogLevel_Info, m_id.GetNodeId(), "Value::Set - %s - %s - %d - %d - %s", cc->GetCommandClassName().c_str(), this->GetLabel().c_str(), m_id.GetIndex(), m_id.GetInstance(), this->GetAsString().c_str()); 295 // flag value as set and queue a "Set Value" message for transmission to the device 296 res = cc->SetValue(*this); 297 298 if (res) 299 { 300 if (!IsWriteOnly()) 301 { 302 // queue a "RequestValue" message to update the value 303 if (m_refreshAfterSet) { 304 cc->RequestValue( 0, m_id.GetIndex(), m_id.GetInstance(), Driver::MsgQueue_Send ); 305 } 306 } 307 else 308 { 309 // There is a "bug" here in that write only values 310 // never send a notification about the value changing. 311 // For sleeping devices it may not change until the 312 // device wakes up at some point in the future. 313 // So when is the right time to change it? 314 if (m_affectsAll) 315 { 316 node->RequestAllConfigParams(0); 317 } 318 else if (m_affectsLength > 0) 319 { 320 for (int i = 0; i < m_affectsLength; i++) 321 { 322 node->RequestConfigParam(m_affects[i]); 323 } 324 } 325 } 326 } 327 } 328 } 329 } 330 331 return res; 332 } 333 334 //----------------------------------------------------------------------------- 335 // <Value::OnValueRefreshed> 336 // A value in a device has been refreshed 337 //----------------------------------------------------------------------------- OnValueRefreshed()338 void Value::OnValueRefreshed() 339 { 340 if (IsWriteOnly()) 341 { 342 return; 343 } 344 345 if (Driver* driver = Manager::Get()->GetDriver(m_id.GetHomeId())) 346 { 347 m_isSet = true; 348 349 bool bSuppress; 350 Options::Get()->GetOptionAsBool("SuppressValueRefresh", &bSuppress); 351 if (!bSuppress) 352 { 353 // Notify the watchers 354 Notification* notification = new Notification(Notification::Type_ValueRefreshed); 355 notification->SetValueId(m_id); 356 driver->QueueNotification(notification); 357 } 358 } 359 } 360 361 //----------------------------------------------------------------------------- 362 // <Value::OnValueChanged> 363 // A value in a device has changed 364 //----------------------------------------------------------------------------- OnValueChanged()365 void Value::OnValueChanged() 366 { 367 if (IsWriteOnly()) 368 { 369 return; 370 } 371 372 if (Driver* driver = Manager::Get()->GetDriver(m_id.GetHomeId())) 373 { 374 m_isSet = true; 375 376 // Notify the watchers 377 Notification* notification = new Notification(Notification::Type_ValueChanged); 378 notification->SetValueId(m_id); 379 driver->QueueNotification(notification); 380 } 381 /* Call Back to the Command Class that this Value has changed, so we can search the 382 * TriggerRefreshValue vector to see if we should request any other values to be 383 * refreshed. 384 */ 385 Node* node = NULL; 386 if (Driver* driver = Manager::Get()->GetDriver(m_id.GetHomeId())) 387 { 388 node = driver->GetNodeUnsafe(m_id.GetNodeId()); 389 if (node != NULL) 390 { 391 if (Internal::CC::CommandClass* cc = node->GetCommandClass(m_id.GetCommandClassId())) 392 { 393 cc->CheckForRefreshValues(this); 394 } 395 } 396 } 397 398 } 399 400 //----------------------------------------------------------------------------- 401 // <Value::GetGenreEnumFromName> 402 // Static helper to get a genre enum from a string 403 //----------------------------------------------------------------------------- GetGenreEnumFromName(char const * _name)404 OpenZWave::ValueID::ValueGenre Value::GetGenreEnumFromName(char const* _name) 405 { 406 ValueID::ValueGenre genre = ValueID::ValueGenre_System; 407 if (_name) 408 { 409 for (int i = 0; i < (int) ValueID::ValueGenre_Count; ++i) 410 { 411 if (!strcmp(_name, c_genreName[i])) 412 { 413 genre = (ValueID::ValueGenre) i; 414 break; 415 } 416 } 417 } 418 419 return genre; 420 } 421 422 //----------------------------------------------------------------------------- 423 // <Value::GetGenreNameFromEnum> 424 // Static helper to get a genre enum as a string 425 //----------------------------------------------------------------------------- GetGenreNameFromEnum(ValueID::ValueGenre _genre)426 char const* Value::GetGenreNameFromEnum(ValueID::ValueGenre _genre) 427 { 428 return c_genreName[_genre]; 429 } 430 431 //----------------------------------------------------------------------------- 432 // <Value::GetTypeEnumFromName> 433 // Static helper to get a type enum from a string 434 //----------------------------------------------------------------------------- GetTypeEnumFromName(char const * _name)435 OpenZWave::ValueID::ValueType Value::GetTypeEnumFromName(char const* _name) 436 { 437 ValueID::ValueType type = ValueID::ValueType_Bool; 438 if (_name) 439 { 440 for (int i = 0; i <= (int) ValueID::ValueType_Max; ++i) 441 { 442 if (!strcmp(_name, c_typeName[i])) 443 { 444 type = (ValueID::ValueType) i; 445 break; 446 } 447 } 448 } 449 450 return type; 451 } 452 453 //----------------------------------------------------------------------------- 454 // <Value::GetTypeNameFromEnum> 455 // Static helper to get a type enum as a string 456 //----------------------------------------------------------------------------- GetTypeNameFromEnum(ValueID::ValueType _type)457 char const* Value::GetTypeNameFromEnum(ValueID::ValueType _type) 458 { 459 if (_type > (int) ValueID::ValueType_Max) 460 { 461 Log::Write(LogLevel_Warning, "Value::GetTypeNameFromEnum is out of range: %d", (int) _type); 462 return c_typeName[ValueID::ValueType_Max + 1]; 463 } 464 465 return c_typeName[_type]; 466 } 467 468 //----------------------------------------------------------------------------- 469 // <Value::VerifyRefreshedValue> 470 // Check a refreshed value 471 //----------------------------------------------------------------------------- VerifyRefreshedValue(void * _originalValue,void * _checkValue,void * _newValue,ValueID::ValueType _type,int _originalValueLength,int _checkValueLength,int _newValueLength)472 int Value::VerifyRefreshedValue(void* _originalValue, void* _checkValue, void* _newValue, ValueID::ValueType _type, int _originalValueLength, // = 0, 473 int _checkValueLength, // = 0, 474 int _newValueLength // = 0 475 ) 476 { 477 // TODO: this is pretty rough code, but it's reused by each value type. It would be 478 // better if the actions were taken (m_value = _value, etc.) in this code rather than 479 // in the calling routine as a result of the return value. In particular, it's messy 480 // to be setting these values after the refesh or notification is sent. With some 481 // focus on the actual variable storage, we should be able to accomplish this with 482 // memory functions. It's really the strings that make things complicated(?). 483 // if this is the first read of a value, assume it is valid (and notify as a change) 484 if (!IsSet()) 485 { 486 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Initial read of value"); 487 Value::OnValueChanged(); 488 return 2; // confirmed change of value 489 } 490 else 491 { 492 switch (_type) 493 { 494 case ValueID::ValueType_Button: // Button is stored as a bool 495 case ValueID::ValueType_Bool: // bool 496 { 497 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%s, new value=%s, type=%s", *((bool*) _originalValue) ? "true" : "false", *((uint8*) _newValue) ? "true" : "false", GetTypeNameFromEnum(_type)); 498 break; 499 } 500 case ValueID::ValueType_Byte: // byte 501 { 502 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%d, new value=%d, type=%s", *((uint8*) _originalValue), *((uint8*) _newValue), GetTypeNameFromEnum(_type)); 503 break; 504 } 505 case ValueID::ValueType_Decimal: // decimal is stored as a string, so treat it as a string here 506 case ValueID::ValueType_String: // string 507 { 508 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%s, new value=%s, type=%s", ((string*) _originalValue)->c_str(), ((string*) _newValue)->c_str(), GetTypeNameFromEnum(_type)); 509 break; 510 } 511 case ValueID::ValueType_Short: // short 512 { 513 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%d, new value=%d, type=%s", *((short*) _originalValue), *((short*) _newValue), GetTypeNameFromEnum(_type)); 514 break; 515 } 516 case ValueID::ValueType_List: // List Type is treated as a int32 517 case ValueID::ValueType_Int: // int32 518 case ValueID::ValueType_BitSet: // BitSet 519 { 520 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%d, new value=%d, type=%s", *((int32*) _originalValue), *((int32*) _newValue), GetTypeNameFromEnum(_type)); 521 break; 522 } 523 case ValueID::ValueType_Raw: // raw 524 { 525 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%x, new value=%x, type=%s", _originalValue, _newValue, GetTypeNameFromEnum(_type)); 526 break; 527 } 528 case ValueID::ValueType_Schedule: // Schedule Type 529 { 530 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%s, new value=%s, type=%s", _originalValue, _newValue, GetTypeNameFromEnum(_type)); 531 /* we cant support verifyChanges yet... so always unset this */ 532 m_verifyChanges = false; 533 break; 534 } 535 } 536 } 537 m_refreshTime = time( NULL); // update value refresh time 538 539 // check whether changes in this value should be verified (since some devices will report values that always 540 // change, where confirming changes is difficult or impossible) 541 Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Changes to this value are %sverified", m_verifyChanges ? "" : "not "); 542 543 if (!m_verifyChanges) 544 { 545 // since we're not checking changes in this value, notify ValueChanged (to be on the safe side) 546 Value::OnValueChanged(); 547 return 2; // confirmed change of value 548 } 549 550 // see if the value has changed (result is used whether checking change or not) 551 bool bOriginalEqual = false; 552 switch (_type) 553 { 554 case ValueID::ValueType_Decimal: // Decimal is stored as a string 555 case ValueID::ValueType_String: // string 556 bOriginalEqual = (strcmp(((string*) _originalValue)->c_str(), ((string*) _newValue)->c_str()) == 0); 557 break; 558 case ValueID::ValueType_Short: // short 559 bOriginalEqual = (*((short*) _originalValue) == *((short*) _newValue)); 560 break; 561 case ValueID::ValueType_List: // List Type is treated as a int32 562 case ValueID::ValueType_Int: // int 563 bOriginalEqual = (*((int32*) _originalValue) == *((int32*) _newValue)); 564 break; 565 case ValueID::ValueType_Byte: // uint8 566 bOriginalEqual = (*((uint8*) _originalValue) == *((uint8*) _newValue)); 567 break; 568 case ValueID::ValueType_Button: // Button is stored as a bool 569 case ValueID::ValueType_Bool: // bool 570 bOriginalEqual = (*((bool*) _originalValue) == *((bool*) _newValue)); 571 break; 572 case ValueID::ValueType_Raw: // raw 573 bOriginalEqual = (_originalValueLength == _newValueLength); // first check length of arrays 574 if (bOriginalEqual) 575 bOriginalEqual = (memcmp(_originalValue, _newValue, _newValueLength) == 0); // if length is the same, then check content 576 break; 577 case ValueID::ValueType_Schedule: // Schedule 578 /* Should not get here */ 579 break; 580 case ValueID::ValueType_BitSet: // BitSet 581 bOriginalEqual = (((Bitfield *) _originalValue)->GetValue() == ((Bitfield *) _newValue)->GetValue()); 582 break; 583 } 584 585 // if this is the first refresh of the value, test to see if the value has changed 586 if (!IsCheckingChange()) 587 { 588 if (bOriginalEqual) 589 { 590 // values are the same, so signal a refresh and return 591 Value::OnValueRefreshed(); 592 return 0; // value hasn't changed 593 } 594 595 // values are different, so flag this as a verification refresh and queue it 596 Log::Write(LogLevel_Info, m_id.GetNodeId(), "Changed value (possible)--rechecking"); 597 SetCheckingChange(true); 598 Manager::Get()->RefreshValue(GetID()); 599 return 1; // value has changed (to be confirmed) 600 } 601 else // IsCheckingChange is true if this is the second read of a potentially changed value 602 { 603 // if the second read is the same as the first read, the value really changed 604 bool bCheckEqual = false; 605 switch (_type) 606 { 607 case ValueID::ValueType_Decimal: // Decimal is stored as a string 608 case ValueID::ValueType_String: // string 609 bCheckEqual = (strcmp(((string*) _checkValue)->c_str(), ((string*) _newValue)->c_str()) == 0); 610 break; 611 case ValueID::ValueType_Short: // short 612 bCheckEqual = (*((short*) _checkValue) == *((short*) _newValue)); 613 break; 614 case ValueID::ValueType_List: // List Type is treated as a int32 615 case ValueID::ValueType_Int: // int32 616 bCheckEqual = (*((int32*) _checkValue) == *((int32*) _newValue)); 617 break; 618 case ValueID::ValueType_Byte: // uint8 619 bCheckEqual = (*((uint8*) _checkValue) == *((uint8*) _newValue)); 620 break; 621 case ValueID::ValueType_Button: // Button is stored as a bool 622 case ValueID::ValueType_Bool: // bool 623 bCheckEqual = (*((bool*) _checkValue) == *((bool*) _newValue)); 624 break; 625 case ValueID::ValueType_Raw: 626 bCheckEqual = (_checkValueLength == _newValueLength); // first check length of arrays 627 if (bCheckEqual) 628 bCheckEqual = (memcmp(_checkValue, _newValue, _newValueLength) == 0); // if length is the same, then check content 629 break; 630 case ValueID::ValueType_Schedule: 631 /* Should not get here */ 632 break; 633 case ValueID::ValueType_BitSet: // BitSet 634 bCheckEqual = (((Bitfield *) _checkValue)->GetValue() == ((Bitfield *) _newValue)->GetValue()); 635 ; 636 break; 637 } 638 if (bCheckEqual) 639 { 640 Log::Write(LogLevel_Info, m_id.GetNodeId(), "Changed value--confirmed"); 641 SetCheckingChange(false); 642 643 // update the saved value and send notification 644 Value::OnValueChanged(); 645 return 2; 646 } 647 648 // if the second read is the same as the original value, the first read is assumed to have been in error 649 // log this situation, but don't change the value or send a ValueChanged Notification 650 if (bOriginalEqual) 651 { 652 Log::Write(LogLevel_Info, m_id.GetNodeId(), "Spurious value change was noted."); 653 SetCheckingChange(false); 654 Value::OnValueRefreshed(); 655 return 0; 656 } 657 658 // the second read is different than both the original value and the checked value...retry 659 // keep trying until we get the same value twice 660 Log::Write(LogLevel_Info, m_id.GetNodeId(), "Changed value (changed again)--rechecking"); 661 SetCheckingChange(true); 662 663 // save a temporary copy of value and re-read value from device 664 Manager::Get()->RefreshValue(GetID()); 665 return 1; 666 } 667 } 668 GetHelp() const669 std::string const Value::GetHelp() const 670 { 671 return Localization::Get()->GetValueHelp(m_id.GetNodeId(), m_id.GetCommandClassId(), m_id.GetIndex(), -1); 672 } SetHelp(string const & _help,string const lang)673 void Value::SetHelp(string const& _help, string const lang) 674 { 675 Localization::Get()->SetValueHelp(m_id.GetNodeId(), m_id.GetCommandClassId(), m_id.GetIndex(), -1, _help, lang); 676 } 677 GetLabel() const678 std::string const Value::GetLabel() const 679 { 680 return Localization::Get()->GetValueLabel(m_id.GetNodeId(), m_id.GetCommandClassId(), m_id.GetIndex(), -1); 681 } SetLabel(string const & _label,string const lang)682 void Value::SetLabel(string const& _label, string const lang) 683 { 684 Localization::Get()->SetValueLabel(m_id.GetNodeId(), m_id.GetCommandClassId(), m_id.GetIndex(), -1, _label, lang); 685 } 686 } // namespace VC 687 } // namespace Internal 688 } // namespace OpenZWave 689 690