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