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
13namespace {
14
15const 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
48const 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.
57class SampleHandler {
58 public:
59  SampleHandler(weave::provider::TaskRunner* task_runner)
60      : task_runner_{task_runner} {}
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:
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
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
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
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
163int 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