1 //-----------------------------------------------------------------------------
2 //
3 //	Meter.cpp
4 //
5 //	Implementation of the Z-Wave COMMAND_CLASS_METER
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/Meter.h"
30 #include "command_classes/MultiInstance.h"
31 #include "Defs.h"
32 #include "Bitfield.h"
33 #include "Msg.h"
34 #include "Node.h"
35 #include "Driver.h"
36 #include "platform/Log.h"
37 
38 #include "value_classes/ValueDecimal.h"
39 #include "value_classes/ValueList.h"
40 #include "value_classes/ValueButton.h"
41 #include "value_classes/ValueInt.h"
42 #include "value_classes/ValueBool.h"
43 
44 using namespace OpenZWave;
45 
46 enum MeterCmd
47 {
48 	MeterCmd_Get				= 0x01,
49 	MeterCmd_Report				= 0x02,
50 	// Version 2
51 	MeterCmd_SupportedGet			= 0x03,
52 	MeterCmd_SupportedReport		= 0x04,
53 	MeterCmd_Reset				= 0x05
54 };
55 
56 enum MeterType
57 {
58 	MeterType_Electric = 1,
59 	MeterType_Gas,
60 	MeterType_Water
61 };
62 
63 enum
64 {
65 	MeterIndex_Exporting = 32,
66 	MeterIndex_Reset
67 };
68 
69 
70 static char const* c_meterTypes[] =
71 {
72 	"Unknown",
73 	"Electric",
74 	"Gas",
75 	"Water"
76 };
77 
78 static char const* c_electricityUnits[] =
79 {
80 	"kWh",
81 	"kVAh",
82 	"W",
83 	"pulses",
84 	"V",
85 	"A",
86 	"Power Factor",
87 	""
88 };
89 
90 static char const* c_gasUnits[] =
91 {
92 	"cubic meters",
93 	"cubic feet",
94 	"",
95 	"pulses",
96 	"",
97 	"",
98 	"",
99 	""
100 };
101 
102 static char const* c_waterUnits[] =
103 {
104 	"cubic meters",
105 	"cubic feet",
106 	"US gallons",
107 	"pulses",
108 	"",
109 	"",
110 	"",
111 	""
112 };
113 
114 static char const* c_electricityLabels[] =
115 {
116 	"Energy",
117 	"Energy",
118 	"Power",
119 	"Count",
120 	"Voltage",
121 	"Current",
122 	"Power Factor",
123 	"Unknown"
124 };
125 
126 //-----------------------------------------------------------------------------
127 // <Meter::Meter>
128 // Constructor
129 //-----------------------------------------------------------------------------
Meter(uint32 const _homeId,uint8 const _nodeId)130 Meter::Meter
131 (
132 	uint32 const _homeId,
133 	uint8 const _nodeId
134 ):
135 	CommandClass( _homeId, _nodeId )
136 {
137 	SetStaticRequest( StaticRequest_Values );
138 }
139 
140 //-----------------------------------------------------------------------------
141 // <Meter::RequestState>
142 // Request current state from the device
143 //-----------------------------------------------------------------------------
RequestState(uint32 const _requestFlags,uint8 const _instance,Driver::MsgQueue const _queue)144 bool Meter::RequestState
145 (
146 	uint32 const _requestFlags,
147 	uint8 const _instance,
148 	Driver::MsgQueue const _queue
149 )
150 {
151 	bool res = false;
152 	if( GetVersion() > 1 )
153 	{
154 		if( _requestFlags & RequestFlag_Static )
155 		{
156  			Msg* msg = new Msg( "MeterCmd_SupportedGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
157 			msg->SetInstance( this, _instance );
158 			msg->Append( GetNodeId() );
159 			msg->Append( 2 );
160 			msg->Append( GetCommandClassId() );
161 			msg->Append( MeterCmd_SupportedGet );
162 			msg->Append( GetDriver()->GetTransmitOptions() );
163 			GetDriver()->SendMsg( msg, _queue );
164 			res = true;
165 		}
166 	}
167 
168 	if( _requestFlags & RequestFlag_Dynamic )
169 	{
170 		res |= RequestValue( _requestFlags, 0, _instance, _queue );
171 	}
172 
173 	return res;
174 }
175 
176 //-----------------------------------------------------------------------------
177 // <Meter::RequestValue>
178 // Request current value from the device
179 //-----------------------------------------------------------------------------
RequestValue(uint32 const _requestFlags,uint8 const _dummy1,uint8 const _instance,Driver::MsgQueue const _queue)180 bool Meter::RequestValue
181 (
182 	uint32 const _requestFlags,
183 	uint8 const _dummy1,	// = 0 (not used)
184 	uint8 const _instance,
185 	Driver::MsgQueue const _queue
186 )
187 {
188 	bool res = false;
189 	if ( !IsGetSupported())
190 	{
191 		Log::Write(  LogLevel_Info, GetNodeId(), "MeterCmd_Get Not Supported on this node");
192 		return false;
193 	}
194 	for( uint8 i=0; i<8; ++i )
195 	{
196 		uint8 baseIndex = i<<2;
197 
198 		Value* value = GetValue( _instance, baseIndex );
199 		if( value != NULL )
200 		{
201 			value->Release();
202 			Msg* msg = new Msg( "MeterCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
203 			msg->SetInstance( this, _instance );
204 			msg->Append( GetNodeId() );
205 			msg->Append( 3 );
206 			msg->Append( GetCommandClassId() );
207 			msg->Append( MeterCmd_Get );
208 			msg->Append( (uint8)( i << 3 ) );
209 			msg->Append( GetDriver()->GetTransmitOptions() );
210 			GetDriver()->SendMsg( msg, _queue );
211 			res |= true;
212 		}
213 	}
214 	return res;
215 }
216 
217 //-----------------------------------------------------------------------------
218 // <Meter::HandleMsg>
219 // Handle a message from the Z-Wave network
220 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)221 bool Meter::HandleMsg
222 (
223 	uint8 const* _data,
224 	uint32 const _length,
225 	uint32 const _instance	// = 1
226 )
227 {
228 	bool handled = false;
229 	if (MeterCmd_SupportedReport == (MeterCmd)_data[0])
230 	{
231 		handled = HandleSupportedReport( _data, _length, _instance );
232 	}
233 	else if (MeterCmd_Report == (MeterCmd)_data[0])
234 	{
235 		handled = HandleReport( _data, _length, _instance );
236 	}
237 
238 	return handled;
239 }
240 
241 //-----------------------------------------------------------------------------
242 // <Meter::HandleSupportedReport>
243 // Create the values for this command class based on the reported parameters
244 //-----------------------------------------------------------------------------
HandleSupportedReport(uint8 const * _data,uint32 const _length,uint32 const _instance)245 bool Meter::HandleSupportedReport
246 (
247 	uint8 const* _data,
248 	uint32 const _length,
249 	uint32 const _instance
250 )
251 {
252 	bool canReset = ((_data[1] & 0x80) != 0);
253 	int8 meterType = (MeterType)(_data[1] & 0x1f);
254 	if (meterType > 4) /* size of c_meterTypes */
255 	{
256 		Log::Write (LogLevel_Warning, GetNodeId(), "meterType Value was greater than range. Dropping Message");
257 		return false;
258 	}
259 
260 
261 	ClearStaticRequest( StaticRequest_Version );
262 	if( Node* node = GetNodeUnsafe() )
263 	{
264 		string msg;
265 		msg = c_meterTypes[meterType];
266 		msg += ": ";
267 		// Create the list of supported scales
268 		uint8 scaleSupported = _data[2];
269 		if( GetVersion() == 2 )
270 		{
271 			// Only four scales are allowed in version 2
272 			scaleSupported &= 0x0f;
273 		}
274 
275 		for( uint8 i=0; i<8; ++i )
276 		{
277 			if( scaleSupported & (1<<i) )
278 			{
279 				uint8 baseIndex = i<<2;		// We leave space between the value indices to insert previous and time delta values if we need to later on.
280 				switch( meterType )
281 				{
282 					case MeterType_Electric:
283 					{
284 						if( ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, baseIndex ) ) )
285 						{
286 							value->SetLabel( c_electricityLabels[i] );
287 							value->SetUnits( c_electricityUnits[i] );
288 							value->Release();
289 						}
290 						else
291 						{
292 							node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, baseIndex, c_electricityLabels[i], c_electricityUnits[i], true, false, "0.0", 0 );
293 						}
294 						if( i != 0 )
295 							msg += ", ";
296 						msg += c_electricityUnits[i];
297 						break;
298 					}
299 					case MeterType_Gas:
300 					{
301 						if( ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, baseIndex ) ) )
302 						{
303 							value->SetLabel( c_meterTypes[MeterType_Gas] );
304 							value->SetUnits( c_gasUnits[i] );
305 							value->Release();
306 						}
307 						else
308 						{
309 							node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, baseIndex, c_meterTypes[meterType], c_gasUnits[i], true, false, "0.0", 0 );
310 						}
311 						if( i != 0 )
312 							msg += ", ";
313 						msg += c_gasUnits[i];
314 						break;
315 					}
316 					case MeterType_Water:
317 					{
318 						if( ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, baseIndex ) ) )
319 						{
320 							value->SetLabel( c_meterTypes[MeterType_Water] );
321 							value->SetUnits( c_waterUnits[i] );
322 							value->Release();
323 						}
324 						else
325 						{
326 							node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, baseIndex, c_meterTypes[meterType], c_waterUnits[i], true, false, "0.0", 0 );
327 						}
328 						if( i != 0 )
329 							msg += ", ";
330 						msg += c_waterUnits[i];
331 						break;
332 					}
333 					default:
334 					{
335 						break;
336 					}
337 				}
338 			}
339 		}
340 
341 		// Create the export flag
342 		node->CreateValueBool( ValueID::ValueGenre_User, GetCommandClassId(), _instance, MeterIndex_Exporting, "Exporting", "", true, false, false, 0 );
343 
344 		// Create the reset button
345 		if( canReset )
346 		{
347 			node->CreateValueButton( ValueID::ValueGenre_System, GetCommandClassId(), _instance, MeterIndex_Reset, "Reset", 0 );
348 		}
349 
350 		Log::Write( LogLevel_Info, GetNodeId(), "Received Meter supported report from node %d, %s", GetNodeId(), msg.c_str() );
351 		return true;
352 	}
353 
354 	return false;
355 }
356 
357 //-----------------------------------------------------------------------------
358 // <Meter::HandleReport>
359 // Read the reported meter value
360 //-----------------------------------------------------------------------------
HandleReport(uint8 const * _data,uint32 const _length,uint32 const _instance)361 bool Meter::HandleReport
362 (
363 	uint8 const* _data,
364 	uint32 const _length,
365 	uint32 const _instance
366 )
367 {
368 	// Import or Export (only valid in version > 1)
369 	bool exporting = false;
370 	if( GetVersion() > 1 )
371 	{
372 		exporting = ((_data[1] & 0x60) == 0x40 );
373 		if( ValueBool* value = static_cast<ValueBool*>( GetValue( _instance, MeterIndex_Exporting ) ) )
374 		{
375 			value->OnValueRefreshed( exporting );
376 			value->Release();
377 		}
378 	}
379 
380 	// Get the value and scale
381 	uint8 scale;
382 	uint8 precision = 0;
383 	string valueStr = ExtractValue( &_data[2], &scale, &precision );
384 
385 	if (scale > 7) /* size of c_electricityLabels, c_electricityUnits, c_gasUnits, c_waterUnits */
386 	{
387 		Log::Write (LogLevel_Warning, GetNodeId(), "Scale was greater than range. Setting to Invalid");
388 		scale = 7;
389 	}
390 
391 
392 	if( GetVersion() == 1 )
393 	{
394 		// In version 1, we don't know the scale until we get the first value report
395 		string label;
396 		string units;
397 		int8 meterType = (MeterType)(_data[1] & 0x1f);
398 		if (meterType > 4) /* size of c_meterTypes */
399 		{
400 			Log::Write (LogLevel_Warning, GetNodeId(), "meterType Value was greater than range. Dropping Message");
401 			return false;
402 		}
403 
404 		switch( (MeterType)(_data[1] & 0x1f) )
405 		{
406 			case MeterType_Electric:
407 			{
408 				// Electricity Meter
409 				label = c_electricityLabels[scale];
410 				units = c_electricityUnits[scale];
411 				break;
412 			}
413 			case MeterType_Gas:
414 			{
415 				// Gas Meter
416 				label = c_meterTypes[MeterType_Gas];
417 				units = c_gasUnits[scale];
418 				break;
419 			}
420 			case MeterType_Water:
421 			{
422 				// Water Meter
423 				label = c_meterTypes[MeterType_Water];
424 				units = c_waterUnits[scale];
425 				break;
426 			}
427 			default:
428 			{
429 				break;
430 			}
431 		}
432 
433 		if( ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, 0 ) ) )
434 		{
435 			Log::Write( LogLevel_Info, GetNodeId(), "Received Meter report from node %d: %s=%s%s", GetNodeId(), label.c_str(), valueStr.c_str(), units.c_str() );
436 			value->SetLabel( label );
437 			value->SetUnits( units );
438 			value->OnValueRefreshed( valueStr );
439 			if( value->GetPrecision() != precision )
440 			{
441 				value->SetPrecision( precision );
442 			}
443 			value->Release();
444 		}
445 	}
446 	else
447 	{
448 		// Version 2 and above
449 		uint8 baseIndex = scale << 2;
450 		if( GetVersion() > 2 )
451 		{
452 			// In version 3, an extra scale bit is stored in the meter type byte.
453 			scale |= ((_data[1]&0x80)>>5);
454 			baseIndex = scale << 2;
455 		}
456 
457 		if( ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, baseIndex ) ) )
458 		{
459 			Log::Write( LogLevel_Info, GetNodeId(), "Received Meter report from node %d: %s%s=%s%s", GetNodeId(), exporting ? "Exporting ": "", value->GetLabel().c_str(), valueStr.c_str(), value->GetUnits().c_str() );
460 			value->OnValueRefreshed( valueStr );
461 			if( value->GetPrecision() != precision )
462 			{
463 				value->SetPrecision( precision );
464 			}
465 			value->Release();
466 
467 			// Read any previous value and time delta
468 			uint8 size = _data[2] & 0x07;
469 			uint16 delta = (uint16)( (_data[3+size]<<8) | _data[4+size]);
470 
471 			if( delta )
472 			{
473 				// There is only a previous value if the time delta is non-zero
474 				ValueDecimal* previous = static_cast<ValueDecimal*>( GetValue( _instance, baseIndex+1 ) );
475 				if( NULL == previous )
476 				{
477 					// We need to create a value to hold the previous
478 					if( Node* node = GetNodeUnsafe() )
479 					{
480 						node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, baseIndex+1, "Previous Reading", value->GetUnits().c_str(), true, false, "0.0", 0 );
481 						previous = static_cast<ValueDecimal*>( GetValue( _instance, baseIndex+1 ) );
482 					}
483 				}
484 				if( previous )
485 				{
486 					precision = 0;
487 					valueStr = ExtractValue( &_data[2], &scale, &precision, 3+size );
488 					Log::Write( LogLevel_Info, GetNodeId(), "    Previous value was %s%s, received %d seconds ago.", valueStr.c_str(), previous->GetUnits().c_str(), delta );
489 					previous->OnValueRefreshed( valueStr );
490 					if( previous->GetPrecision() != precision )
491 					{
492 						previous->SetPrecision( precision );
493 					}
494 					previous->Release();
495 				}
496 
497 				// Time delta
498 				ValueInt* interval = static_cast<ValueInt*>( GetValue( _instance, baseIndex+2 ) );
499 				if( NULL == interval )
500 				{
501 					// We need to create a value to hold the time delta
502 					if( Node* node = GetNodeUnsafe() )
503 					{
504 						node->CreateValueInt( ValueID::ValueGenre_User, GetCommandClassId(), _instance, baseIndex+2, "Interval", "seconds", true, false, 0, 0 );
505 						interval = static_cast<ValueInt*>( GetValue( _instance, baseIndex+2 ) );
506 					}
507 				}
508 				if( interval )
509 				{
510 		 			interval->OnValueRefreshed( (int32)delta );
511 					interval->Release();
512 				}
513 			}
514 		}
515 	}
516 
517 	return true;
518 }
519 
520 //-----------------------------------------------------------------------------
521 // <Meter::SetValue>
522 // Set the device's scale, or reset its accumulated values.
523 //-----------------------------------------------------------------------------
SetValue(Value const & _value)524 bool Meter::SetValue
525 (
526 	Value const& _value
527 )
528 {
529 	if( MeterIndex_Reset == _value.GetID().GetIndex() )
530 	{
531 		ValueButton const* button = static_cast<ValueButton const*>(&_value);
532 		if( button->IsPressed() )
533 		{
534 			Msg* msg = new Msg( "MeterCmd_Reset", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
535 			msg->SetInstance( this, _value.GetID().GetInstance() );
536 			msg->Append( GetNodeId() );
537 			msg->Append( 2 );
538 			msg->Append( GetCommandClassId() );
539 			msg->Append( MeterCmd_Reset );
540 			msg->Append( GetDriver()->GetTransmitOptions() );
541 			GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
542 			return true;
543 		}
544 	}
545 
546 	return false;
547 }
548 
549 //-----------------------------------------------------------------------------
550 // <Meter::CreateVars>
551 // Create the values managed by this command class
552 //-----------------------------------------------------------------------------
CreateVars(uint8 const _instance)553 void Meter::CreateVars
554 (
555 	uint8 const _instance
556 )
557 {
558 	if( Node* node = GetNodeUnsafe() )
559 	{
560 		node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, 0, "Unknown", "", true, false, "0.0", 0 );
561 	}
562 }
563