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