普通文本  |  289行  |  7.61 KB

// 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"
          }
        },
        "errors": ["tempOutOfRange", "unsupportedUnits"]
      }
    },
    "state": {
      "supportedUnits": {
        "type": "array",
        "items": {
          "type": "string",
          "enum": [ "celsius", "fahrenheit", "kelvin" ]
        },
        "minItems": 1,
        "uniqueItems": true,
        "isRequired": true
      },
      "units": {
        "type": "string",
        "enum": [ "celsius", "fahrenheit", "kelvin" ],
        "isRequired": true
      },
      "tempSetting": {
        "type": "number",
        "isRequired": true
      },
      "maxTempSetting": {
        "type": "number",
        "isRequired": true
      },
      "minTempSetting": {
        "type": "number",
        "isRequired": true
      }
    }
  },
  "temperatureSensor": {
    "commands": {
      "setConfig": {
        "minimalRole": "user",
        "parameters": {
          "units": {
            "type": "string"
          }
        },
        "errors": ["unsupportedUnits"]
      }
    },
    "state": {
      "supportedUnits": {
        "type": "array",
        "items": {
          "type": "string",
          "enum": [
            "celsius",
            "fahrenheit",
            "kelvin"
          ]
        },
        "minItems": 1,
        "uniqueItems": true,
        "isRequired": true
      },
      "units": {
        "type": "string",
        "enum": [ "celsius", "fahrenheit", "kelvin" ],
        "isRequired": true
      },
      "value": {
        "type": "number",
        "isRequired": true
      }
    }
  },
  "brightness": {
    "commands": {
      "setConfig": {
        "minimalRole": "user",
        "parameters": {
          "brightness": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          }
        }
      }
    },
    "state": {
      "brightness": {
        "type": "integer",
        "isRequired": true,
        "minimum": 0,
        "maximum": 100
      }
    }
  }
})";

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, "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, "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;
}