Add support for virtual lock & light devices

Change-Id: Ib26fad33aee2618f0f299c1c4a970d7822a8d1df
Reviewed-on: https://weave-review.googlesource.com/1412
Reviewed-by: Paul Westbrook <pwestbro@google.com>
diff --git a/libweave/examples/daemon/light_handler.h b/libweave/examples/daemon/light_handler.h
new file mode 100644
index 0000000..08eb682
--- /dev/null
+++ b/libweave/examples/daemon/light_handler.h
@@ -0,0 +1,131 @@
+// 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 <weave/device.h>
+
+#include <base/bind.h>
+#include <base/memory/weak_ptr.h>
+
+namespace weave {
+namespace examples {
+namespace daemon {
+
+// LightHandler is a command handler example that shows
+// how to handle commands for a Weave light.
+class LightHandler {
+ public:
+  LightHandler() = default;
+  void Register(Device* device) {
+    device_ = device;
+
+    device->AddStateDefinitionsFromJson(R"({
+      "onOff": {"state": ["on", "standby"]},
+      "brightness": {"brightness": "integer"}
+    })");
+
+    device->SetStatePropertiesFromJson(R"({
+      "onOff":{"state": "standby"},
+      "brightness":{"brightness": 0}
+    })",
+                                       nullptr);
+
+    // Once bug b/25304415 is fixed, these should be changed
+    // to use the standard commands.
+    device->AddCommandDefinitionsFromJson(R"({
+      "onOff": {
+         "_setConfig":{
+           "parameters": {
+             "_state": ["on", "standby"]
+           }
+         }
+       },
+       "brightness": {
+         "_setConfig":{
+           "parameters": {
+             "_brightness": {
+               "type": "integer",
+               "minimum": 0,
+               "maximum": 100
+             }
+           }
+        }
+      }
+    })");
+    device->AddCommandHandler(
+        "onOff._setConfig",
+        base::Bind(&LightHandler::OnOnOffSetConfig,
+                   weak_ptr_factory_.GetWeakPtr()));
+    device->AddCommandHandler(
+        "brightness._setConfig",
+         base::Bind(&LightHandler::OnBrightnessSetConfig,
+                    weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void OnBrightnessSetConfig(const std::weak_ptr<Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+    int32_t brightness_value = 0;
+    if (cmd->GetParameters()->GetInteger("_brightness", &brightness_value)) {
+      // Display this command in terminal.
+      LOG(INFO) << cmd->GetName() << " brightness: " << brightness_value;
+
+      if (brightness_state_ != brightness_value) {
+        brightness_state_ = brightness_value;
+        UpdateLightState();
+      }
+      cmd->Complete({}, nullptr);
+      return;
+    }
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                 "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void OnOnOffSetConfig(const std::weak_ptr<Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+    std::string requested_state;
+    if (cmd->GetParameters()->GetString("_state", &requested_state)) {
+      LOG(INFO) << cmd->GetName() << " state: " << requested_state;
+
+      bool new_light_status = requested_state == "on";
+      if (new_light_status != light_status_) {
+        light_status_ = new_light_status;
+
+        LOG(INFO) << "Light is now: " << (light_status_ ? "ON" : "OFF");
+        UpdateLightState();
+      }
+      cmd->Complete({}, nullptr);
+      return;
+    }
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                 "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void UpdateLightState(void) {
+    base::DictionaryValue state;
+    state.SetString("onOff.state", light_status_ ? "on" : "standby");
+    state.SetInteger("brightness.brightness", brightness_state_);
+    device_->SetStateProperties(state, nullptr);
+  }
+
+  Device* device_{nullptr};
+
+  // Simulate the state of the light.
+  bool light_status_;
+  int32_t brightness_state_;
+  base::WeakPtrFactory<LightHandler> weak_ptr_factory_{this};
+};
+
+}  // namespace daemon
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/daemon/lock_handler.h b/libweave/examples/daemon/lock_handler.h
new file mode 100644
index 0000000..71ff83a
--- /dev/null
+++ b/libweave/examples/daemon/lock_handler.h
@@ -0,0 +1,115 @@
+// 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 <weave/device.h>
+#include <weave/enum_to_string.h>
+
+#include <base/bind.h>
+#include <base/memory/weak_ptr.h>
+
+namespace weave {
+
+namespace lockstate {
+enum class LockState { kUnlocked, kLocked, kPartiallyLocked };
+
+const weave::EnumToStringMap<LockState>::Map kLockMapMethod[] = {
+  {LockState::kLocked, "locked"},
+  {LockState::kUnlocked, "unlocked"},
+  {LockState::kPartiallyLocked, "partiallyLocked"}};
+}  // namespace lockstate
+
+template <>
+EnumToStringMap<lockstate::LockState>::EnumToStringMap()
+  : EnumToStringMap(lockstate::kLockMapMethod) {}
+
+namespace examples {
+namespace daemon {
+
+// LockHandler is a command handler example that shows
+// how to handle commands for a Weave lock.
+class LockHandler {
+ public:
+  LockHandler() = default;
+  void Register(Device* device) {
+    device_ = device;
+
+    device->AddStateDefinitionsFromJson(R"({
+      "_lock": {"lockedState": ["locked", "unlocked", "partiallyLocked"]}
+    })");
+
+    device->SetStatePropertiesFromJson(R"({
+      "_lock":{"lockedState": "locked"}
+    })",
+                                       nullptr);
+
+    // Once bug b/25304415 is fixed, and when the lock trait is published
+    // these should be changed  to use the standard commands
+    device->AddCommandDefinitionsFromJson(R"({
+        "_lock": {
+          "_setConfig":{
+            "parameters": {
+              "_lockedState": ["locked", "unlocked"]
+            }
+          }
+        }
+    })");
+    device->AddCommandHandler(
+        "_lock._setConfig",
+        base::Bind(&LockHandler::OnLockSetConfig,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void OnLockSetConfig(const std::weak_ptr<Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+    std::string requested_state;
+    if (cmd->GetParameters()->GetString("_lockedState", &requested_state)) {
+      LOG(INFO) << cmd->GetName() << " state: " << requested_state;
+
+      lockstate::LockState new_lock_status;
+
+      if (!weave::StringToEnum(requested_state, &new_lock_status)) {
+        // Invalid lock state was specified.
+        ErrorPtr error;
+        Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                     "Invalid parameters");
+        cmd->Abort(error.get(), nullptr);
+        return;
+      }
+
+      if (new_lock_status != lock_state_) {
+        lock_state_ = new_lock_status;
+
+        LOG(INFO) << "Lock is now: " << requested_state;
+        UpdateLockState();
+      }
+      cmd->Complete({}, nullptr);
+      return;
+    }
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                 "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void UpdateLockState(void) {
+    base::DictionaryValue state;
+    std::string updated_state = weave::EnumToString(lock_state_);
+    state.SetString("lock.lockedState", updated_state);
+    device_->SetStateProperties(state, nullptr);
+  }
+
+  Device* device_{nullptr};
+
+  // Simulate the state of the light.
+  lockstate::LockState lock_state_{lockstate::LockState::kLocked};
+  base::WeakPtrFactory<LockHandler> weak_ptr_factory_{this};
+};
+
+}  // namespace daemon
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/daemon/main.cc b/libweave/examples/daemon/main.cc
index 263e730..3a4efe7 100644
--- a/libweave/examples/daemon/main.cc
+++ b/libweave/examples/daemon/main.cc
@@ -8,6 +8,8 @@
 #include <base/bind.h>
 
 #include "examples/daemon/ledflasher_handler.h"
+#include "examples/daemon/light_handler.h"
+#include "examples/daemon/lock_handler.h"
 #include "examples/daemon/sample_handler.h"
 
 #include "examples/provider/avahi_client.h"
@@ -33,7 +35,9 @@
              << "\t-d,--disable_security        Disable privet security\n"
              << "\t--registration_ticket=TICKET Register device with the "
                 "given ticket\n"
-             << "\t--disable_privet             Disable local privet\n";
+             << "\t--disable_privet             Disable local privet\n"
+             << "\t--type=UIDEVICEKIND          Create a device with the "
+                "specified ui device kind\n";
 }
 
 void OnRegisterDeviceDone(weave::Device* device, weave::ErrorPtr error) {
@@ -50,6 +54,7 @@
   bool disable_security = false;
   bool disable_privet = false;
   std::string registration_ticket;
+  std::string ui_device_kind;
   for (int i = 1; i < argc; ++i) {
     std::string arg = argv[i];
     if (arg == "-h" || arg == "--help") {
@@ -75,6 +80,13 @@
         return 1;
       }
       logging::SetMinLogLevel(-std::stoi(arg.substr(pos + 1)));
+    } else if (arg.find("--type") != std::string::npos) {
+      auto pos = arg.find("=");
+      if (pos == std::string::npos) {
+        ShowUsage(argv[0]);
+        return 1;
+      }
+      ui_device_kind = arg.substr(pos + 1);
     } else {
       ShowUsage(argv[0]);
       return 1;
@@ -111,6 +123,20 @@
   sample.Register(device.get());
   ledFlasher.Register(device.get());
 
+  // If the caller specified a particular ui device kind, register the
+  // correspoinding device handlers
+  // TODO: move this to before device registration, as this should also
+  // cause a particular model manifest to be used.
+  weave::examples::daemon::LightHandler lightHandler;
+  weave::examples::daemon::LockHandler lockHandler;
+  if (!ui_device_kind.empty()) {
+    if (ui_device_kind == "light") {
+      lightHandler.Register(device.get());
+    } else if (ui_device_kind == "lock") {
+      lockHandler.Register(device.get());
+    }
+  }
+
   task_runner.Run();
 
   LOG(INFO) << "exit";