1 //-----------------------------------------------------------------------------
2 //
3 // UserCode.cpp
4 //
5 // Implementation of the Z-Wave COMMAND_CLASS_USER_CODE
6 //
7 // Copyright (c) 2012 Greg Satz <satz@iranger.com>
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 "command_classes/CommandClasses.h"
30 #include "command_classes/UserCode.h"
31 #include "Node.h"
32 #include "Options.h"
33 #include "platform/Log.h"
34
35 #include "value_classes/ValueByte.h"
36 #include "value_classes/ValueRaw.h"
37
38 using namespace OpenZWave;
39
40 enum UserCodeCmd
41 {
42 UserCodeCmd_Set = 0x01,
43 UserCodeCmd_Get = 0x02,
44 UserCodeCmd_Report = 0x03,
45 UserNumberCmd_Get = 0x04,
46 UserNumberCmd_Report = 0x05
47 };
48
49 enum
50 {
51 UserCodeIndex_Refresh = 254,
52 UserCodeIndex_Count = 255
53 };
54
55 const uint8 UserCodeLength = 10;
56
57 //-----------------------------------------------------------------------------
58 // <UserCode::UserCode>
59 // Constructor
60 //-----------------------------------------------------------------------------
UserCode(uint32 const _homeId,uint8 const _nodeId)61 UserCode::UserCode
62 (
63 uint32 const _homeId,
64 uint8 const _nodeId
65 ):
66 CommandClass( _homeId, _nodeId ),
67 m_queryAll( false ),
68 m_currentCode( 0 ),
69 m_userCodeCount( 0 ),
70 m_refreshUserCodes(false)
71 {
72 SetStaticRequest( StaticRequest_Values );
73 memset( m_userCodesStatus, 0xff, sizeof(m_userCodesStatus) );
74 Options::Get()->GetOptionAsBool("RefreshAllUserCodes", &m_refreshUserCodes );
75
76 }
77
78 //-----------------------------------------------------------------------------
79 // <UserCode::ReadXML>
80 // Class specific configuration
81 //-----------------------------------------------------------------------------
ReadXML(TiXmlElement const * _ccElement)82 void UserCode::ReadXML
83 (
84 TiXmlElement const* _ccElement
85 )
86 {
87 int32 intVal;
88
89 CommandClass::ReadXML( _ccElement );
90 if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "codes", &intVal ) )
91 {
92 m_userCodeCount = intVal;
93 }
94 }
95
96 //-----------------------------------------------------------------------------
97 // <UserCode::WriteXML>
98 // Class specific configuration
99 //-----------------------------------------------------------------------------
WriteXML(TiXmlElement * _ccElement)100 void UserCode::WriteXML
101 (
102 TiXmlElement* _ccElement
103 )
104 {
105 char str[32];
106
107 CommandClass::WriteXML( _ccElement );
108 snprintf( str, sizeof(str), "%d", m_userCodeCount );
109 _ccElement->SetAttribute( "codes", str);
110 }
111
112 //-----------------------------------------------------------------------------
113 // <UserCode::RequestState>
114 // Nothing to do for UserCode
115 //-----------------------------------------------------------------------------
RequestState(uint32 const _requestFlags,uint8 const _instance,Driver::MsgQueue const _queue)116 bool UserCode::RequestState
117 (
118 uint32 const _requestFlags,
119 uint8 const _instance,
120 Driver::MsgQueue const _queue
121 )
122 {
123 bool requests = false;
124 if( ( _requestFlags & RequestFlag_Static ) && HasStaticRequest( StaticRequest_Values ) )
125 {
126 requests |= RequestValue( _requestFlags, UserCodeIndex_Count, _instance, _queue );
127 }
128
129 if( _requestFlags & RequestFlag_Session )
130 {
131 if( m_userCodeCount > 0 )
132 {
133 m_queryAll = true;
134 m_currentCode = 1;
135 requests |= RequestValue( _requestFlags, m_currentCode, _instance, _queue );
136 }
137 }
138
139 return requests;
140 }
141
142 //-----------------------------------------------------------------------------
143 // <UserCode::RequestValue>
144 // Nothing to do for UserCode
145 //-----------------------------------------------------------------------------
RequestValue(uint32 const _requestFlags,uint8 const _userCodeIdx,uint8 const _instance,Driver::MsgQueue const _queue)146 bool UserCode::RequestValue
147 (
148 uint32 const _requestFlags,
149 uint8 const _userCodeIdx,
150 uint8 const _instance,
151 Driver::MsgQueue const _queue
152 )
153 {
154 if( _instance != 1 )
155 {
156 // This command class doesn't work with multiple instances
157 return false;
158 }
159 if ( !IsGetSupported() )
160 {
161 Log::Write( LogLevel_Info, GetNodeId(), "UserNumberCmd_Get Not Supported on this node");
162 return false;
163 }
164 if( _userCodeIdx == UserCodeIndex_Count )
165 {
166 // Get number of supported user codes.
167 Msg* msg = new Msg( "UserNumberCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
168 msg->Append( GetNodeId() );
169 msg->Append( 2 );
170 msg->Append( GetCommandClassId() );
171 msg->Append( UserNumberCmd_Get );
172 msg->Append( GetDriver()->GetTransmitOptions() );
173 GetDriver()->SendMsg( msg, _queue );
174 return true;
175 }
176 if (_userCodeIdx == 0)
177 {
178 Log::Write( LogLevel_Warning, GetNodeId(), "UserCodeCmd_Get with Index 0 not Supported");
179 return false;
180 }
181 Msg* msg = new Msg( "UserCodeCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
182 msg->Append( GetNodeId() );
183 msg->Append( 3 );
184 msg->Append( GetCommandClassId() );
185 msg->Append( UserCodeCmd_Get );
186 msg->Append( _userCodeIdx );
187 msg->Append( GetDriver()->GetTransmitOptions() );
188 GetDriver()->SendMsg( msg, _queue );
189 return true;
190 }
191
192 //-----------------------------------------------------------------------------
193 // <UserCode::HandleMsg>
194 // Handle a message from the Z-Wave network
195 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)196 bool UserCode::HandleMsg
197 (
198 uint8 const* _data,
199 uint32 const _length,
200 uint32 const _instance // = 1
201 )
202 {
203 if( UserNumberCmd_Report == (UserCodeCmd)_data[0] )
204 {
205 m_userCodeCount = _data[1];
206 if( m_userCodeCount > 254 )
207 {
208 // Make space for code count.
209 m_userCodeCount = 254;
210 }
211 ClearStaticRequest( StaticRequest_Values );
212 if( m_userCodeCount == 0 )
213 {
214 Log::Write( LogLevel_Info, GetNodeId(), "Received User Number report from node %d: Not supported", GetNodeId() );
215 }
216 else
217 {
218 Log::Write( LogLevel_Info, GetNodeId(), "Received User Number report from node %d: Supported Codes %d (%d)", GetNodeId(), m_userCodeCount, _data[1] );
219 }
220
221 if( ValueByte* value = static_cast<ValueByte*>( GetValue( _instance, UserCodeIndex_Count ) ) )
222 {
223 value->OnValueRefreshed( m_userCodeCount );
224 value->Release();
225 }
226
227 if( Node* node = GetNodeUnsafe() )
228 {
229 uint8 data[UserCodeLength];
230
231 memset( data, 0, UserCodeLength );
232 for( uint8 i = 0; i <= m_userCodeCount; i++ )
233 {
234 char str[16];
235 if (i == 0)
236 {
237 snprintf( str, sizeof(str), "Enrollment Code");
238 node->CreateValueRaw( ValueID::ValueGenre_User, GetCommandClassId(), _instance, i, str, "", true, false, data, UserCodeLength, 0 );
239 }
240 else
241 {
242 snprintf( str, sizeof(str), "Code %d:", i);
243 node->CreateValueRaw( ValueID::ValueGenre_User, GetCommandClassId(), _instance, i, str, "", false, false, data, UserCodeLength, 0 );
244 }
245 }
246 }
247 return true;
248 }
249 else if( UserCodeCmd_Report == (UserCodeCmd)_data[0] )
250 {
251 int i = _data[1];
252 if( ValueRaw* value = static_cast<ValueRaw*>( GetValue( _instance, i ) ) )
253 {
254 uint8 data[UserCodeLength];
255 int8 size = _length - 4;
256 if( size > UserCodeLength )
257 {
258 Log::Write( LogLevel_Warning, GetNodeId(), "User Code length %d is larger then maximum %d", size, UserCodeLength );
259 size = UserCodeLength;
260 }
261 Log::Write( LogLevel_Info, GetNodeId(), "User Code Packet is %d", size );
262 m_userCodesStatus[i] = _data[2];
263 if (size > 0) {
264 memcpy( data, &_data[3], size );
265 } else {
266 size = 1;
267 data[0] = 0;
268 }
269 value->OnValueRefreshed( data, size );
270 value->Release();
271 }
272 Log::Write( LogLevel_Info, GetNodeId(), "Received User Code Report from node %d for User Code %d (%s)", GetNodeId(), i, CodeStatus( _data[2] ).c_str() );
273 if( m_queryAll && i == m_currentCode )
274 {
275
276 if (m_refreshUserCodes || (_data[2] != UserCode_Available)) {
277 if( ++i <= m_userCodeCount )
278 {
279 m_currentCode = i;
280 RequestValue( 0, m_currentCode, _instance, Driver::MsgQueue_Query );
281 }
282 else
283 {
284 m_queryAll = false;
285 /* we might have reset this as part of the RefreshValues Button Value */
286 Options::Get()->GetOptionAsBool("RefreshAllUserCodes", &m_refreshUserCodes );
287 }
288 } else {
289 Log::Write( LogLevel_Info, GetNodeId(), "Not Requesting additional UserCode Slots as RefreshAllUserCodes is false, and slot %d is available", i);
290 m_queryAll = false;
291 }
292 }
293 return true;
294 }
295
296 return false;
297 }
298
299 //-----------------------------------------------------------------------------
300 // <UserCode::SetValue>
301 // Set a User Code value
302 //-----------------------------------------------------------------------------
SetValue(Value const & _value)303 bool UserCode::SetValue
304 (
305 Value const& _value
306 )
307 {
308 if( (ValueID::ValueType_Raw == _value.GetID().GetType()) && (_value.GetID().GetIndex() < UserCodeIndex_Refresh) )
309 {
310 ValueRaw const* value = static_cast<ValueRaw const*>(&_value);
311 uint8* s = value->GetValue();
312 uint8 len = value->GetLength();
313
314 if( len > UserCodeLength )
315 {
316 return false;
317 }
318 m_userCodesStatus[value->GetID().GetIndex()] = UserCode_Occupied;
319 Msg* msg = new Msg( "UserCodeCmd_Set", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
320 msg->SetInstance( this, _value.GetID().GetInstance() );
321 msg->Append( GetNodeId() );
322 msg->Append( 4 + len );
323 msg->Append( GetCommandClassId() );
324 msg->Append( UserCodeCmd_Set );
325 msg->Append( value->GetID().GetIndex() );
326 msg->Append( UserCode_Occupied );
327 for( uint8 i = 0; i < len; i++ )
328 {
329 msg->Append( s[i] );
330 }
331 msg->Append( GetDriver()->GetTransmitOptions() );
332 GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
333 return true;
334 }
335 if ( (ValueID::ValueType_Button == _value.GetID().GetType()) && (_value.GetID().GetIndex() == UserCodeIndex_Refresh) )
336 {
337 m_refreshUserCodes = true;
338 m_currentCode = 1;
339 m_queryAll = true;
340 RequestValue( 0, m_currentCode, _value.GetID().GetInstance(), Driver::MsgQueue_Query );
341 return true;
342 }
343 return false;
344 }
345
346 //-----------------------------------------------------------------------------
347 // <UserCode::CreateVars>
348 // Create the values managed by this command class
349 //-----------------------------------------------------------------------------
CreateVars(uint8 const _instance)350 void UserCode::CreateVars
351 (
352 uint8 const _instance
353
354 )
355 {
356 if( Node* node = GetNodeUnsafe() )
357 {
358 node->CreateValueByte( ValueID::ValueGenre_System, GetCommandClassId(), _instance, UserCodeIndex_Count, "Code Count", "", true, false, 0, 0 );
359 node->CreateValueButton( ValueID::ValueGenre_System, GetCommandClassId(), _instance, UserCodeIndex_Refresh, "Refresh All UserCodes", 0);
360 }
361 }
362