1 //-----------------------------------------------------------------------------
2 //
3 //	ThermostatSetpoint.cpp
4 //
5 //	Implementation of the Z-Wave COMMAND_CLASS_THERMOSTAT_SETPOINT
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/ThermostatSetpoint.h"
30 #include "Defs.h"
31 #include "Msg.h"
32 #include "Node.h"
33 #include "Driver.h"
34 #include "platform/Log.h"
35 
36 #include "value_classes/ValueDecimal.h"
37 
38 #include "tinyxml.h"
39 
40 using namespace OpenZWave;
41 
42 enum ThermostatSetpointCmd
43 {
44 	ThermostatSetpointCmd_Set				= 0x01,
45 	ThermostatSetpointCmd_Get				= 0x02,
46 	ThermostatSetpointCmd_Report			= 0x03,
47 	ThermostatSetpointCmd_SupportedGet		= 0x04,
48 	ThermostatSetpointCmd_SupportedReport	= 0x05
49 };
50 
51 enum
52 {
53 	ThermostatSetpoint_Unused0	= 0,
54 	ThermostatSetpoint_Heating1,
55 	ThermostatSetpoint_Cooling1,
56 	ThermostatSetpoint_Unused3,
57 	ThermostatSetpoint_Unused4,
58 	ThermostatSetpoint_Unused5,
59 	ThermostatSetpoint_Unused6,
60 	ThermostatSetpoint_Furnace,
61 	ThermostatSetpoint_DryAir,
62 	ThermostatSetpoint_MoistAir,
63 	ThermostatSetpoint_AutoChangeover,
64 	ThermostatSetpoint_HeatingEcon,
65 	ThermostatSetpoint_CoolingEcon,
66 	ThermostatSetpoint_AwayHeating,
67 	ThermostatSetpoint_Count
68 };
69 
70 static char const* c_setpointName[] =
71 {
72 	"Unused 0",
73 	"Heating 1",
74 	"Cooling 1",
75 	"Unused 3",
76 	"Unused 4",
77 	"Unused 5",
78 	"Unused 6",
79 	"Furnace",
80 	"Dry Air",
81 	"Moist Air",
82 	"Auto Changeover",
83 	"Heating Econ",
84 	"Cooling Econ",
85 	"Away Heating"
86 };
87 
88 //-----------------------------------------------------------------------------
89 // <ThermostatSetpoint::ThermostatSetpoint>
90 // Constructor
91 //-----------------------------------------------------------------------------
ThermostatSetpoint(uint32 const _homeId,uint8 const _nodeId)92 ThermostatSetpoint::ThermostatSetpoint
93 (
94 	uint32 const _homeId,
95 	uint8 const _nodeId
96 ):
97 	CommandClass( _homeId, _nodeId ), m_setPointBase( 1 )
98 {
99 	SetStaticRequest( StaticRequest_Values );
100 }
101 
102 //-----------------------------------------------------------------------------
103 // <ThermostatSetpoint::ReadXML>
104 // Read the saved change-counter value
105 //-----------------------------------------------------------------------------
ReadXML(TiXmlElement const * _ccElement)106 void ThermostatSetpoint::ReadXML
107 (
108 	TiXmlElement const* _ccElement
109 )
110 {
111 	CommandClass::ReadXML( _ccElement );
112 
113 	int intVal;
114 	if( TIXML_SUCCESS == _ccElement->QueryIntAttribute( "base", &intVal ) )
115 	{
116 		m_setPointBase = (uint8)intVal;
117 	}
118 }
119 
120 //-----------------------------------------------------------------------------
121 // <ThermostatSetpoint::WriteXML>
122 // Write the change-counter value
123 //-----------------------------------------------------------------------------
WriteXML(TiXmlElement * _ccElement)124 void ThermostatSetpoint::WriteXML
125 (
126 	TiXmlElement* _ccElement
127 )
128 {
129 	CommandClass::WriteXML( _ccElement );
130 
131 	char str[8];
132 	snprintf( str, 8, "%d", m_setPointBase );
133 	_ccElement->SetAttribute( "base", str );
134 }
135 
136 //-----------------------------------------------------------------------------
137 // <ThermostatSetpoint::RequestState>
138 // Get the static thermostat setpoint details from the device
139 //-----------------------------------------------------------------------------
RequestState(uint32 const _requestFlags,uint8 const _instance,Driver::MsgQueue const _queue)140 bool ThermostatSetpoint::RequestState
141 (
142 	uint32 const _requestFlags,
143 	uint8 const _instance,
144 	Driver::MsgQueue const _queue
145 )
146 {
147 	bool requests = false;
148 	if( ( _requestFlags & RequestFlag_Static ) && HasStaticRequest( StaticRequest_Values ) )
149 	{
150 		requests |= RequestValue( _requestFlags, 0xff, _instance, _queue );
151 	}
152 
153 	if( _requestFlags & RequestFlag_Session )
154 	{
155 		for( uint8 i=0; i<ThermostatSetpoint_Count; ++i )
156 		{
157 			requests |= RequestValue( _requestFlags, i, _instance, _queue );
158 		}
159 	}
160 
161 	return requests;
162 }
163 
164 //-----------------------------------------------------------------------------
165 // <ThermostatSetpoint::RequestValue>
166 // Request current state from the device
167 //-----------------------------------------------------------------------------
RequestValue(uint32 const _requestFlags,uint8 const _setPointIndex,uint8 const _instance,Driver::MsgQueue const _queue)168 bool ThermostatSetpoint::RequestValue
169 (
170 	uint32 const _requestFlags,
171 	uint8 const _setPointIndex,
172 	uint8 const _instance,
173 	Driver::MsgQueue const _queue
174 )
175 {
176 	if( _setPointIndex == 0xff )		// check for supportedget
177 	{
178 		// Request the supported setpoints
179 		Msg* msg = new Msg( "ThermostatSetpointCmd_SupportedGet", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
180 		msg->SetInstance( this, _instance );
181 		msg->Append( GetNodeId() );
182 		msg->Append( 2 );
183 		msg->Append( GetCommandClassId() );
184 		msg->Append( ThermostatSetpointCmd_SupportedGet );
185 		msg->Append( GetDriver()->GetTransmitOptions() );
186 		GetDriver()->SendMsg( msg, _queue );
187 		return true;
188 	}
189 	if ( !IsGetSupported() )
190 	{
191 		Log::Write(  LogLevel_Info, GetNodeId(), "ThermostatSetpointCmd_Get Not Supported on this node");
192 		return false;
193 	}
194 	Value* value = GetValue( 1, _setPointIndex );
195 	if( value != NULL )
196 	{
197 		value->Release();
198 		// Request the setpoint value
199 		Msg* msg = new Msg( "ThermostatSetpointCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId() );
200 		msg->SetInstance( this, _instance );
201 		msg->Append( GetNodeId() );
202 		msg->Append( 3 );
203 		msg->Append( GetCommandClassId() );
204 		msg->Append( ThermostatSetpointCmd_Get );
205 		msg->Append( _setPointIndex );
206 		msg->Append( GetDriver()->GetTransmitOptions() );
207 		GetDriver()->SendMsg( msg, _queue );
208 		return true;
209 	}
210 	return false;
211 }
212 
213 //-----------------------------------------------------------------------------
214 // <ThermostatSetpoint::HandleMsg>
215 // Handle a message from the Z-Wave network
216 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)217 bool ThermostatSetpoint::HandleMsg
218 (
219 	uint8 const* _data,
220 	uint32 const _length,
221 	uint32 const _instance	// = 1
222 )
223 {
224 	if( ThermostatSetpointCmd_Report == (ThermostatSetpointCmd)_data[0] )
225 	{
226 		// We have received a thermostat setpoint value from the Z-Wave device
227 		if( ValueDecimal* value = static_cast<ValueDecimal*>( GetValue( _instance, _data[1] ) ) )
228 		{
229 			uint8 scale;
230 			uint8 precision = 0;
231 			string temperature = ExtractValue( &_data[2], &scale, &precision );
232 
233 			value->SetUnits( scale ? "F" : "C" );
234 			value->OnValueRefreshed( temperature );
235 			if( value->GetPrecision() != precision )
236 			{
237 				value->SetPrecision( precision );
238 			}
239 			value->Release();
240 
241 			Log::Write( LogLevel_Info, GetNodeId(), "Received thermostat setpoint report: Setpoint %s = %s%s", value->GetLabel().c_str(), value->GetValue().c_str(), value->GetUnits().c_str() );
242 		}
243 		return true;
244 	}
245 
246 	if( ThermostatSetpointCmd_SupportedReport == (ThermostatSetpointCmd)_data[0] )
247 	{
248 		if( Node* node = GetNodeUnsafe() )
249 		{
250 			// We have received the supported thermostat setpoints from the Z-Wave device
251 			Log::Write( LogLevel_Info, GetNodeId(), "Received supported thermostat setpoints" );
252 
253 			// Parse the data for the supported setpoints
254 			for( uint32 i=1; i<_length-1; ++i )
255 			{
256 				for( int32 bit=0; bit<8; ++bit )
257 				{
258 					if( ( _data[i] & (1<<bit) ) != 0 )
259 					{
260 						// Add supported setpoint
261 						int32 index = (int32)((i-1)<<3) + bit + m_setPointBase;
262 						if( index < ThermostatSetpoint_Count )
263 						{
264 						  	node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, index, c_setpointName[index], "C", false, false, "0.0", 0 );
265 							Log::Write( LogLevel_Info, GetNodeId(), "    Added setpoint: %s", c_setpointName[index] );
266 						}
267 					}
268 				}
269 			}
270 		}
271 
272 		ClearStaticRequest( StaticRequest_Values );
273 		return true;
274 	}
275 
276 	return false;
277 }
278 
279 //-----------------------------------------------------------------------------
280 // <ThermostatSetpoint::SetValue>
281 // Set a thermostat setpoint temperature
282 //-----------------------------------------------------------------------------
SetValue(Value const & _value)283 bool ThermostatSetpoint::SetValue
284 (
285 	Value const& _value
286 )
287 {
288 	if( ValueID::ValueType_Decimal == _value.GetID().GetType() )
289 	{
290 		ValueDecimal const* value = static_cast<ValueDecimal const*>(&_value);
291 		uint8 scale = strcmp( "C", value->GetUnits().c_str() ) ? 1 : 0;
292 
293 		Msg* msg = new Msg( "ThermostatSetpointCmd_Set", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true );
294 		msg->SetInstance( this, _value.GetID().GetInstance() );
295 		msg->Append( GetNodeId() );
296 		msg->Append( 4 + GetAppendValueSize( value->GetValue() ) );
297 		msg->Append( GetCommandClassId() );
298 		msg->Append( ThermostatSetpointCmd_Set );
299 		msg->Append( value->GetID().GetIndex() );
300 		AppendValue( msg, value->GetValue(), scale );
301 		msg->Append( GetDriver()->GetTransmitOptions() );
302 		GetDriver()->SendMsg( msg, Driver::MsgQueue_Send );
303 		return true;
304 	}
305 
306 	return false;
307 }
308 
309 //-----------------------------------------------------------------------------
310 // <ThermostatSetpoint::CreateVars>
311 // Create the values managed by this command class
312 //-----------------------------------------------------------------------------
CreateVars(uint8 const _instance,uint8 const _index)313 void ThermostatSetpoint::CreateVars
314 (
315 	uint8 const _instance,
316 	uint8 const _index
317 )
318 {
319 	if( Node* node = GetNodeUnsafe() )
320 	{
321 	  	node->CreateValueDecimal( ValueID::ValueGenre_User, GetCommandClassId(), _instance, _index, "Setpoint", "C", false, false, "0.0", 0  );
322 	}
323 }
324