Skip to content

Commit

Permalink
Implement Refresh based on CommandClasses that Send a Target Value - …
Browse files Browse the repository at this point in the history
…Issue #1321
  • Loading branch information
Fishwaldo committed Jul 7, 2020
1 parent 6c2ca61 commit 98c7c03
Show file tree
Hide file tree
Showing 26 changed files with 313 additions and 38 deletions.
3 changes: 3 additions & 0 deletions cpp/src/command_classes/Basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ namespace OpenZWave
{
if (Internal::VC::ValueByte* value = static_cast<Internal::VC::ValueByte*>(GetValue(_instance, ValueID_Index_Basic::Set)))
{
/* Set the Target Value, if it is present */
if (_length == 4)
value->SetTargetValue(_data[2], _data[3]);
value->OnValueRefreshed(_data[1]);
value->Release();
}
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/command_classes/Color.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ namespace OpenZWave
if (Internal::VC::ValueString* color = static_cast<Internal::VC::ValueString*>(GetValue(_instance, ValueID_Index_Color::Color)))
{
Log::Write(LogLevel_Info, GetNodeId(), "Received a updated Color from Device: %s", colorStr.c_str());
if (GetVersion() >= 3)
color->SetTargetValue(decodeColor(m_colorTargetValues), _data[4]);
color->OnValueRefreshed(colorStr);
color->Release();
}
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/command_classes/SwitchBinary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ namespace OpenZWave
// data[1] => Switch state
if (Internal::VC::ValueBool* value = static_cast<Internal::VC::ValueBool*>(GetValue(_instance, ValueID_Index_SwitchBinary::Level)))
{
if (GetVersion() >= 2)
value->SetTargetValue(_data[2] != 0, _data[3]);
value->OnValueRefreshed(_data[1] != 0);
value->Release();
}
Expand Down
20 changes: 6 additions & 14 deletions cpp/src/command_classes/SwitchMultilevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ namespace OpenZWave

if (Internal::VC::ValueByte* value = static_cast<Internal::VC::ValueByte*>(GetValue(_instance, ValueID_Index_SwitchMultiLevel::Level)))
{
/* Target Value - 0 to 100 is valid values, 0xFF is also valid */
if ((GetVersion() >= 4) && ((_data[2] <= 100) || (_data[2] == 0xFF)))
value->SetTargetValue(_data[2], _data[3]);
value->OnValueRefreshed(_data[1]);
value->Release();
}
Expand All @@ -157,7 +160,7 @@ namespace OpenZWave
{
if (Internal::VC::ValueByte* value = static_cast<Internal::VC::ValueByte*>(GetValue(_instance, ValueID_Index_SwitchMultiLevel::Duration)))
{
value->OnValueRefreshed(_data[3]);
value->OnValueRefreshed(decodeDuration(_data[3]));
value->Release();
}
}
Expand Down Expand Up @@ -400,24 +403,13 @@ namespace OpenZWave
Internal::VC::ValueByte* durationValue = static_cast<Internal::VC::ValueByte*>(GetValue(_instance, ValueID_Index_SwitchMultiLevel::Duration));
uint8 duration = durationValue->GetValue();
durationValue->Release();
if (duration == 0xff)
{
Log::Write(LogLevel_Info, GetNodeId(), " Duration: Default");
}
else if (duration >= 0x80)
{
Log::Write(LogLevel_Info, GetNodeId(), " Duration: %d minutes", duration - 0x7f);
}
else
{
Log::Write(LogLevel_Info, GetNodeId(), " Duration: %d seconds", duration);
}
Log::Write(LogLevel_Info, GetNodeId(), " Duration: %d seconds", duration);

msg->Append(4);
msg->Append(GetCommandClassId());
msg->Append(SwitchMultilevelCmd_Set);
msg->Append(_level);
msg->Append(duration);
msg->Append(encodeDuration(duration));
}
else
{
Expand Down
158 changes: 149 additions & 9 deletions cpp/src/value_classes/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ namespace OpenZWave
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)
{
SetLabel(_label);
if (Driver* driver = Manager::Get()->GetDriver(m_id.GetHomeId()))
{
Timer::SetDriver(driver);
}
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -109,6 +113,15 @@ namespace OpenZWave

m_id = ValueID(_homeId, _nodeId, genre, _commandClassId, instance, index, type);

/* For Values Loaded from a XML File - We Need to load the Timer Driver here, as the default
* constructor doens't have a m_id set, hence we can't get the HomeID
*/

if (Driver* driver = Manager::Get()->GetDriver(m_id.GetHomeId()))
{
Timer::SetDriver(driver);
}

char const* label = _valueElement->Attribute("label");
if (label)
{
Expand Down Expand Up @@ -469,9 +482,16 @@ namespace OpenZWave
// <Value::VerifyRefreshedValue>
// Check a refreshed value
//-----------------------------------------------------------------------------
int Value::VerifyRefreshedValue(void* _originalValue, void* _checkValue, void* _newValue, ValueID::ValueType _type, int _originalValueLength, // = 0,
int Value::VerifyRefreshedValue(
void* _originalValue,
void* _checkValue,
void* _newValue,
void* _targetValue,
ValueID::ValueType _type,
int _originalValueLength, // = 0,
int _checkValueLength, // = 0,
int _newValueLength // = 0
int _newValueLength, // = 0,
int _targetValueLength // = 0,
)
{
// TODO: this is pretty rough code, but it's reused by each value type. It would be
Expand All @@ -494,48 +514,72 @@ namespace OpenZWave
case ValueID::ValueType_Button: // Button is stored as a bool
case ValueID::ValueType_Bool: // bool
{
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));
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Value Updated: old value=%s, new value=%s, type=%s", *((bool*) _originalValue) ? "true" : "false", *((uint8*) _newValue) ? "true" : "false", GetTypeNameFromEnum(_type));
if (m_targetValueSet)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "\tTarget Value is Set to %s", *((bool*) _targetValue) ? "true" : "false" );
break;
}
case ValueID::ValueType_Byte: // byte
{
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%d, new value=%d, type=%s", *((uint8*) _originalValue), *((uint8*) _newValue), GetTypeNameFromEnum(_type));
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Value Updated: old value=%d, new value=%d, type=%s", *((uint8*) _originalValue), *((uint8*) _newValue), GetTypeNameFromEnum(_type));
if (m_targetValueSet)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "\tTarget Value is Set to %d", *((uint8*) _targetValue));
break;
}
case ValueID::ValueType_Decimal: // decimal is stored as a string, so treat it as a string here
case ValueID::ValueType_String: // string
{
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));
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Value Updated: old value=%s, new value=%s, type=%s", ((string*) _originalValue)->c_str(), ((string*) _newValue)->c_str(), GetTypeNameFromEnum(_type));
if (m_targetValueSet)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "\tTarget Value is Set to %d", *((string*) _targetValue)->c_str());
break;
}
case ValueID::ValueType_Short: // short
{
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%d, new value=%d, type=%s", *((short*) _originalValue), *((short*) _newValue), GetTypeNameFromEnum(_type));
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Value Updated: old value=%d, new value=%d, type=%s", *((short*) _originalValue), *((short*) _newValue), GetTypeNameFromEnum(_type));
if (m_targetValueSet)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "\tTarget Value is Set to %d", *((short*) _targetValue));
break;
}
case ValueID::ValueType_List: // List Type is treated as a int32
case ValueID::ValueType_Int: // int32
case ValueID::ValueType_BitSet: // BitSet
{
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%d, new value=%d, type=%s", *((int32*) _originalValue), *((int32*) _newValue), GetTypeNameFromEnum(_type));
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Value Updated: old value=%d, new value=%d, type=%s", *((int32*) _originalValue), *((int32*) _newValue), GetTypeNameFromEnum(_type));
if (m_targetValueSet)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "\tTarget Value is Set to %d", *((int32*) _targetValue));
break;
}
case ValueID::ValueType_Raw: // raw
{
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%x, new value=%x, type=%s", _originalValue, _newValue, GetTypeNameFromEnum(_type));
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Value Updated: old value=%x, new value=%x, type=%s", _originalValue, _newValue, GetTypeNameFromEnum(_type));
if (m_targetValueSet)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "\tTarget Value is Set to %x", _targetValue);
break;
}
case ValueID::ValueType_Schedule: // Schedule Type
{
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Refreshed Value: old value=%s, new value=%s, type=%s", _originalValue, _newValue, GetTypeNameFromEnum(_type));
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Value Updated: old value=%s, new value=%s, type=%s", _originalValue, _newValue, GetTypeNameFromEnum(_type));
if (m_targetValueSet)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "\tTarget Value is Set to %x", _targetValue);
/* we cant support verifyChanges yet... so always unset this */
m_verifyChanges = false;
break;
}
}

}
m_refreshTime = time( NULL); // update value refresh time

/* if this is a Value that has a Target Value Set, then lets compare the reported value against the Target Value
* and Trigger a Refresh (based on the Duration, if necessary)
*/
if (m_targetValueSet)
{
return CheckTargetValue(_newValue, _targetValue, _type, _newValueLength, _targetValueLength);
}


// check whether changes in this value should be verified (since some devices will report values that always
// change, where confirming changes is difficult or impossible)
Log::Write(LogLevel_Detail, m_id.GetNodeId(), "Changes to this value are %sverified", m_verifyChanges ? "" : "not ");
Expand Down Expand Up @@ -686,6 +730,102 @@ namespace OpenZWave
{
Localization::Get()->SetValueLabel(m_id.GetNodeId(), m_id.GetCommandClassId(), m_id.GetIndex(), -1, _label, lang);
}

//-----------------------------------------------------------------------------
// <Value::CheckTargetValue>
// Check the reported value against the Target Value
//-----------------------------------------------------------------------------
int Value::CheckTargetValue(void* _newValue, void* _targetValue, ValueID::ValueType _type, int _newValueLength, int _targetValueLength)
{
// see if the value has changed (result is used whether checking change or not)
bool bOriginalEqual = false;
switch (_type)
{
case ValueID::ValueType_Decimal: // Decimal is stored as a string
case ValueID::ValueType_String: // string
bOriginalEqual = (strcmp(((string*) _targetValue)->c_str(), ((string*) _newValue)->c_str()) == 0);
break;
case ValueID::ValueType_Short: // short
bOriginalEqual = (*((short*) _targetValue) == *((short*) _newValue));
break;
case ValueID::ValueType_List: // List Type is treated as a int32
case ValueID::ValueType_Int: // int
bOriginalEqual = (*((int32*) _targetValue) == *((int32*) _newValue));
break;
case ValueID::ValueType_Byte: // uint8
bOriginalEqual = (*((uint8*) _targetValue) == *((uint8*) _newValue));
break;
case ValueID::ValueType_Button: // Button is stored as a bool
case ValueID::ValueType_Bool: // bool
bOriginalEqual = (*((bool*) _targetValue) == *((bool*) _newValue));
break;
case ValueID::ValueType_Raw: // raw
bOriginalEqual = (_targetValueLength == _newValueLength); // first check length of arrays
if (bOriginalEqual)
bOriginalEqual = (memcmp(_targetValue, _newValue, _newValueLength) == 0); // if length is the same, then check content
break;
case ValueID::ValueType_Schedule: // Schedule
/* Should not get here */
break;
case ValueID::ValueType_BitSet: // BitSet
bOriginalEqual = (((Bitfield *) _targetValue)->GetValue() == ((Bitfield *) _newValue)->GetValue());
break;
}
if (bOriginalEqual)
{
/* reset the Bool around TargetValueSet so we
* can handle situations where Target Value is not supplied in the future
*/
m_targetValueSet = false;

Value::OnValueChanged();
return 2; // confirmed change of value
}
/* They are not equal - So we need to issue a Get, But lets pace the timing of the Get based on Duration
* - Caveat here is that if the outgoing queue is large, then this will be additionally delayed
*/
int32 timeout;
if (m_duration <= 5)
{
/* for Durations less than 5 seconds, lets refresh every 1/2 seconds
*/
timeout = 500;
}
else
{
/* Everything else is 1 second
*/
timeout = 1000;
}

TimerThread::TimerCallback callback = bind(&Value::sendValueRefresh, this, 1);
TimerSetEvent(500, callback, 1);

This comment has been minimized.

Copy link
@DanielBaulig

DanielBaulig Jul 8, 2020

Should this 500 be timeout instead?

This comment has been minimized.

Copy link
@DanielBaulig
/* signal that the value hasn't changed */
return 0;
}

//-----------------------------------------------------------------------------
// <Value::sendValueRefresh>
// Callback from the Timer to send a Get value to refresh a value from the
// CheckTargetValue function
//-----------------------------------------------------------------------------
void Value::sendValueRefresh(uint32 _unused)
{
Log::Write(LogLevel_Info, m_id.GetNodeId(), "Sending Get to Refresh Value after Target Check");
if (Driver* driver = Manager::Get()->GetDriver(m_id.GetHomeId()))
{
Node* node = driver->GetNodeUnsafe(m_id.GetNodeId());
if (node != NULL)
{
if (Internal::CC::CommandClass* cc = node->GetCommandClass(m_id.GetCommandClassId()))
{
cc->RequestValue( 0, m_id.GetIndex(), m_id.GetInstance(), Driver::MsgQueue_Send );
}
}
}
}


} // namespace VC
} // namespace Internal
} // namespace OpenZWave
Expand Down
15 changes: 13 additions & 2 deletions cpp/src/value_classes/Value.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <time.h>
#endif
#include "Defs.h"
#include "TimerThread.h"
#include "platform/Ref.h"
#include "value_classes/ValueID.h"
#include "platform/Log.h"
Expand All @@ -50,7 +51,7 @@ namespace OpenZWave
/** \brief Base class for values associated with a node.
* \ingroup ValueID
*/
class Value: public Internal::Platform::Ref
class Value: public Internal::Platform::Ref, private Timer
{
friend class OpenZWave::Driver;
friend class ValueStore;
Expand Down Expand Up @@ -162,6 +163,8 @@ namespace OpenZWave
return Ref::Release();
}
#endif
void sendValueRefresh(uint32 _unused);

protected:
virtual ~Value();

Expand All @@ -173,9 +176,15 @@ namespace OpenZWave
{
m_checkChange = _check;
}
bool IsTargetValueSet() const
{
return m_targetValueSet;
}

void OnValueRefreshed(); // A value in a device has been refreshed
void OnValueChanged(); // The refreshed value actually changed
int VerifyRefreshedValue(void* _originalValue, void* _checkValue, void* _newValue, ValueID::ValueType _type, int _originalValueLength = 0, int _checkValueLength = 0, int _newValueLength = 0);
int VerifyRefreshedValue(void* _originalValue, void* _checkValue, void* _newValue, void* _targetValue, ValueID::ValueType _type, int _originalValueLength = 0, int _checkValueLength = 0, int _newValueLength = 0, int _targetValueLength = 0);
int CheckTargetValue(void* _newValue, void* _targetValue, ValueID::ValueType _type, int _newValueLength, int _targetValueLength);

int32 m_min;
int32 m_max;
Expand All @@ -184,6 +193,8 @@ namespace OpenZWave
bool m_verifyChanges; // if true, apparent changes are verified; otherwise, they're not
bool m_refreshAfterSet; // if true, all value sets are followed by a get to refresh the value manually
ValueID m_id;
bool m_targetValueSet; // If the Target Value is Set
uint32 m_duration; // The Duration, if the CC supports it

private:
string m_units;
Expand Down
13 changes: 12 additions & 1 deletion cpp/src/value_classes/ValueBitSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,13 +341,24 @@ namespace OpenZWave
m_size = size;
}

//-----------------------------------------------------------------------------
// <ValueByte::SetTargetValue>
// Set the Value Target (Used for Automatic Refresh)
//-----------------------------------------------------------------------------
void ValueBitSet::SetTargetValue(uint32 const _target, uint32 _duration)
{
m_targetValueSet = true;
m_targetValue = _target;
m_duration = _duration;
}

//-----------------------------------------------------------------------------
// <ValueBitSet::OnValueRefreshed>
// A value in a device has been refreshed
//-----------------------------------------------------------------------------
void ValueBitSet::OnValueRefreshed(uint32 const _value)
{
switch (VerifyRefreshedValue((void*) &m_value, (void*) &m_valueCheck, (void*) &_value, ValueID::ValueType_BitSet))
switch (VerifyRefreshedValue((void*) &m_value, (void*) &m_valueCheck, (void*) &_value, (void*) &m_targetValue, ValueID::ValueType_BitSet))
{
case 0: // value hasn't changed, nothing to do
break;
Expand Down
3 changes: 3 additions & 0 deletions cpp/src/value_classes/ValueBitSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ namespace OpenZWave
bool Set(uint32 const _value);
uint32 GetValue() const;

void SetTargetValue(uint32 const _target, uint32 _duration = 0);

bool SetBit(uint8 const _idx);
bool ClearBit(uint8 const _idx);
bool GetBit(uint8 _idx) const;
Expand Down Expand Up @@ -92,6 +94,7 @@ namespace OpenZWave
uint32 m_BitMask; // Valid Bits
uint8 m_size; // Number of bytes in size
vector<int32> m_bits;
uint32 m_targetValue; // Target Value, if Supported;
};
} // namespace VC
} // namespace Internal
Expand Down
Loading

0 comments on commit 98c7c03

Please sign in to comment.