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 
15 const char kTraits[] = R"({
16   "_sample": {
17     "commands": {
18       "hello": {
19         "minimalRole": "user",
20         "parameters": {
21           "name": { "type": "string" }
22         },
23         "results": {
24           "reply": { "type": "string" }
25         }
26       },
27       "ping": {
28         "minimalRole": "user",
29         "parameters": {}
30       },
31       "countdown": {
32         "minimalRole": "user",
33         "parameters": {
34           "seconds": {
35             "type": "integer",
36             "minimum": 1,
37             "maximum": 25
38           }
39         }
40       }
41     },
42     "state": {
43       "pingCount": { "type": "integer" }
44     }
45   }
46 })";
47 
48 const char kComponent[] = "sample";
49 
50 }  // anonymous namespace
51 
52 // SampleHandler is a command handler example.
53 // It implements the following commands:
54 // - _hello: handle a command with an argument and set its results.
55 // - _ping: update device state.
56 // - _countdown: handle long running command and report progress.
57 class SampleHandler {
58  public:
SampleHandler(weave::provider::TaskRunner * task_runner)59   SampleHandler(weave::provider::TaskRunner* task_runner)
60       : task_runner_{task_runner} {}
Register(weave::Device * device)61   void Register(weave::Device* device) {
62     device_ = device;
63 
64     device->AddTraitDefinitionsFromJson(kTraits);
65     CHECK(device->AddComponent(kComponent, {"_sample"}, nullptr));
66     CHECK(device->SetStatePropertiesFromJson(
67         kComponent, R"({"_sample": {"pingCount": 0}})", nullptr));
68 
69     device->AddCommandHandler(kComponent, "_sample.hello",
70                               base::Bind(&SampleHandler::OnHelloCommand,
71                                          weak_ptr_factory_.GetWeakPtr()));
72     device->AddCommandHandler(kComponent, "_sample.ping",
73                               base::Bind(&SampleHandler::OnPingCommand,
74                                          weak_ptr_factory_.GetWeakPtr()));
75     device->AddCommandHandler(kComponent, "_sample.countdown",
76                               base::Bind(&SampleHandler::OnCountdownCommand,
77                                          weak_ptr_factory_.GetWeakPtr()));
78   }
79 
80  private:
OnHelloCommand(const std::weak_ptr<weave::Command> & command)81   void OnHelloCommand(const std::weak_ptr<weave::Command>& command) {
82     auto cmd = command.lock();
83     if (!cmd)
84       return;
85     LOG(INFO) << "received command: " << cmd->GetName();
86 
87     const auto& params = cmd->GetParameters();
88     std::string name;
89     if (!params.GetString("name", &name)) {
90       weave::ErrorPtr error;
91       weave::Error::AddTo(&error, FROM_HERE, "invalid_parameter_value",
92                           "Name is missing");
93       cmd->Abort(error.get(), nullptr);
94       return;
95     }
96 
97     base::DictionaryValue result;
98     result.SetString("reply", "Hello " + name);
99     cmd->Complete(result, nullptr);
100     LOG(INFO) << cmd->GetName() << " command finished: " << result;
101   }
102 
OnPingCommand(const std::weak_ptr<weave::Command> & command)103   void OnPingCommand(const std::weak_ptr<weave::Command>& command) {
104     auto cmd = command.lock();
105     if (!cmd)
106       return;
107     LOG(INFO) << "received command: " << cmd->GetName();
108 
109     device_->SetStateProperty(kComponent, "_sample.pingCount",
110                               base::FundamentalValue{++ping_count_}, nullptr);
111     LOG(INFO) << "New component state: " << device_->GetComponents();
112 
113     cmd->Complete({}, nullptr);
114 
115     LOG(INFO) << cmd->GetName() << " command finished";
116   }
117 
OnCountdownCommand(const std::weak_ptr<weave::Command> & command)118   void OnCountdownCommand(const std::weak_ptr<weave::Command>& command) {
119     auto cmd = command.lock();
120     if (!cmd)
121       return;
122     LOG(INFO) << "received command: " << cmd->GetName();
123 
124     const auto& params = cmd->GetParameters();
125     int seconds;
126     if (!params.GetInteger("seconds", &seconds))
127       seconds = 10;
128 
129     LOG(INFO) << "starting countdown";
130     DoTick(cmd, seconds);
131   }
132 
DoTick(const std::weak_ptr<weave::Command> & command,int seconds)133   void DoTick(const std::weak_ptr<weave::Command>& command, int seconds) {
134     auto cmd = command.lock();
135     if (!cmd)
136       return;
137 
138     if (seconds > 0) {
139       LOG(INFO) << "countdown tick: " << seconds << " seconds left";
140       base::DictionaryValue progress;
141       progress.SetInteger("seconds_left", seconds);
142       cmd->SetProgress(progress, nullptr);
143       task_runner_->PostDelayedTask(
144           FROM_HERE,
145           base::Bind(&SampleHandler::DoTick, weak_ptr_factory_.GetWeakPtr(),
146                      command, --seconds),
147           base::TimeDelta::FromSeconds(1));
148       return;
149     }
150 
151     cmd->Complete({}, nullptr);
152     LOG(INFO) << "countdown finished";
153     LOG(INFO) << cmd->GetName() << " command finished";
154   }
155 
156   weave::Device* device_{nullptr};
157   weave::provider::TaskRunner* task_runner_{nullptr};
158 
159   int ping_count_{0};
160   base::WeakPtrFactory<SampleHandler> weak_ptr_factory_{this};
161 };
162 
main(int argc,char ** argv)163 int main(int argc, char** argv) {
164   Daemon::Options opts;
165   if (!opts.Parse(argc, argv)) {
166     Daemon::Options::ShowUsage(argv[0]);
167     return 1;
168   }
169   Daemon daemon{opts};
170   SampleHandler handler{daemon.GetTaskRunner()};
171   handler.Register(daemon.GetDevice());
172   daemon.Run();
173   return 0;
174 }
175