Oven virtual device sample initial commit. Used as weave device for CES
demo.

Virtual oven implements temperatureSetting, temperatureSensor, and
brightness traits.

Change-Id: I33ec5c841ad102377166086668cbb0e1b78755dc
Reviewed-on: https://weave-review.googlesource.com/1867
Reviewed-by: Peng Ying <pying@google.com>
diff --git a/examples/daemon/examples.gyp b/examples/daemon/examples.gyp
index 2b497a5..d63c5e4 100644
--- a/examples/daemon/examples.gyp
+++ b/examples/daemon/examples.gyp
@@ -8,7 +8,8 @@
         'light/daemon.gyp:weave_daemon_light',
         'lock/daemon.gyp:weave_daemon_lock',
         'ledflasher/daemon.gyp:weave_daemon_ledflasher',
-        'speaker/daemon.gyp:weave_daemon_speaker'
+        'speaker/daemon.gyp:weave_daemon_speaker',
+        'oven/daemon.gyp:weave_daemon_oven',
       ]
     }
   ]
diff --git a/examples/daemon/oven/daemon.gyp b/examples/daemon/oven/daemon.gyp
new file mode 100644
index 0000000..44b4798
--- /dev/null
+++ b/examples/daemon/oven/daemon.gyp
@@ -0,0 +1,18 @@
+# Copyright 2015 The Weave Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'targets': [
+    {
+      'target_name': 'weave_daemon_oven',
+      'type': 'executable',
+      'sources': [
+        'oven.cc',
+      ],
+      'dependencies': [
+        '<@(DEPTH)/libweave_standalone.gyp:libweave',
+        '<@(DEPTH)/examples/provider/provider.gyp:libweave_provider',
+      ]
+    }
+  ]
+}
diff --git a/examples/daemon/oven/oven.cc b/examples/daemon/oven/oven.cc
new file mode 100644
index 0000000..79fc925
--- /dev/null
+++ b/examples/daemon/oven/oven.cc
@@ -0,0 +1,263 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/daemon/common/daemon.h"
+
+#include <weave/device.h>
+#include <weave/provider/task_runner.h>
+
+#include <base/bind.h>
+#include <base/memory/weak_ptr.h>
+
+namespace {
+// Time for sensor temperature to match setting temperature
+const double kWarmUpTime = 60.0;
+// Oven max temp
+const double kMaxTemp = 300.0;
+// Oven min temp
+const double kMinTemp = 20.0;
+
+const char kTraits[] = R"({
+  "temperatureSetting": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "units": {
+            "type": "string"
+          },
+          "tempSetting": {
+            "type": "number"
+          }
+        }
+      }
+    },
+    "state": {
+      "supportedUnits": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "enum": [
+            "celsius",
+            "fahrenheit",
+            "kelvin"
+          ]
+        }
+      },
+      "units": {
+        "type": "string"
+      },
+      "tempSetting": {
+        "type": "number"
+      },
+      "maxTempSetting": {
+        "type": "number"
+      },
+      "minTempSetting": {
+        "type": "number"
+      }
+    }
+  },
+  "temperatureSensor": {
+    "state": {
+      "supportedUnits": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "enum": [
+            "celsius",
+            "fahrenheit",
+            "kelvin"
+          ]
+        }
+      },
+      "units": {
+        "type": "string"
+      },
+      "value": {
+        "type": "number"
+      }
+    }
+  },
+  "brightness": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "brightness": {
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 100
+          }
+        }
+      }
+    },
+    "state": {
+      "brightness": {
+        "type": "number"
+      }
+    }
+  }
+})";
+
+const char kComponent[] = "oven";
+}  // anonymous namespace
+
+// OvenHandler is a virtual oven example
+// It implements the following commands from traits:
+// - temperatureSetting: sets the temperature for the oven
+// - brightness: sets the brightness of the oven light
+// It exposes the following states from traits:
+// - temperatureSetting: temperature setting for the oven
+// - temperatureSensor: current oven temperature
+// - brightness: current oven brightness
+class OvenHandler {
+ public:
+  OvenHandler(weave::provider::TaskRunner* task_runner)
+      : task_runner_{task_runner} {}
+
+  void Register(weave::Device* device) {
+    device_ = device;
+
+    device->AddTraitDefinitionsFromJson(kTraits);
+    CHECK(device->AddComponent(
+        kComponent, {"temperatureSetting", "temperatureSensor", "brightness"},
+        nullptr));
+
+    UpdateOvenState();
+
+    device->AddCommandHandler(kComponent, "temperatureSetting.setConfig",
+                              base::Bind(&OvenHandler::OnSetTempCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
+
+    device->AddCommandHandler(kComponent, "brightness.setConfig",
+                              base::Bind(&OvenHandler::OnSetBrightnessCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
+  }
+
+private:
+  void OnSetTempCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+
+    const auto& params = cmd->GetParameters();
+    std::string units;
+    double temp;
+
+    if (params.GetString("units", &units) &&
+        params.GetDouble("tempSetting", &temp)) {
+      units_ = units;
+      target_temperature_ = temp;
+
+      UpdateOvenState();
+
+      cmd->Complete({}, nullptr);
+      LOG(INFO) << cmd->GetName() << " updated oven, matching temp";
+
+      if (target_temperature_ != current_temperature_ && !is_match_ticking_) {
+        double tickIncrement = ((target_temperature_ - current_temperature_) /
+            kWarmUpTime);
+        DoTick(tickIncrement);
+      }
+      return;
+    }
+
+    weave::ErrorPtr error;
+    weave::Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                        "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void OnSetBrightnessCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+
+    const auto& params = cmd->GetParameters();
+
+    int brightness;
+    if (params.GetInteger("brightness", &brightness)) {
+      brightness_ = brightness;
+
+      UpdateOvenState();
+
+      cmd->Complete({}, nullptr);
+      return;
+    }
+
+    weave::ErrorPtr error;
+    weave::Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                        "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void UpdateOvenState() {
+    base::DictionaryValue state;
+    base::ListValue supportedUnits;
+    supportedUnits.AppendStrings({"celsius"});
+
+    state.SetString("temperatureSensor.units", units_);
+    state.SetDouble("temperatureSensor.value", current_temperature_);
+    state.Set("temperatureSensor.supportedUnits", supportedUnits.DeepCopy());
+
+    state.SetString("temperatureSetting.units", units_);
+    state.SetDouble("temperatureSetting.tempSetting", target_temperature_);
+    state.Set("temperatureSetting.supportedUnits", supportedUnits.DeepCopy());
+    state.SetDouble("temperatureSetting.maxTempSetting", kMaxTemp);
+    state.SetDouble("temperatureSetting.minTempSetting", kMinTemp);
+
+    state.SetInteger("brightness.brightness", brightness_);
+
+    device_->SetStateProperties(kComponent, state, nullptr);
+  }
+
+  void DoTick(double tickIncrement) {
+    LOG(INFO) << "Oven matching temp tick";
+
+    if (std::fabs(target_temperature_ - current_temperature_) >=
+        tickIncrement) {
+      is_match_ticking_ = true;
+      current_temperature_ += tickIncrement;
+      UpdateOvenState();
+      task_runner_->PostDelayedTask(
+          FROM_HERE, base::Bind(&OvenHandler::DoTick,
+                                weak_ptr_factory_.GetWeakPtr(), tickIncrement),
+          base::TimeDelta::FromSeconds(1));
+      return;
+    }
+
+    is_match_ticking_ = false;
+    current_temperature_ = target_temperature_;
+    UpdateOvenState();
+
+    LOG(INFO) << "Oven temp matched";
+  }
+
+  weave::Device* device_{nullptr};
+  weave::provider::TaskRunner* task_runner_{nullptr};
+
+  std::string units_ = "celsius";
+  double target_temperature_ = 0.0;
+  double current_temperature_ = 0.0;
+  int brightness_ = 0;
+  bool is_match_ticking_ = false;
+
+  base::WeakPtrFactory<OvenHandler> weak_ptr_factory_{this};
+};
+
+int main(int argc, char** argv) {
+  Daemon::Options opts;
+  if (!opts.Parse(argc, argv)) {
+    Daemon::Options::ShowUsage(argv[0]);
+    return 1;
+  }
+  Daemon daemon{opts};
+  OvenHandler handler{daemon.GetTaskRunner()};
+  handler.Register(daemon.GetDevice());
+  daemon.Run();
+  return 0;
+}