1 // Copyright 2015 The Weave Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "examples/daemon/common/daemon.h"
6
7 #include <weave/device.h>
8 #include <weave/provider/task_runner.h>
9
10 #include <base/bind.h>
11 #include <base/memory/weak_ptr.h>
12
13 namespace {
14 // Time for sensor temperature to match setting temperature
15 const double kWarmUpTime = 60.0;
16 // Oven max temp
17 const double kMaxTemp = 300.0;
18 // Oven min temp
19 const double kMinTemp = 20.0;
20
21 const char kTraits[] = R"({
22 "temperatureSetting": {
23 "commands": {
24 "setConfig": {
25 "minimalRole": "user",
26 "parameters": {
27 "units": {
28 "type": "string"
29 },
30 "tempSetting": {
31 "type": "number"
32 }
33 },
34 "errors": ["tempOutOfRange", "unsupportedUnits"]
35 }
36 },
37 "state": {
38 "supportedUnits": {
39 "type": "array",
40 "items": {
41 "type": "string",
42 "enum": [ "celsius", "fahrenheit", "kelvin" ]
43 },
44 "minItems": 1,
45 "uniqueItems": true,
46 "isRequired": true
47 },
48 "units": {
49 "type": "string",
50 "enum": [ "celsius", "fahrenheit", "kelvin" ],
51 "isRequired": true
52 },
53 "tempSetting": {
54 "type": "number",
55 "isRequired": true
56 },
57 "maxTempSetting": {
58 "type": "number",
59 "isRequired": true
60 },
61 "minTempSetting": {
62 "type": "number",
63 "isRequired": true
64 }
65 }
66 },
67 "temperatureSensor": {
68 "commands": {
69 "setConfig": {
70 "minimalRole": "user",
71 "parameters": {
72 "units": {
73 "type": "string"
74 }
75 },
76 "errors": ["unsupportedUnits"]
77 }
78 },
79 "state": {
80 "supportedUnits": {
81 "type": "array",
82 "items": {
83 "type": "string",
84 "enum": [
85 "celsius",
86 "fahrenheit",
87 "kelvin"
88 ]
89 },
90 "minItems": 1,
91 "uniqueItems": true,
92 "isRequired": true
93 },
94 "units": {
95 "type": "string",
96 "enum": [ "celsius", "fahrenheit", "kelvin" ],
97 "isRequired": true
98 },
99 "value": {
100 "type": "number",
101 "isRequired": true
102 }
103 }
104 },
105 "brightness": {
106 "commands": {
107 "setConfig": {
108 "minimalRole": "user",
109 "parameters": {
110 "brightness": {
111 "type": "integer",
112 "minimum": 0,
113 "maximum": 100
114 }
115 }
116 }
117 },
118 "state": {
119 "brightness": {
120 "type": "integer",
121 "isRequired": true,
122 "minimum": 0,
123 "maximum": 100
124 }
125 }
126 }
127 })";
128
129 const char kComponent[] = "oven";
130 } // anonymous namespace
131
132 // OvenHandler is a virtual oven example
133 // It implements the following commands from traits:
134 // - temperatureSetting: sets the temperature for the oven
135 // - brightness: sets the brightness of the oven light
136 // It exposes the following states from traits:
137 // - temperatureSetting: temperature setting for the oven
138 // - temperatureSensor: current oven temperature
139 // - brightness: current oven brightness
140 class OvenHandler {
141 public:
OvenHandler(weave::provider::TaskRunner * task_runner)142 OvenHandler(weave::provider::TaskRunner* task_runner)
143 : task_runner_{task_runner} {}
144
Register(weave::Device * device)145 void Register(weave::Device* device) {
146 device_ = device;
147
148 device->AddTraitDefinitionsFromJson(kTraits);
149 CHECK(device->AddComponent(
150 kComponent, {"temperatureSetting", "temperatureSensor", "brightness"},
151 nullptr));
152
153 UpdateOvenState();
154
155 device->AddCommandHandler(kComponent, "temperatureSetting.setConfig",
156 base::Bind(&OvenHandler::OnSetTempCommand,
157 weak_ptr_factory_.GetWeakPtr()));
158
159 device->AddCommandHandler(kComponent, "brightness.setConfig",
160 base::Bind(&OvenHandler::OnSetBrightnessCommand,
161 weak_ptr_factory_.GetWeakPtr()));
162 }
163
164 private:
OnSetTempCommand(const std::weak_ptr<weave::Command> & command)165 void OnSetTempCommand(const std::weak_ptr<weave::Command>& command) {
166 auto cmd = command.lock();
167 if (!cmd)
168 return;
169 LOG(INFO) << "received command: " << cmd->GetName();
170
171 const auto& params = cmd->GetParameters();
172 std::string units;
173 double temp;
174
175 if (params.GetString("units", &units) &&
176 params.GetDouble("tempSetting", &temp)) {
177 units_ = units;
178 target_temperature_ = temp;
179
180 UpdateOvenState();
181
182 cmd->Complete({}, nullptr);
183 LOG(INFO) << cmd->GetName() << " updated oven, matching temp";
184
185 if (target_temperature_ != current_temperature_ && !is_match_ticking_) {
186 double tickIncrement =
187 ((target_temperature_ - current_temperature_) / kWarmUpTime);
188 DoTick(tickIncrement);
189 }
190 return;
191 }
192
193 weave::ErrorPtr error;
194 weave::Error::AddTo(&error, FROM_HERE, "invalid_parameter_value",
195 "Invalid parameters");
196 cmd->Abort(error.get(), nullptr);
197 }
198
OnSetBrightnessCommand(const std::weak_ptr<weave::Command> & command)199 void OnSetBrightnessCommand(const std::weak_ptr<weave::Command>& command) {
200 auto cmd = command.lock();
201 if (!cmd)
202 return;
203 LOG(INFO) << "received command: " << cmd->GetName();
204
205 const auto& params = cmd->GetParameters();
206
207 int brightness;
208 if (params.GetInteger("brightness", &brightness)) {
209 brightness_ = brightness;
210
211 UpdateOvenState();
212
213 cmd->Complete({}, nullptr);
214 return;
215 }
216
217 weave::ErrorPtr error;
218 weave::Error::AddTo(&error, FROM_HERE, "invalid_parameter_value",
219 "Invalid parameters");
220 cmd->Abort(error.get(), nullptr);
221 }
222
UpdateOvenState()223 void UpdateOvenState() {
224 base::DictionaryValue state;
225 base::ListValue supportedUnits;
226 supportedUnits.AppendStrings({"celsius"});
227
228 state.SetString("temperatureSensor.units", units_);
229 state.SetDouble("temperatureSensor.value", current_temperature_);
230 state.Set("temperatureSensor.supportedUnits", supportedUnits.DeepCopy());
231
232 state.SetString("temperatureSetting.units", units_);
233 state.SetDouble("temperatureSetting.tempSetting", target_temperature_);
234 state.Set("temperatureSetting.supportedUnits", supportedUnits.DeepCopy());
235 state.SetDouble("temperatureSetting.maxTempSetting", kMaxTemp);
236 state.SetDouble("temperatureSetting.minTempSetting", kMinTemp);
237
238 state.SetInteger("brightness.brightness", brightness_);
239
240 device_->SetStateProperties(kComponent, state, nullptr);
241 }
242
DoTick(double tickIncrement)243 void DoTick(double tickIncrement) {
244 LOG(INFO) << "Oven matching temp tick";
245
246 if (std::fabs(target_temperature_ - current_temperature_) >=
247 tickIncrement) {
248 is_match_ticking_ = true;
249 current_temperature_ += tickIncrement;
250 UpdateOvenState();
251 task_runner_->PostDelayedTask(
252 FROM_HERE, base::Bind(&OvenHandler::DoTick,
253 weak_ptr_factory_.GetWeakPtr(), tickIncrement),
254 base::TimeDelta::FromSeconds(1));
255 return;
256 }
257
258 is_match_ticking_ = false;
259 current_temperature_ = target_temperature_;
260 UpdateOvenState();
261
262 LOG(INFO) << "Oven temp matched";
263 }
264
265 weave::Device* device_{nullptr};
266 weave::provider::TaskRunner* task_runner_{nullptr};
267
268 std::string units_ = "celsius";
269 double target_temperature_ = 0.0;
270 double current_temperature_ = 0.0;
271 int brightness_ = 0;
272 bool is_match_ticking_ = false;
273
274 base::WeakPtrFactory<OvenHandler> weak_ptr_factory_{this};
275 };
276
main(int argc,char ** argv)277 int main(int argc, char** argv) {
278 Daemon::Options opts;
279 if (!opts.Parse(argc, argv)) {
280 Daemon::Options::ShowUsage(argv[0]);
281 return 1;
282 }
283 Daemon daemon{opts};
284 OvenHandler handler{daemon.GetTaskRunner()};
285 handler.Register(daemon.GetDevice());
286 daemon.Run();
287 return 0;
288 }
289