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