1 //-----------------------------------------------------------------------------
2 //
3 //	SwitchBinary.cpp
4 //
5 //	Implementation of the Z-Wave COMMAND_CLASS_SWITCH_BINARY
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 //  spec: http://zwavepublic.com/sites/default/files/command_class_specs_2017A/SDS13781-3%20Z-Wave%20Application%20Command%20Class%20Specification.pdf
27 //        pp. 78ff
28 //-----------------------------------------------------------------------------
29 
30 #include "command_classes/CommandClasses.h"
31 #include "command_classes/SwitchBinary.h"
32 #include "command_classes/WakeUp.h"
33 #include "Defs.h"
34 #include "Msg.h"
35 #include "Driver.h"
36 #include "Node.h"
37 #include "platform/Log.h"
38 
39 #include "value_classes/ValueBool.h"
40 #include "value_classes/ValueByte.h"
41 
42 namespace OpenZWave
43 {
44 	namespace Internal
45 	{
46 		namespace CC
47 		{
48 
49 			enum SwitchBinaryCmd
50 			{
51 				SwitchBinaryCmd_Set = 0x01,
52 				SwitchBinaryCmd_Get = 0x02,
53 				SwitchBinaryCmd_Report = 0x03
54 			};
55 
56 //-----------------------------------------------------------------------------
57 // <SwitchBinary::RequestState>
58 // Request current state from the device
59 //-----------------------------------------------------------------------------
RequestState(uint32 const _requestFlags,uint8 const _instance,Driver::MsgQueue const _queue)60 			bool SwitchBinary::RequestState(uint32 const _requestFlags, uint8 const _instance, Driver::MsgQueue const _queue)
61 			{
62 				if (_requestFlags & RequestFlag_Dynamic)
63 				{
64 					return RequestValue(_requestFlags, ValueID_Index_SwitchBinary::Level, _instance, _queue);
65 				}
66 
67 				return false;
68 			}
69 
70 //-----------------------------------------------------------------------------
71 // <SwitchBinary::RequestValue>
72 // Request current value from the device
73 //-----------------------------------------------------------------------------
RequestValue(uint32 const _requestFlags,uint16 const _dummy1,uint8 const _instance,Driver::MsgQueue const _queue)74 			bool SwitchBinary::RequestValue(uint32 const _requestFlags, uint16 const _dummy1,	// = 0 (not used)
75 					uint8 const _instance, Driver::MsgQueue const _queue)
76 			{
77 				if (m_com.GetFlagBool(COMPAT_FLAG_GETSUPPORTED))
78 				{
79 					Msg* msg = new Msg("SwitchBinaryCmd_Get", GetNodeId(), REQUEST, FUNC_ID_ZW_SEND_DATA, true, true, FUNC_ID_APPLICATION_COMMAND_HANDLER, GetCommandClassId());
80 					msg->SetInstance(this, _instance);
81 					msg->Append(GetNodeId());
82 					msg->Append(2);
83 					msg->Append(GetCommandClassId());
84 					msg->Append(SwitchBinaryCmd_Get);
85 					msg->Append(GetDriver()->GetTransmitOptions());
86 					GetDriver()->SendMsg(msg, _queue);
87 					return true;
88 				}
89 				else
90 				{
91 					Log::Write(LogLevel_Info, GetNodeId(), "SwitchBinaryCmd_Get Not Supported on this node");
92 				}
93 				return false;
94 			}
95 
96 //-----------------------------------------------------------------------------
97 // <SwitchBinary::HandleMsg>
98 // Handle a message from the Z-Wave network
99 //-----------------------------------------------------------------------------
HandleMsg(uint8 const * _data,uint32 const _length,uint32 const _instance)100 			bool SwitchBinary::HandleMsg(uint8 const* _data, uint32 const _length, uint32 const _instance	// = 1
101 					)
102 			{
103 				if (SwitchBinaryCmd_Report == (SwitchBinaryCmd) _data[0])
104 				{
105 					Log::Write(LogLevel_Info, GetNodeId(), "Received SwitchBinary report from node %d: level=%s", GetNodeId(), _data[1] ? "On" : "Off");
106 
107 					// data[1] => Switch state
108 					if (Internal::VC::ValueBool* value = static_cast<Internal::VC::ValueBool*>(GetValue(_instance, ValueID_Index_SwitchBinary::Level)))
109 					{
110 						value->OnValueRefreshed(_data[1] != 0);
111 						value->Release();
112 					}
113 
114 					if (GetVersion() >= 2)
115 					{
116 
117 						// data[2] => target state
118 						if (Internal::VC::ValueBool* value = static_cast<Internal::VC::ValueBool*>(GetValue(_instance, ValueID_Index_SwitchBinary::TargetState)))
119 						{
120 							value->OnValueRefreshed(_data[2] != 0);
121 							value->Release();
122 						}
123 
124 						// data[3] might be duration
125 						if (_length > 3)
126 						{
127 							if (Internal::VC::ValueByte* value = static_cast<Internal::VC::ValueByte*>(GetValue(_instance, ValueID_Index_SwitchBinary::Duration)))
128 							{
129 								value->OnValueRefreshed(_data[3]);
130 								value->Release();
131 							}
132 						}
133 					}
134 
135 					return true;
136 				}
137 
138 				return false;
139 			}
140 
141 //-----------------------------------------------------------------------------
142 // <SwitchBinary::SetValue>
143 // Set the state of the switch
144 //-----------------------------------------------------------------------------
SetValue(Internal::VC::Value const & _value)145 			bool SwitchBinary::SetValue(Internal::VC::Value const& _value)
146 			{
147 				bool res = false;
148 				uint8 instance = _value.GetID().GetInstance();
149 
150 				switch (_value.GetID().GetIndex())
151 				{
152 					case ValueID_Index_SwitchBinary::Level:
153 					{
154 						if (Internal::VC::ValueBool* value = static_cast<Internal::VC::ValueBool*>(GetValue(instance, ValueID_Index_SwitchBinary::Level)))
155 						{
156 							res = SetState(instance, (static_cast<Internal::VC::ValueBool const*>(&_value))->GetValue());
157 							value->Release();
158 						}
159 						break;
160 					}
161 					case ValueID_Index_SwitchBinary::Duration:
162 					{
163 						if (Internal::VC::ValueByte* value = static_cast<Internal::VC::ValueByte*>(GetValue(instance, ValueID_Index_SwitchBinary::Duration)))
164 						{
165 							value->OnValueRefreshed((static_cast<Internal::VC::ValueByte const*>(&_value))->GetValue());
166 							value->Release();
167 						}
168 						res = true;
169 						break;
170 					}
171 
172 				}
173 
174 				return res;
175 			}
176 
177 //-----------------------------------------------------------------------------
178 // <SwitchBinary::SetValueBasic>
179 // Update class values based in BASIC mapping
180 //-----------------------------------------------------------------------------
SetValueBasic(uint8 const _instance,uint8 const _value)181 			void SwitchBinary::SetValueBasic(uint8 const _instance, uint8 const _value)
182 			{
183 				// Send a request for new value to synchronize it with the BASIC set/report.
184 				// In case the device is sleeping, we set the value anyway so the BASIC set/report
185 				// stays in sync with it. We must be careful mapping the uint8 BASIC value
186 				// into a class specific value.
187 				// When the device wakes up, the real requested value will be retrieved.
188 				RequestValue(0, 0, _instance, Driver::MsgQueue_Send);
189 				if (Node* node = GetNodeUnsafe())
190 				{
191 					if (Internal::CC::WakeUp* wakeUp = static_cast<Internal::CC::WakeUp*>(node->GetCommandClass(Internal::CC::WakeUp::StaticGetCommandClassId())))
192 					{
193 						if (!wakeUp->IsAwake())
194 						{
195 							if (Internal::VC::ValueBool* value = static_cast<Internal::VC::ValueBool*>(GetValue(_instance, 0)))
196 							{
197 								value->OnValueRefreshed(_value != 0);
198 								value->Release();
199 							}
200 						}
201 					}
202 				}
203 			}
204 
205 //-----------------------------------------------------------------------------
206 // <SwitchBinary::SetState>
207 // Set a new state for the switch
208 //-----------------------------------------------------------------------------
SetState(uint8 const _instance,bool const _state)209 			bool SwitchBinary::SetState(uint8 const _instance, bool const _state)
210 			{
211 				uint8 const nodeId = GetNodeId();
212 				uint8 const targetValue = _state ? 0xff : 0;
213 
214 				Log::Write(LogLevel_Info, nodeId, "SwitchBinary::Set - Setting to %s", _state ? "On" : "Off");
215 				Msg* msg = new Msg("SwitchBinaryCmd_Set", nodeId, REQUEST, FUNC_ID_ZW_SEND_DATA, true);
216 				msg->SetInstance(this, _instance);
217 				msg->Append(nodeId);
218 
219 				if (GetVersion() >= 2)
220 				{
221 					Internal::VC::ValueByte* durationValue = static_cast<Internal::VC::ValueByte*>(GetValue(_instance, ValueID_Index_SwitchBinary::Duration));
222 					uint8 duration = durationValue->GetValue();
223 					durationValue->Release();
224 					if (duration == 0xff)
225 					{
226 						Log::Write(LogLevel_Info, GetNodeId(), "  Duration: Default");
227 					}
228 					else if (duration >= 0x80)
229 					{
230 						Log::Write(LogLevel_Info, GetNodeId(), "  Duration: %d minutes", duration - 0x7f);
231 					}
232 					else
233 					{
234 						Log::Write(LogLevel_Info, GetNodeId(), "  Duration: %d seconds", duration);
235 					}
236 
237 					msg->Append(4);
238 					msg->Append(GetCommandClassId());
239 					msg->Append(SwitchBinaryCmd_Set);
240 					msg->Append(targetValue);
241 					msg->Append(duration);
242 				}
243 				else
244 				{
245 					msg->Append(3);
246 					msg->Append(GetCommandClassId());
247 					msg->Append(SwitchBinaryCmd_Set);
248 					msg->Append(targetValue);
249 				}
250 
251 				msg->Append(GetDriver()->GetTransmitOptions());
252 				GetDriver()->SendMsg(msg, Driver::MsgQueue_Send);
253 				return true;
254 			}
255 
256 //-----------------------------------------------------------------------------
257 // <SwitchBinary::CreateVars>
258 // Create the values managed by this command class
259 //-----------------------------------------------------------------------------
CreateVars(uint8 const _instance)260 			void SwitchBinary::CreateVars(uint8 const _instance)
261 			{
262 				if (Node* node = GetNodeUnsafe())
263 				{
264 					if (GetVersion() >= 2)
265 					{
266 						node->CreateValueByte(ValueID::ValueGenre_System, GetCommandClassId(), _instance, ValueID_Index_SwitchBinary::Duration, "Transition Duration", "", false, false, 0xff, 0);
267 						node->CreateValueBool(ValueID::ValueGenre_System, GetCommandClassId(), _instance, ValueID_Index_SwitchBinary::TargetState, "Target State", "", true, false, true, 0);
268 					}
269 					node->CreateValueBool(ValueID::ValueGenre_User, GetCommandClassId(), _instance, ValueID_Index_SwitchBinary::Level, "Switch", "", false, false, false, 0);
270 				}
271 			}
272 		} // namespace CC
273 	} // namespace Internal
274 } // namespace OpenZWave
275