blob: 356888b992fde259ca5ffccd4d28b4ff26610947 [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "LockAppCommandDelegate.h"
#include <lib/support/BytesToHex.h>
#include <platform/PlatformManager.h>
#include <LockManager.h>
#include <utility>
using chip::to_underlying;
class LockAppCommandHandler
{
public:
static LockAppCommandHandler * FromJSON(const char * json);
static void HandleCommand(intptr_t context);
LockAppCommandHandler(std::string && cmd, Json::Value && params) :
mCommandName(std::move(cmd)), mCommandParameters(std::move(params))
{}
private:
// aCommand should be "lock" or "unlock".
static CHIP_ERROR ExtractPINFromParams(const char * aCommand, const Json::Value & aParams, Optional<chip::ByteSpan> & aPIN,
chip::Platform::ScopedMemoryBuffer<uint8_t> & aPINBuffer);
std::string mCommandName;
Json::Value mCommandParameters;
};
LockAppCommandHandler * LockAppCommandHandler::FromJSON(const char * json)
{
// Command format:
// { "Cmd": "SetDoorState", "Params": { "EndpointId": 1, "DoorState": 2} }
Json::Reader reader;
Json::Value value;
if (!reader.parse(json, value))
{
ChipLogError(NotSpecified, "Lock App: Error parsing JSON with error %s:", reader.getFormattedErrorMessages().c_str());
return nullptr;
}
if (value.empty() || !value.isObject())
{
ChipLogError(NotSpecified, "Lock App: Invalid JSON command received");
return nullptr;
}
if (!value.isMember("Cmd") || !value["Cmd"].isString())
{
ChipLogError(NotSpecified, "Lock App: Invalid JSON command received: command name is missing");
return nullptr;
}
Json::Value params = Json::objectValue;
if (value.isMember("Params"))
{
if (!value["Params"].isObject())
{
ChipLogError(NotSpecified, "Lock App: Invalid JSON command received: specified parameters are incorrect");
return nullptr;
}
params = value["Params"];
}
auto commandName = value["Cmd"].asString();
return chip::Platform::New<LockAppCommandHandler>(std::move(commandName), std::move(params));
}
void LockAppCommandHandler::HandleCommand(intptr_t context)
{
auto * self = reinterpret_cast<LockAppCommandHandler *>(context);
const auto & params = self->mCommandParameters;
// Determine the endpoint ID from the parameters JSON. If it is missing, use the default endpoint defined in the
// door-lock-server.h
CHIP_ERROR err = CHIP_NO_ERROR;
chip::EndpointId endpointId = DOOR_LOCK_SERVER_ENDPOINT;
if (params.isMember("EndpointId"))
{
VerifyOrExit(params["EndpointId"].isUInt(),
ChipLogError(NotSpecified, "Lock App: Unable to execute command \"%s\": invalid endpoint Id",
self->mCommandName.c_str()));
endpointId = static_cast<chip::EndpointId>(params["EndpointId"].asUInt());
}
// TODO: Make commands separate objects derived from some base class to clean up this mess.
// Now we can try to execute a command
if (self->mCommandName == "SetDoorState")
{
VerifyOrExit(params.isMember("DoorState"),
ChipLogError(NotSpecified,
"Lock App: Unable to execute command to set the door state: DoorState is missing in command"));
VerifyOrExit(
params["DoorState"].isUInt(),
ChipLogError(NotSpecified, "Lock App: Unable to execute command to set the door state: invalid type for DoorState"));
auto doorState = params["DoorState"].asUInt();
VerifyOrExit(doorState < to_underlying(DoorStateEnum::kUnknownEnumValue),
ChipLogError(NotSpecified,
"Lock App: Unable to execute command to set door state: DoorState is out of range [doorState=%u]",
doorState));
LockManager::Instance().SetDoorState(endpointId, static_cast<DoorStateEnum>(doorState));
}
else if (self->mCommandName == "SendDoorLockAlarm")
{
VerifyOrExit(
params.isMember("AlarmCode"),
ChipLogError(NotSpecified, "Lock App: Unable to execute command to send lock alarm: AlarmCode is missing in command"));
VerifyOrExit(
params["AlarmCode"].isUInt(),
ChipLogError(NotSpecified, "Lock App: Unable to execute command to send lock alarm: invalid type for AlarmCode"));
auto alarmCode = params["AlarmCode"].asUInt();
VerifyOrExit(
alarmCode < to_underlying(AlarmCodeEnum::kUnknownEnumValue),
ChipLogError(NotSpecified,
"Lock App: Unable to execute command to send lock alarm: AlarmCode is out of range [alarmCode=%u]",
alarmCode));
LockManager::Instance().SendLockAlarm(endpointId, static_cast<AlarmCodeEnum>(alarmCode));
}
else if (self->mCommandName == "Lock")
{
VerifyOrExit(params["OperationSource"].isUInt(),
ChipLogError(NotSpecified, "Lock App: Unable to execute command to lock: invalid type for OperationSource"));
auto operationSource = params["OperationSource"].asUInt();
Optional<chip::ByteSpan> pin;
chip::Platform::ScopedMemoryBuffer<uint8_t> pinBuffer;
SuccessOrExit(err = ExtractPINFromParams("lock", params, pin, pinBuffer));
OperationErrorEnum error = OperationErrorEnum::kUnspecified;
LockManager::Instance().Lock(endpointId, NullNullable, NullNullable, pin, error, OperationSourceEnum(operationSource));
VerifyOrExit(error == OperationErrorEnum::kUnspecified,
ChipLogError(NotSpecified, "Lock App: Lock error received: %u", to_underlying(error)));
}
else if (self->mCommandName == "Unlock")
{
VerifyOrExit(params["OperationSource"].isUInt(),
ChipLogError(NotSpecified, "Lock App: Unable to execute command to unlock: invalid type for OperationSource"));
auto operationSource = params["OperationSource"].asUInt();
Optional<chip::ByteSpan> pin;
chip::Platform::ScopedMemoryBuffer<uint8_t> pinBuffer;
SuccessOrExit(err = ExtractPINFromParams("unlock", params, pin, pinBuffer));
OperationErrorEnum error = OperationErrorEnum::kUnspecified;
LockManager::Instance().Unlock(endpointId, NullNullable, NullNullable, pin, error, OperationSourceEnum(operationSource));
VerifyOrExit(error == OperationErrorEnum::kUnspecified,
ChipLogError(NotSpecified, "Lock App: Unlock error received: %u", to_underlying(error)));
}
else
{
ChipLogError(NotSpecified, "Lock App: Unable to execute command \"%s\": command not supported", self->mCommandName.c_str());
}
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Lock App: Failed executing command \"%s\": %" CHIP_ERROR_FORMAT, self->mCommandName.c_str(),
err.Format());
}
chip::Platform::Delete(self);
}
CHIP_ERROR LockAppCommandHandler::ExtractPINFromParams(const char * aCommand, const Json::Value & aParams,
Optional<chip::ByteSpan> & aPIN,
chip::Platform::ScopedMemoryBuffer<uint8_t> & aPINBuffer)
{
if (aParams.isMember("PINAsHex"))
{
// Hex-encoded PIN bytes. So a PIN consisting of the numbers 123 gets encoded as the string "313233"
VerifyOrReturnError(
aParams["PINAsHex"].isString(), CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(NotSpecified, "Lock App: Unable to execute command to %s: invalid type for PIN", aCommand));
auto pinAsHex = aParams["PINAsHex"].asString();
size_t size = pinAsHex.length();
VerifyOrReturnError(size % 2 == 0, CHIP_ERROR_INVALID_STRING_LENGTH);
size_t bufferSize = size / 2;
VerifyOrReturnError(aPINBuffer.Calloc(bufferSize), CHIP_ERROR_NO_MEMORY);
size_t octetCount = chip::Encoding::HexToBytes(pinAsHex.c_str(), size, aPINBuffer.Get(), bufferSize);
VerifyOrReturnError(
octetCount != 0 || size == 0, CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(NotSpecified, "Lock app: Unable to execute command to %s: invalid hex value for PIN", aCommand));
aPIN.Emplace(aPINBuffer.Get(), octetCount);
ChipLogProgress(NotSpecified, "Lock App: Received command to %s with hex PIN: %s", aCommand, pinAsHex.c_str());
}
else if (aParams.isMember("PINAsString"))
{
// ASCII-encoded PIN bytes. So a PIN consisting of the numbers 123 gets encoded as the string "123"
VerifyOrReturnError(
aParams["PINAsString"].isString(), CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(NotSpecified, "Lock App: Unable to execute command to %s: invalid type for PIN", aCommand));
auto pinAsString = aParams["PINAsString"].asString();
size_t bufferSize = pinAsString.length();
VerifyOrReturnError(aPINBuffer.Calloc(bufferSize), CHIP_ERROR_NO_MEMORY);
memcpy(aPINBuffer.Get(), pinAsString.c_str(), bufferSize);
aPIN.Emplace(aPINBuffer.Get(), bufferSize);
ChipLogProgress(NotSpecified, "Lock App: Received command to %s with string PIN: %s", aCommand, pinAsString.c_str());
}
else
{
aPIN.ClearValue();
}
return CHIP_NO_ERROR;
}
void LockAppCommandDelegate::OnEventCommandReceived(const char * json)
{
auto handler = LockAppCommandHandler::FromJSON(json);
if (nullptr == handler)
{
ChipLogError(NotSpecified, "Lock App: Unable to instantiate a command handler");
return;
}
chip::DeviceLayer::PlatformMgr().ScheduleWork(LockAppCommandHandler::HandleCommand, reinterpret_cast<intptr_t>(handler));
}