// 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;
}