Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pull request project-chip#63: UIC-2321: Moving the Unify Matter Bridg…
Browse files Browse the repository at this point in the history
…e to matter repo.

Merge in WMN_TOOLS/matter from feature/unify_matter_bridge to silabs

Squashed commit of the following:

commit daf32d2989c04e17911cd1a7379a887210e82ae8
Author: Anders Esbensen <[email protected]>
Date:   Tue Sep 13 13:29:45 2022 +0200

    Applied new formating.

commit 7d9e0b2f4ebed9919ae36776ab2536f8d11406ec
Author: Anders Esbensen <[email protected]>
Date:   Tue Sep 13 12:27:05 2022 +0200

    Added stograge persistance.

commit 354849c5861f15b70241abd2ca91f3d9db449e81
Author: Anders Esbensen <[email protected]>
Date:   Mon Sep 12 11:06:49 2022 +0200

    Only build efr32 examples in CI for now.

commit 6b72a5ac9c011cb0570e578e74e92ac7db76c883
Author: Anders Esbensen <[email protected]>
Date:   Thu Sep 8 12:09:55 2022 +0200

    UIC-2321: Use pkg_config instead.

commit 28afe77a4b9525909f12b5b04b36e86302dbb31c
Author: Anders Esbensen <[email protected]>
Date:   Fri Sep 2 09:47:11 2022 +0200

    UIC-2321: Moving the Unify Matter Bridge to matter repo.
Anders Esbensen authored and jmartinez-silabs committed Jan 9, 2024
1 parent 2998e66 commit 7c92b22
Showing 86 changed files with 16,807 additions and 1 deletion.
2 changes: 2 additions & 0 deletions examples/platform/linux/NamedPipeCommands.h
Original file line number Diff line number Diff line change
@@ -21,6 +21,8 @@
#include <lib/core/CHIPError.h>
#include <pthread.h>
#include <string>
#include <pthread.h>
#include <errno.h>

class NamedPipeCommandDelegate
{
2 changes: 1 addition & 1 deletion silabs_ci_scripts/build_custom_examples.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
BUILD_TYPES = {("standard", "")}
building_command = './scripts/examples/gn_efr32_example.sh ./silabs_examples/{app}/efr32 ./out/custom/{app}/{network} {board} {buildArguments}'

for examples in glob.glob("./silabs_examples/*"):
for examples in glob.glob("./silabs_examples/*/efr32"):
for build in BUILDS:
for board in BOARDS:
for build_type in BUILD_TYPES:
33 changes: 33 additions & 0 deletions silabs_examples/unify-matter-bridge/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
ARG VERSION=latest
FROM connectedhomeip/chip-build:${VERSION}

# Rust Version to install
ENV RUST_VERSION=1.60.0
# Rust and Cargo home directories
ENV RUSTUP_HOME=/opt/rustup-home
ENV CARGO_HOME=/opt/cargo-home
# Install Rust and Cargo
RUN curl https://sh.rustup.rs -sSf --output /tmp/sh.rustup.rs \
&& cd /tmp && chmod +x sh.rustup.rs \
&& ./sh.rustup.rs -y --profile minimal --default-toolchain ${RUST_VERSION}\
&& rm /tmp/sh.rustup.rs \
&& /opt/cargo-home/bin/cargo install cargo2junit \
# remove the main branch reference once the maintainer tagged 81d73b4
&& /opt/cargo-home/bin/cargo install cargo-deb --git https://github.com/kornelski/cargo-deb.git --branch main \
&& chmod -R a+rw ${RUSTUP_HOME} ${CARGO_HOME} \
&& find ${RUSTUP_HOME} ${CARGO_HOME} -type d -exec chmod a+x {} \;
ENV PATH="${CARGO_HOME}/bin:${PATH}"

#Target dependencies
RUN apt update && \
apt install -y \
libsqlite3-dev libedit-dev libyaml-cpp0.6 libmosquitto-dev\
libyaml-cpp-dev \
libboost-atomic-dev libboost-chrono-dev libboost-date-time-dev \
libboost-filesystem-dev libboost-regex-dev libboost-program-options-dev \
libboost-serialization-dev libboost-system-dev libboost-thread-dev \
libboost-log-dev nlohmann-json3-dev

#Build host dependencies
RUN ln -s /usr/bin/python3 /usr/bin/python
RUN apt install -y ruby ruby-dev
25 changes: 25 additions & 0 deletions silabs_examples/unify-matter-bridge/linux/.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2021 Project CHIP Authors
#
# 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.

import("//build_overrides/build.gni")

# The location of the build configuration file.
buildconfig = "${build_root}/config/BUILDCONFIG.gn"

# CHIP uses angle bracket includes.
check_system_includes = true

default_args = {
import("//args.gni")
}
53 changes: 53 additions & 0 deletions silabs_examples/unify-matter-bridge/linux/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import("//build_overrides/chip.gni")
import("${chip_root}/build/chip/tools.gni")
import("${chip_root}/build/config/linux/pkg_config.gni")

assert(chip_build_tools)

pkg_config("unify") {
packages = [ "libunify" ]
}

executable("unify-matter-bridge") {
include_dirs = [
"src",
"src/cluster_translator",
"src/device_type_mapper",
"src/matter_node_state_monitor",
"include",
]

sources = [
"src/matter_data_storage.cpp",
"src/attribute_state_cache.cpp",
"src/cluster_translator/bridged_device_basic_info_attribute_translator.cpp",
"src/cluster_translator/group_command_translator.cpp",
"src/cluster_translator/group_translator.cpp",
"src/cluster_translator/identify_attribute_translator.cpp",
"src/cluster_translator/identify_command_translator.cpp",

#"src/cluster_translator/level_attribute_translator.cpp",
#"src/cluster_translator/level_command_translator.cpp",
"src/cluster_translator/on_off_attribute_translator.cpp",
"src/cluster_translator/on_off_command_translator.cpp",
"src/demo_uic_cli.cpp",
"src/device_type_mapper/matter_device_translator.cpp",
"src/device_type_mapper/matter_device_types_clusters_list_updated.inc",
"src/dummy.cpp",
"src/matter_bridge_config.c",
"src/matter_bridge_main.cpp",
"src/matter_node_state_monitor/matter_cluster_interactor.cpp",
"src/matter_node_state_monitor/matter_endpoint_builder.cpp",
"src/matter_node_state_monitor/matter_node_state_monitor.cpp",
]

deps = [
"${chip_root}/examples/platform/linux:app-main",
"//../unify-matter-bridge-common",
]
configs += [ ":unify" ]
}

group("linux") {
deps = [ ":unify-matter-bridge" ]
}
38 changes: 38 additions & 0 deletions silabs_examples/unify-matter-bridge/linux/args.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

import("//build_overrides/chip.gni")

import("${chip_root}/config/standalone/args.gni")

chip_device_project_config_include = "<CHIPProjectAppConfig.h>"
chip_project_config_include = "<CHIPProjectAppConfig.h>"
chip_system_project_config_include = "<SystemProjectConfig.h>"

chip_project_config_include_dirs =
[ "${chip_root}/examples/tv-app/tv-common/include" ]
chip_project_config_include_dirs += [ "${chip_root}/config/standalone" ]

chip_build_libshell = true

chip_enable_additional_data_advertising = true

chip_enable_rotating_device_id = true

cpp_standard = "c++17"

enable_pic = false

default_configs_exceptions =
[ "//third_party/connectedhomeip/build/config/compiler:exceptions" ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2021 Project CHIP Authors
#
# 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.

declare_args() {
# Root directory for build files.
build_root = "//third_party/connectedhomeip/build"
}
18 changes: 18 additions & 0 deletions silabs_examples/unify-matter-bridge/linux/build_overrides/chip.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
# Root directory for CHIP.
chip_root = "//third_party/connectedhomeip"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
# Root directory for nlassert.
nlassert_root = "//third_party/connectedhomeip/third_party/nlassert"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
# Root directory for nlfaultinjection.
nlfaultinjection_root =
"//third_party/connectedhomeip/third_party/nlfaultinjection"
}
18 changes: 18 additions & 0 deletions silabs_examples/unify-matter-bridge/linux/build_overrides/nlio.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
# Root directory for nlio.
nlio_root = "//third_party/connectedhomeip/third_party/nlio"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
# Root directory for nlunit-test.
nlunit_test_root = "//third_party/connectedhomeip/third_party/nlunit-test"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
openthread_root = "//third_party/openthread/repo"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
ot_br_posix_root = "//third_party/ot-br-posix"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (c) 2020 Project CHIP Authors
#
# 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.

declare_args() {
# Location of the Pigweed repository.
dir_pigweed = "//third_party/connectedhomeip/third_party/pigweed/repo"
}

import("$dir_pigweed/modules.gni")
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This file is automatically generated by Pigweed's environment setup. Do not
# edit it manually or check it in.
declare_args() {
pw_env_setup_CIPD_ARM = "//.environment/cipd/packages/arm"
dir_cipd_arm = "//.environment/cipd/packages/arm"
pw_env_setup_CIPD_PIGWEED = "//.environment/cipd/packages/pigweed"
dir_cipd_pigweed = "//.environment/cipd/packages/pigweed"
pw_env_setup_CIPD_PYTHON = "//.environment/cipd/packages/python"
dir_cipd_python = "//.environment/cipd/packages/python"
pw_env_setup_VIRTUAL_ENV = "//.environment/pigweed-venv"
pw_env_setup_PACKAGE_ROOT = "//.environment/packages"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup attribute_state_cache
* @brief The module cache the attribute states in a memory and provides
* the state when a user of the API request the attribute state using
* ConcreteAttributePath.
*
* @{
*/

#ifndef ATTRIBUTE_STATE_CASH_H
#define ATTRIBUTE_STATE_CASH_H

#include <stdexcept>
#include <map>
#include <variant>
#include <string>
#include <iostream>

#include "matter.h"
#include <lib/core/Optional.h>

using value_t
= std::variant<bool, uint8_t, uint16_t, uint32_t, uint64_t, int, std::string>;
using AttrPath_t = chip::app::ConcreteAttributePath;

class attribute_state_cache
{
public:
attribute_state_cache() {};
~attribute_state_cache() {};

// Get the attribute_state_cache class instance
static attribute_state_cache &get_instance();

/**
* @brief load the attribute state to a memory
*
* @param attributePath A representation of a concrete attribute path.
* @param data The attribute state value.
*/
template<typename T> void set(const AttrPath_t &attributePath, const T &data)
{
attribute_state_container[attributePath] = data;
}

/**
* @brief Get the attribute state from a memory
*
* @param attributePath A representation of a concrete attribute path
*/
template<typename T> const T &get(const AttrPath_t &attributePath) const
{
auto iter = attribute_state_container.find(attributePath);
if (iter != attribute_state_container.end()) {
return std::get<T>(iter->second);
} else {
throw std::out_of_range(
"The attribute path is not in attribute_state_container container");
}
}

private:
// Attribute State Container
std::map<const AttrPath_t, value_t> attribute_state_container;
};

#endif //ATTRIBUTE_STATE_CASH_H
/** @} end attribute_state_cache */
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup matter_cluster_translator
* @brief TODO: Write brief for matter_cluster_translator
*
* TODO: Write component description for matter_cluster_translator
*
* @{
*/

#ifndef MATTER_CLUSTER_TRANSLATOR_HPP
#define MATTER_CLUSTER_TRANSLATOR_HPP

#include <app/CommandHandlerInterface.h>
#include <app/AttributeAccessInterface.h>


namespace unify::matter_bridge
{
/**
* The purpose of the cluster translator is to handle read/write attribute
* calls as well as matter commands. Since the matter data model and the UIC
* data model are very similar we will generally be able to do a 1-1
* translation.
*
* The translator has a ZAP generated handler, which implements a generic
* handler for all clusters which is able to send translate Matter commands
* into mqtt messages. The translator uses
* `InteractionModelEngine::RegisterCommandHandler` to register itself with
* the matter application framework. As the default behavior the translator
* will directly translate the matter command into a unify mqtt command.
*
* The translator will also handle attribute read and attribute writes. For
* attributes the translator uses the system
* `registerAttributeAccessOverride, when an attribute read is requested the
* Unify Reported value should be reported when an attribute write is
* requested the corresponding /WriteAttribute command is published on the
* mqtt side.
*
* The Unify Cluster translator is not required to check the capabilities of
* a node before sending WriteAttributes or other commands.
*
*/
class matter_cluster_translator {

/**
* @brief Register access an interface to a cluster
*
* Register a cluster translator.
*
* @param command_handler
* @param attribute_access
* @return true on success
*/
bool register_cluster(chip::ClusterId, const CommandHandlerInterface& command_handler, const AttributeAccessInterface& attribute_access);
}

}
#endif //MATTER_CLUSTER_TRANSLATOR_HPP
/** @} end matter_cluster_translator */
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup matter_data_storage
* @brief the module persist the dynamic endpoint mapping
* from unify unid and endpoint.
*
* @{
*/

#ifndef MATTER_DATA_STORAGE_HPP
#define MATTER_DATA_STORAGE_HPP

#include <optional>
#include "matter.h"
#include <platform/KeyValueStoreManager.h>

namespace unify::matter_bridge
{
class matter_data_storage
{
public:
struct endpoint_mapping {
const char *unify_unid;
uint8_t unify_endpoint;
std::optional<chip::EndpointId> matter_endpoint;
};
struct group_mapping {
uint16_t matter_group_id;
std::optional<uint16_t> unify_group_id;
};
/**
* @brief Persist data
*
* @param key_value a struct that contain the key and value,
* that should be persisted to the data storage
* @return true if the data is persisted
* @return false if the data is not persisted
*/
template<typename T> bool persist_data(T &key_value);

/**
* @brief Get the persisted dynamic endpoint
*
* @param key_value a struct that contain the key and value
* that the persisted dat copy over
* @return true if value is written on the value field of
* the key_value struct
* @return false if the key is not found in the data storage
*/
template<typename T> bool get_persisted_data(T &key_value);
/**
* @brief remove the persisted data
*
* @param key key
*/
template<typename T> void remove_persisted_data(T &key);

static matter_data_storage &instance();
};

} // namespace unify::matter_bridge
#endif //MATTER_DATA_STORAGE_HPP
/** @} end matter_data_storage */
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup matter_device_mapper
* @brief Unify matter device mapper
*
* The purpose of the device mapper is to translate unify terms into matter terms.
* The device mapper is capable of doing the following:
* - Given a list of unify cluster names find the best matching device type
* - Given a unify cluster name return the matter cluster id
* - Given a unify attribute name return the matter attribute id
* - Given a unify Command name return the matter command id
*
* @{
*/

#ifndef MATTER_DEVICE_TRANSLATOR_HPP
#define MATTER_DEVICE_TRANSLATOR_HPP

#include <string>
#include <optional>
#include <vector>
#include <app-common/zap-generated/ids/Clusters.h>

namespace unify::matter_bridge
{
/**
* @brief Device translator class.
*/
class device_translator
{
public:
/**
* @brief Given list of clustername find possible matching device types.
*
* @param clusters Vector of clusters to find device type for.
* @return vector of possible device types sorted by most likely, or an empty
* vector if none is found.
*/
virtual std::vector<chip::DeviceTypeId>
get_device_types(const std::vector<const char *> &clusters) const;

/**
* @brief It provides the matter device name from device id.
*
* @param device_id the device id.
* @return std::optional containing the device name or std::nullopt_t.
*/
virtual std::optional<const char *>
get_device_name(chip::DeviceTypeId device_id) const;

/**
* @brief Get the cluster id given a unify cluster name.
*
* @param cluster_name
* @return std::optional containing the cluster id or std::nullopt_t.
*/
virtual std::optional<chip::ClusterId>
get_cluster_id(const std::string &cluster_name) const;

/**
* @brief Get the attribute id given a unify cluster name and attribute name.
*
* @param cluster_name
* @param attribute_name
* @return std::optional containg the attribute id or std::nullopt_t.
*/

virtual std::optional<chip::AttributeId>
get_attribute_id(const std::string &cluster_name,
const std::string &attribute_name) const;

/**
* @brief Get the command id given a unify cluster name and command name.
*
* @param cluster_name
* @param attribute_name
* @return std::optional containing the command id or std::nullopt_t.
*/

virtual std::optional<chip::CommandId>
get_command_id(const std::string &cluster_name,
const std::string &command_name) const;

virtual ~device_translator() = default;

static const device_translator& instance() {
static device_translator me;
return me;
}
};
} // namespace unify::matter_bridge

#endif //MATTER_DEVICE_TRANSLATOR_HPP
/** @} end matter_device_mapper */
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

#ifndef MATTER_NODE_STATE_MONITOR_HPP
#define MATTER_NODE_STATE_MONITOR_HPP

#include <string>
#include "unify_node_state_monitor.hpp"
#include "matter_context.hpp"
#include <optional>
#include <map>

namespace unify::matter_bridge
{
class device_translator;

/**
* @brief definition of a bridged endpoint
*
* this holds all the information needed to map the
* matter endpoints to a unify endpoint
*
*/
struct bridged_endpoint {
/// the unid of the bridged endpoint
///
std::string unify_unid;

/// the unify endpoint number
///
uint8_t unify_endpoint;

/// the endpoint id assigned on the matter bridge
///
chip::EndpointId matter_endpoint;

/// the index that holds the matter endpoint in
/// memory
uint16_t index;

/// Matter Device type of the endpoint
///
uint16_t matter_type;

/// the ember endpoint structure, this will contain
/// the complete cluster list
matter_endpoint_context ember_endpoint;

/// Tells if the node is reachable, ie it is possible to communicate with the
/// device it may be that the node is a non-listening device, so it could be
/// that the communication latency is very high
bool reachable;

bridged_endpoint(matter_endpoint_context &&context) :
ember_endpoint(std::move(context))
{}
};

/**
* @brief Matter node state monitor
*
* The matter nodestate monitor is responsible for using a unify_node_state_monitor
* to check when new unify nodes has been added to- or removed from- the network and
* then crating a set of bridged endpoints.
*
* As this components is the first point of contact between the Matter world
* and the unify world, the node state monitor also provides a translation between
* matter endpoints ids and the unify (unid,epid) tuple.
*
*/
class matter_node_state_monitor :
private unify::node_state_monitor::node_state_monitor_interface
{
public:
matter_node_state_monitor(const device_translator &matter_device_translator);

/**
* @brief Get the bridged endpoint by object from unify addresses
*
* @param unid
* @param epid
* @return const bridged_endpoint&
*/
const struct bridged_endpoint *bridged_endpoint(const std::string &unid,
int epid) const;
/**
* @brief Get the unify endpoint address from a matter endpoint id
*
* @return std::pair<std::string unid, int epid>
*/
const struct bridged_endpoint *
bridged_endpoint(chip::EndpointId endpoint) const;

enum update_t {
NODE_ADDED, //<<< A node has been added (or just found)
NODE_DELETED, ///<<< A node has been deleted
NODE_STATE_CHANGED /// A node has changed its reachable state
};

/**
* @brief Callback function for event notifications
*
*/
using event_listener_t
= std::function<void(const struct bridged_endpoint &, update_t)>;

/**
* @brief Register an event listener with the node state monitor
*
* An event listener may be registered with the node state monitor allowing a user
* to get notified when certain events occur.
*
* @param event_listener Callback function to be called
*/
void register_event_listener(const event_listener_t &event_listener);

private:
/**
* @brief called when a unify device is added
*
* When a device is added a number of matter endpoints are created. All
* newly added endpints will get a matter endpoint id assigned.
*
* Then add endpints are created their existence is published on the matter
* fabric. Using the function emberAfSetDynamicEndpoint
*
* @param node
*/
void on_unify_node_added(const unify::node_state_monitor::node &node);

/**
* @brief Called when a Unify node is deleted.
*
* This will remove all endpoints belonging to a unid from mapped endpoint
* list. The removal of the endpoint is announced on the matter fabric
* Using emberAfClearDynamicEndpoint
*
* @param unid
*/
void on_unify_node_removed(const std::string &unid);

/**
* @brief Called when a unify node changes state.
*
* When this function is called all endpoints belonging to a Unify node
* changes its state The updated state must be published on the matter
* fabric
*
* @param unid
* @param state
*/
void on_unify_node_state_changed(const node_state_monitor::node &node);

/**
* @brief Invoke all listeners
*
* Invokes all listeners for all endpoints of the node
*
* @param node
*/
void invoke_listeners(const struct bridged_endpoint &ep,
update_t update) const;
/**
* @brief construct a new bridged endpoint.
*
* this function used by the matter bridge to construct an instance of a
* bridged endpoint. this function will query the unify node state monitor
* about details regarding the endpoint. note all matter endpoints
* has a device type, this is not the case for unify endpoints.
*
* in some cases we need to look at the capabilities of endpoint 0
* to determine the actual device type.
*
* @param node
* @return std::vector<bridged_endpoint>
*/
std::vector<struct bridged_endpoint>
new_bridged_endpoints(const unify::node_state_monitor::node &node);
const device_translator &matter_device_translator;

/** @brief map containing all bridged endpoints which are currently registered
the node state monitor. If an entry is dropped from this list any associated
resources which might be in use by matter will released as well
*/
std::multimap<std::string, struct bridged_endpoint> bridged_endpoints;

/**
* @brief Event listeners
*
*/
std::vector<event_listener_t> event_listeners;
};
} // namespace unify::matter_bridge

#endif //MATTER_NODE_STATE_MONITOR_HPP
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/
#include "attribute_state_cache.hpp"

attribute_state_cache instance;
attribute_state_cache & attribute_state_cache::get_instance()
{
return instance;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

#ifndef ATTRIBUTE_TRANSLATOR_INTERFACE_HPP
#define ATTRIBUTE_TRANSLATOR_INTERFACE_HPP

#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <sstream>
#include <sstream>
#include <regex>

#include "matter.h"
#include "uic_mqtt.h"
#include "matter_node_state_monitor.hpp"
#include "matter_device_translator.hpp"
#include "sl_log.h"

namespace unify::matter_bridge
{

/**
* @brief Base class for attribute access interfaces
*
* This class performs the base functionality for all attribute access
* interfaces for in the Unify Bridge. Upon construction the class will register
* itself with chip::app framework
*
*/
class attribute_translator_interface :
public chip::app::AttributeAccessInterface
{
public:
attribute_translator_interface(matter_node_state_monitor &node_state_monitor,
chip::ClusterId id) :
chip::app::AttributeAccessInterface(
chip::Optional<chip::EndpointId>::Missing(), id),
m_node_state_monitor(node_state_monitor)
{
registerAttributeAccessOverride(this);

//Register the an event listener for subscriptions
auto f = [&](const bridged_endpoint &ep,
matter_node_state_monitor::update_t update) {
attributes_update_subscription(ep, update);
};
node_state_monitor.register_event_listener(f);
}

protected:
/**
* @brief List of cluster names which this translator will be using.
*
* @return std::vector<const char *>
*/
virtual std::vector<const char *> unify_cluster_names() const = 0;

/**
* @brief Called when a reported attribute is updated
*
* @param ep
* @param cluster
* @param attribute
* @param value
*/
virtual void reported_updated(const bridged_endpoint *ep,
const std::string &cluster,
const std::string &attribute,
const nlohmann::json &value)
= 0;
matter_node_state_monitor &m_node_state_monitor;

private:
const char *LOG_TAG = "attribute_cluster_server";

void on_mqtt_message_cb(const char *topic,
const char *message,
const size_t message_length)
{
std::regex rgx("ucl/by-unid"
"/([^/]*)" // UNID
"/ep([^/]*)" // Endpoint
"/([^/]*)" // Cluster
"/Attributes"
"/([^/]*)" // Attribute
"/Reported");
std::smatch match;
std::string topic_str(topic);
if (!std::regex_search(topic_str, match, rgx)) {
return;
}
const std::string &unid = match.str(1);
const std::string &endpoint_id = match.str(2);
const std::string &cluster = match.str(3);
const std::string &attribute = match.str(4);

auto unify_node
= m_node_state_monitor.bridged_endpoint(unid, std::stoi(endpoint_id));
// In Matter Bridge Endpoint 0 is dedicated to the root node (bridge app)
// So unify bridged node will not be assigned endpoint 0.
if (!unify_node) {
sl_log_debug(
LOG_TAG,
"The bridged node is not assigned a matter dynamic endpoint\n");
return;
}

std::string msg(message, message_length);
if (!attribute.empty() && !cluster.empty() && !msg.empty()) {
try {
nlohmann::json jsn = nlohmann::json::parse(msg);
reported_updated(unify_node, cluster, attribute, jsn["value"]);
} catch (const nlohmann::json::parse_error &e) {
sl_log_info(LOG_TAG,
"It was not possible to parse incoming attribute state "
"update since the message payload is not json, %s\n",
e.what());
} catch (const nlohmann::json::type_error &e) {
sl_log_info(
LOG_TAG,
"It was not possible to parse incoming attribute state update since "
"the value of different type or key is not present, %s\n",
e.what());
}
} else {
sl_log_debug(LOG_TAG, "Unknown attributes [%s]", attribute.c_str());
}
}

void
attributes_update_subscription(const bridged_endpoint &ep,
matter_node_state_monitor::update_t update)
{
for (const auto &unify_cluster: unify_cluster_names()) {
std::string topic = "ucl/by-unid/" + ep.unify_unid + "/ep"
+ std::to_string(ep.unify_endpoint) + "/"
+ unify_cluster + "/Attributes/+/Reported";

if (update == matter_node_state_monitor::update_t::NODE_ADDED) {
uic_mqtt_subscribe_ex(
topic.c_str(),
attribute_translator_interface::on_mqtt_message_c_cb,
this);
} else if (update == matter_node_state_monitor::update_t::NODE_DELETED) {
uic_mqtt_unsubscribe_ex(
topic.c_str(),
attribute_translator_interface::on_mqtt_message_c_cb,
this);
}
}
}

static void on_mqtt_message_c_cb(const char *topic,
const char *message,
const size_t message_length,
void *user)
{
attribute_translator_interface *instance
= static_cast<attribute_translator_interface *>(user);
if (instance) {
instance->on_mqtt_message_cb(topic, message, message_length);
}
}
};
} // namespace unify::matter_bridge

#endif //ATTRIBUTE_TRANSLATOR_INTERFACE_HPP

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup bridged_device_basic_info_attribute_translator
* @brief The module collect all relevant information regarding
* Basic/NameAndLocation/State clusters from the unify node and map them to
* matter bridged device basic information cluster.
*
* @{
*/

#ifndef BRIDGED_DEVICE_BASIC_INFO_ATTRIBUTE_TRANSLATOR_HPP
#define BRIDGED_DEVICE_BASIC_INFO_ATTRIBUTE_TRANSLATOR_HPP

#include "attribute_translator_interface.hpp"

namespace unify::matter_bridge
{
class BridgedDeviceBasicInfoAttributeAccess :
public attribute_translator_interface
{
public:
BridgedDeviceBasicInfoAttributeAccess(
matter_node_state_monitor &node_state_monitor);
CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath &aPath,
chip::app::AttributeValueEncoder &aEncoder) override;
CHIP_ERROR Write(const chip::app::ConcreteDataAttributePath &aPath,
chip::app::AttributeValueDecoder &aDecoder) override;

void reported_updated(const bridged_endpoint *ep,
const std::string &cluster,
const std::string &attribute,
const nlohmann::json &unify_value) override;

std::vector<const char *> unify_cluster_names() const override
{
return std::vector<const char *>({"Basic", "NameAndLocation"});
}

private:
uint32_t map_basic_cluster_attributes(
const std::string &dot_dot_basic_attributes) const;
static void unify_node_reachable_state_update(
const bridged_endpoint &ep, matter_node_state_monitor::update_t state);
};

} // namespace unify::matter_bridge

#endif //BRIDGED_DEVICE_BASIC_INFO_ATTRIBUTE_TRANSLATOR_HPP
/** @} end bridged_device_basic_info_attribute_translator */
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

#ifndef COMMAND_TRANSLATOR_INTERFACE_HPP
#define COMMAND_TRANSLATOR_INTERFACE_HPP

#include "matter.h"

#include "matter_node_state_monitor.hpp"
#include "group_translator.hpp"
#include "uic_mqtt.h"
#include "sl_log.h"

namespace unify::matter_bridge {

/**
* @brief Base class for handling translation of commands
*
* This class hold base functionality for command translators, such
* at registering the translator with the chip::app framework.
*
*/
class command_translator_interface : public chip::app::CommandHandlerInterface {
public:
command_translator_interface(const matter_node_state_monitor& node_state_monitor,chip::ClusterId id, const char* name) :
chip::app::CommandHandlerInterface(chip::Optional<chip::EndpointId>::Missing(),
id ),
m_node_state_monitor(node_state_monitor),
cluster_name(name)
{
chip::app::InteractionModelEngine::GetInstance()->RegisterCommandHandler(this);
}

/**
* @brief Send a unify command
*
* This function sends a unify command, either as a Single cast or a group
* cast message, depending on the context.
*
* @param ctxt
* @param cmd
* @param payload
*/
void send_unify_mqtt_cmd(const CommandHandlerInterface::HandlerContext& ctxt,const std::string& cmd, const nlohmann::json& payload) const {
std::string topic;
auto unify_node = m_node_state_monitor.bridged_endpoint(ctxt.mRequestPath.mEndpointId);
if (!unify_node) {
return;
}

if( ctxt.mCommandHandler.GetExchangeContext()->IsGroupExchangeContext() ) {
auto matter_group_id = ctxt.mCommandHandler.GetExchangeContext()->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
auto unify_group_id = group_translator::instance().get_unify_group( {matter_group_id} );
if(unify_group_id) {
topic = "ucl/by-group/" + std::to_string(unify_group_id.value());
} else {
return;
}
} else {
topic = "ucl/by-unid/" + unify_node->unify_unid + "/ep"
+ std::to_string(unify_node->unify_endpoint);
}
topic = topic + "/" + std::string(cluster_name) + "/Commands/"+ cmd;

std::string msg = payload.dump();
sl_log_debug("command_translator_interface", "--- send_unify_mqtt_cmd %s -> %s ---", topic.c_str(), msg.c_str());

uic_mqtt_publish(topic.c_str(), msg.c_str(), msg.size(), true);
}

template<typename T> void send_unify_mqtt_command_with_callbacks(
const CommandHandlerInterface::HandlerContext &ctxt,
T command_data,
void(*send_command_callback)(const char *, uint8_t, T),
void(*send_group_command_callback)(unify_group_t, T)) const
{
auto unify_node = m_node_state_monitor.bridged_endpoint(ctxt.mRequestPath.mEndpointId);
if (!unify_node) {
return;
}

sl_log_debug("command_translator_interface", "Sending translated command from Matter to Unify to node %s", unify_node->unify_unid.c_str());
if( ctxt.mCommandHandler.GetExchangeContext()->IsGroupExchangeContext() ) {
auto matter_group_id = ctxt.mCommandHandler.GetExchangeContext()->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId();
auto unify_group_id = group_translator::instance().get_unify_group( {matter_group_id} );
if(unify_group_id) {
send_group_command_callback(unify_group_id.value(), command_data);
} else {
return;
}
}

send_command_callback(unify_node->unify_unid.c_str(), unify_node->unify_endpoint, command_data);
}

protected:
const matter_node_state_monitor& m_node_state_monitor;
const char* cluster_name;
};

}

#endif //COMMAND_TRANSLATOR_INTERFACE_HPP

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*******************************************************************************
* # License
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
******************************************************************************/
#include "group_command_translator.hpp"
#include "uic_mqtt.h"
#include <iostream>
#include <string>
#include <sstream>
#include "sl_log.h"
#include <nlohmann/json.hpp>
#include <app-common/zap-generated/cluster-objects.h>


using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters::Groups;
using namespace chip::DeviceLayer;
using namespace unify::matter_bridge;

#define LOG_TAG "group_cluster_server"


void GroupClusterCommandHandler::InvokeCommand(
CommandHandlerInterface::HandlerContext &ctxt)
{

std::string cmd;
nlohmann::json payload;

// We copy the TLV reader here, because it is state full. and we need the
// ember group handler to read the parameters as well
TLV::TLVReader tlv;
tlv.Init(ctxt.GetReader());

switch (ctxt.mRequestPath.mCommandId) {
case Commands::AddGroup::Id:
{
Commands::AddGroup::DecodableType addGroupData;
CHIP_ERROR TLVError = DataModel::Decode(tlv, addGroupData);
if(CHIP_ERROR::IsSuccess(TLVError)) {
cmd = "AddGroup";
payload["GroupId"] = addGroupData.groupId;
payload["GroupName"] = std::string(addGroupData.groupName.begin(),addGroupData.groupName.end());

group_translator::matter_group group = { addGroupData.groupId };
group_translator::instance().add_matter_group( group );
}
}
break;
case Commands::RemoveGroup::Id:
{
Commands::RemoveGroup::DecodableType removeGroupData;
CHIP_ERROR TLVError = DataModel::Decode(tlv, removeGroupData);
if(CHIP_ERROR::IsSuccess(TLVError)) {
cmd = "RemoveGroup";
payload["GroupId"] = removeGroupData.groupId;
group_translator::matter_group group = {removeGroupData.groupId };
group_translator::instance().remove_matter_group( group );
}
}
break;
default:
// As we are just "sniffing" on the commands we do not need to
// do the actual matter handling here. This is done by the ember code.
break;
}

// We will not set the handle flag on the group cluster,
// Because it need to be passed to the Matter App framework
ctxt.SetCommandNotHandled();
if(!cmd.empty()) {
send_unify_mqtt_cmd(ctxt, cmd, payload);
}

}



Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/******************************************************************************
* # License
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @file on_off_command_translator.h
* @ingroup components
*
* @brief OnOff cluster command handler for matter interface
*
* @{
*/
#pragma once
#include "matter.h"
#include "command_translator_interface.hpp"

namespace unify::matter_bridge
{
class GroupClusterCommandHandler : public command_translator_interface
{
public:
GroupClusterCommandHandler(const matter_node_state_monitor& node_state_monitor) : command_translator_interface(node_state_monitor,chip::app::Clusters::Groups::Id,"Groups" ) {}
// CommandHandlerInterface
void InvokeCommand(chip::app::CommandHandlerInterface::HandlerContext
&handlerContext) override;

};

} // namespace unify::matter_bridge
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/
#include "group_translator.hpp"
#include "matter_data_storage.hpp"
#include <optional>
#include <sstream>
#include <sstream>
#include <regex>

namespace unify::matter_bridge
{

std::optional<unify_group_t>
group_translator::get_unify_group(const matter_group &group)
{
matter_data_storage::group_mapping group_info = {group.group};
if (matter_data_storage::instance().get_persisted_data(group_info)) {
return group_info.unify_group_id.value();
} else {
return std::nullopt;
}
}

bool group_translator::add_matter_group(const matter_group &group)
{
while (allocated_unify_groups.count(last_allocated_group)) {
last_allocated_group++;
if (last_allocated_group == std::numeric_limits<unify_group_t>::max()) {
return false;
}
}
matter_data_storage::group_mapping group_info
= {group.group, last_allocated_group};
matter_data_storage::instance().persist_data(group_info);
return true;
}

void group_translator::remove_matter_group(const matter_group &group)
{
auto ug = get_unify_group(group);
if (ug) {
matter_data_storage::group_mapping group_info = {group.group};
matter_data_storage::instance().remove_persisted_data(group_info);
}
}

void group_translator::register_unify_group(const unify_group_t &group)
{
allocated_unify_groups.insert(group);
}

void group_translator::on_mqtt_message_cb(const char *topic,
const char *message,
const size_t message_length)
{
std::regex rgx("ucl/by-group"
"/([^/]*)" // group id
"/NodeList"
"/([^/]*)");
std::smatch match;
std::string topic_str(topic);
if (!std::regex_search(topic_str, match, rgx)) {
return;
}
const std::string &group_id = match.str(1);
const unify_group_t group = std::stoi(group_id);
group_translator::register_unify_group(group);
}
} // namespace unify::matter_bridge
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup group_translator
* @brief Translate between matter groups ans unify groups.
*
* The purpose of the group translator is to translate Matter group into Unify groups
*
* @{
*/

#ifndef GROUP_TRANSLATOR_HPP
#define GROUP_TRANSLATOR_HPP

#include <optional>
#include <set>

#include "matter.h"
#include "uic_mqtt.h"
#include <string>

namespace unify::matter_bridge
{

using unify_group_t = uint16_t;

/**
* @brief Group Translator
* The role of the group translator is to translate between Unify Gorup Ids
* and matter group ID's
*/
class group_translator
{
public:
struct matter_group {
//uint32_t fabric;
chip::GroupId group;

bool operator<(const matter_group &b) const
{
return group < b.group;
}
};

group_translator() : last_allocated_group(0)
{
std::string topic_by_group = "ucl/by-group/#";
uic_mqtt_subscribe_ex(topic_by_group.c_str(),
group_translator::on_mqtt_message_c_cb,
this);
}

std::optional<unify_group_t> get_unify_group(const matter_group &group);

/**
* @brief Register a new matter group
*
* This funciton allocates a new unify group to match with the matter group number.
*
* @param group
* @return true
* @return false
*/
bool add_matter_group(const matter_group &group);

/**
* @brief Remove a created group map
*
* @param group
*/
void remove_matter_group(const matter_group &group);

/**
* @brief Register a created unify group
*
* This is used to ensure that we do not allocate unify
* groups which are already in use
*
* @param group
*/
void register_unify_group(const unify_group_t &group);

static group_translator &instance()
{
static group_translator m;
return m;
}

private:
unify_group_t last_allocated_group;
std::set<unify_group_t> allocated_unify_groups;
void on_mqtt_message_cb(const char *topic,
const char *message,
const size_t message_length);
static void on_mqtt_message_c_cb(const char *topic,
const char *message,
const size_t message_length,
void *user)
{
group_translator *instance = static_cast<group_translator *>(user);
if (instance) {
instance->on_mqtt_message_cb(topic, message, message_length);
}
}
};

} // namespace unify::matter_bridge

#endif //GROUP_TRANSLATOR_HPP
/** @} end group_translator */
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/
#include "identify_attribute_translator.hpp"

#include <nlohmann/json.hpp>
#include <sstream>
#include <regex>
#include "matter.h"

#include <attribute_state_cache.hpp>
#include "matter_device_translator.hpp"
#include "uic_mqtt.h"
#include "sl_log.h"

#define LOG_TAG "identify_cluster_server"

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::Identify;
using namespace chip::app::Clusters::Identify::Attributes;
using namespace unify::matter_bridge;

CHIP_ERROR IdentifyAttributeAccess::Read(const ConcreteReadAttributePath &aPath,
AttributeValueEncoder &aEncoder)
{
if (aPath.mClusterId != Clusters::Identify::Id) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (aPath.mAttributeId == IdentifyTime::Id) {
ConcreteAttributePath attr_path = ConcreteAttributePath(aPath.mEndpointId,
aPath.mClusterId,
aPath.mAttributeId);
Attributes::IdentifyTime::TypeInfo::Type remaining_time
= attribute_state_cache::get_instance()
.get<Attributes::IdentifyTime::TypeInfo::Type>(attr_path);
return aEncoder.Encode(remaining_time);
} else {
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}
}

CHIP_ERROR
IdentifyAttributeAccess::Write(const ConcreteDataAttributePath &aPath,
AttributeValueDecoder &aDecoder)
{
if (aPath.mClusterId != Clusters::Identify::Id) {
return CHIP_ERROR_INVALID_ARGUMENT;
}

auto unify_node = m_node_state_monitor.bridged_endpoint(aPath.mEndpointId);

if (!unify_node) {
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}

if (aPath.mAttributeId == IdentifyTime::Id) {
IdentifyTime::TypeInfo::DecodableType timeout_value;
aDecoder.Decode(timeout_value);
nlohmann::json jsn;
jsn["value"] = timeout_value;
std::string identify_time_topic
= "ucl/by-unid/" + unify_node->unify_unid + "/ep"
+ std::to_string(unify_node->unify_endpoint)
+ "/Identify/Attributes/IdentifyTime/Desired";
std::string payload_str = jsn.dump();
uic_mqtt_publish(identify_time_topic.c_str(),
payload_str.c_str(),
payload_str.length(),
true);
return CHIP_NO_ERROR;
} else {
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}
}

void IdentifyAttributeAccess::reported_updated(
const bridged_endpoint *ep,
const std::string &cluster,
const std::string &attribute,
const nlohmann::json &unify_value)
{
auto cluster_id = device_translator::instance().get_cluster_id(cluster);
if (!cluster_id.has_value() || (cluster_id.value() != Identify::Id)) {
return;
}
auto attribute_id
= device_translator::instance().get_attribute_id(cluster, attribute);

if (!attribute_id.has_value()) {
return;
}

if (attribute_id.value() == IdentifyTime::Id) {
chip::EndpointId node_matter_endpoint = ep->matter_endpoint;
ConcreteAttributePath attr_path
= ConcreteAttributePath(node_matter_endpoint,
Identify::Id,
attribute_id.value());
IdentifyTime::TypeInfo::Type value = unify_value;
attribute_state_cache::get_instance()
.set<Attributes::IdentifyTime::TypeInfo::Type>(attr_path, value);
MatterReportingAttributeChangeCallback(node_matter_endpoint,
Clusters::Identify::Id,
Attributes::IdentifyTime::Id,

ZCL_INT16U_ATTRIBUTE_TYPE,
reinterpret_cast<uint8_t *>(&value));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup identify_attribute_translator
* @brief components
*
* It parse and update attribute state update for identify cluster.
*
* @{
*/

#ifndef IDENTIFY_ATTRIBUTE_TRANSLATOR_HPP
#define IDENTIFY_ATTRIBUTE_TRANSLATOR_HPP

#include "attribute_translator_interface.hpp"

namespace unify::matter_bridge
{
class IdentifyAttributeAccess : public attribute_translator_interface
{
public:
IdentifyAttributeAccess(matter_node_state_monitor &node_state_monitor) :
attribute_translator_interface(node_state_monitor,
chip::app::Clusters::Identify::Id)
{}
CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath &aPath,
chip::app::AttributeValueEncoder &aEncoder) override;
CHIP_ERROR Write(const chip::app::ConcreteDataAttributePath &aPath,
chip::app::AttributeValueDecoder &aDecoder) override;

private:
void reported_updated(const bridged_endpoint *ep,
const std::string &cluster,
const std::string &attribute,
const nlohmann::json &unify_value) override;
std::vector<const char *> unify_cluster_names() const override
{
return std::vector<const char *>({"Identify"});
}
};

} // namespace unify::matter_bridge
#endif //IDENTIFY_ATTRIBUTE_TRANSLATOR_HPP
/** @} end identify_attribute_translator */
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/
#include "identify_command_translator.hpp"
#include <attribute_state_cache.hpp>

// Standard library
#include <iostream>
#include <string>
#include <sstream>

// Unify library
#include "uic_mqtt.h"
#include "sl_log.h"

// Third party
#include <nlohmann/json.hpp>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::Identify;
using namespace unify::matter_bridge;

#define LOG_TAG "identify_cluster_command_server"

void IdentifyClusterCommandHandler::InvokeCommand(
CommandHandlerInterface::HandlerContext &ctxt)
{
auto unify_node
= m_node_state_monitor.bridged_endpoint(ctxt.mRequestPath.mEndpointId);

if (!unify_node) {
return;
}
nlohmann::json payload = {};

std::string cmd;
switch (ctxt.mRequestPath.mCommandId) {
case Commands::Identify::Id: {
Commands::Identify::DecodableType data;
CHIP_ERROR TLVError = DataModel::Decode(ctxt.GetReader(), data);
if (TLVError != CHIP_NO_ERROR) {
ctxt.mCommandHandler.AddStatus(
ctxt.mRequestPath,
Protocols::InteractionModel::Status::InvalidCommand);
ctxt.SetCommandHandled();
sl_log_error(LOG_TAG, "Unable to read Identify command payload");
return;
}

cmd = "Identify";
payload["IdentifyTime"] = data.identifyTime;
ctxt.mCommandHandler.AddStatus(
ctxt.mRequestPath,
Protocols::InteractionModel::Status::Success);
ctxt.SetCommandHandled();
break;
}
/**case Commands::IdentifyQuery::Id: {
cmd = "IdentifyQuery";
//read the remaining time in second for identify the endpoint
ConcreteAttributePath attr_path
= ConcreteAttributePath(ctxt.mRequestPath.mEndpointId,
Identify::Id,
Attributes::IdentifyTime::Id);
Attributes::IdentifyTime::TypeInfo::Type remaining_time = 0;
remaining_time
= attribute_state_cache::get_instance()
.get<Attributes::IdentifyTime::TypeInfo::Type>(attr_path);
// Preparing the response
Commands::IdentifyQueryResponse::Type data_response;
data_response.timeout = remaining_time;
ctxt.mCommandHandler.AddResponseData(ctxt.mRequestPath, data_response);
break;
}*/
case Commands::TriggerEffect::Id: {
Commands::TriggerEffect::DecodableType data;
CHIP_ERROR TLVError = DataModel::Decode(ctxt.GetReader(), data);
if (TLVError != CHIP_NO_ERROR) {
ctxt.mCommandHandler.AddStatus(
ctxt.mRequestPath,
Protocols::InteractionModel::Status::InvalidCommand);
ctxt.SetCommandHandled();
sl_log_error(LOG_TAG, "Unable to read TriggerEffect command payload");
return;
}
cmd = "TriggerEffect";
payload["EffectIdentifier"] = static_cast<uint8_t>(data.effectIdentifier);
payload["EffectVariant"] = static_cast<uint8_t>(data.effectVariant);
ctxt.mCommandHandler.AddStatus(
ctxt.mRequestPath,
Protocols::InteractionModel::Status::Success);
ctxt.SetCommandHandled();
break;
}
default: {
ctxt.mCommandHandler.AddStatus(
ctxt.mRequestPath,
Protocols::InteractionModel::Status::UnsupportedCommand);
ctxt.SetCommandHandled();
sl_log_info(LOG_TAG, "Unknown identify cluster command is received");
return;
}
}
send_unify_mqtt_cmd( ctxt, cmd, payload);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup identify_command_translator
* @brief Identify cluster command translator for matter interface
*
* Translate Identify cluster commands from the matter protocol to unify
* dotdot data model.
*
* @{
*/

#ifndef IDENTIFY_COMMAND_TRANSLATOR_HPP
#define IDENTIFY_COMMAND_TRANSLATOR_HPP

#pragma once
#include "command_translator_interface.hpp"

namespace unify::matter_bridge
{
class IdentifyClusterCommandHandler : public command_translator_interface
{
public:
IdentifyClusterCommandHandler(
const matter_node_state_monitor &node_state_monitor) :
command_translator_interface(node_state_monitor,
chip::app::Clusters::Identify::Id,"Identify")
{}
void InvokeCommand(chip::app::CommandHandlerInterface::HandlerContext
&HandlerContext) override;
};
} // namespace unify::matter_bridge

#endif //IDENTIFY_COMMAND_TRANSLATOR_HPP
/** @} end identify_command_translator */

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup level_attribute_translator
* @brief Level attribute translator for matter interface
*
* Translates attributes from matter to unify and unify to matter.
*
* @{
*/

#ifndef LEVEL_ATTRIBUTE_TRANSLATOR_HPP
#define LEVEL_ATTRIBUTE_TRANSLATOR_HPP

#include "attribute_translator_interface.hpp"

namespace unify::matter_bridge
{
class LevelAttributeAccess : public attribute_translator_interface
{
public:
LevelAttributeAccess(matter_node_state_monitor &node_state_monitor) :
attribute_translator_interface(node_state_monitor,
chip::app::Clusters::LevelControl::Id)
{}

CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath &aPath,
chip::app::AttributeValueEncoder &aEncoder) override;
CHIP_ERROR Write(const chip::app::ConcreteDataAttributePath &aPath,
chip::app::AttributeValueDecoder &aDecoder) override;

void
attributes_update_subscription(const bridged_endpoint &ep,
matter_node_state_monitor::update_t update);

private:
CHIP_ERROR
ReadAttributeState(const chip::app::ConcreteReadAttributePath &aPath,
chip::app::AttributeValueEncoder &aEncoder);

void reported_updated(const bridged_endpoint *ep,
const std::string &cluster,
const std::string &attribute,
const nlohmann::json &unify_value) override;
std::vector<const char *> unify_cluster_names() const override
{
return std::vector<const char *>({"Level"});
}
};
} // namespace unify::matter_bridge

#endif //LEVEL_ATTRIBUTE_TRANSLATOR_HPP
/** @} end level_attribute_translator */
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/
#include "level_command_translator.hpp"

// Standard library
#include <iostream>
#include <string>
#include <sstream>

// Unify library
#include "uic_mqtt.h"
#include "sl_log.h"

namespace Unify
{
#include "dotdot_mqtt_send_commands.h"
}

// Third party
#include <nlohmann/json.hpp>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::LevelControl;
using namespace chip::DeviceLayer;
using namespace unify::matter_bridge;

#define LOG_TAG "level_command_cluster_server"

void set_command_handled_success(CommandHandlerInterface::HandlerContext &ctxt)
{
ctxt.mCommandHandler.AddStatus(ctxt.mRequestPath,
Protocols::InteractionModel::Status::Success);
ctxt.SetCommandHandled();
}

void set_command_handled_failed(CommandHandlerInterface::HandlerContext &ctxt)
{
ctxt.mCommandHandler.AddStatus(
ctxt.mRequestPath,
Protocols::InteractionModel::Status::InvalidCommand);
ctxt.SetCommandHandled();
}

void LevelClusterCommandHandler::InvokeCommand(
CommandHandlerInterface::HandlerContext &ctxt)
{
auto unify_node
= m_node_state_monitor.bridged_endpoint(ctxt.mRequestPath.mEndpointId);

if (!unify_node) {
return;
}

switch (ctxt.mRequestPath.mCommandId) {
case Commands::MoveToLevel::Id: {
Commands::MoveToLevel::DecodableType move_to_level_command_data;
CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(), move_to_level_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(LOG_TAG,
"Failed processing MoveToLevel command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_move_to_level_fields_t
move_to_level_fields;
move_to_level_fields.level = move_to_level_command_data.level;
move_to_level_fields.transition_time
= move_to_level_command_data.transitionTime;
move_to_level_fields.options_mask = move_to_level_command_data.optionMask;
move_to_level_fields.options_override
= move_to_level_command_data.optionOverride;

send_unify_mqtt_command_with_callbacks<
const Unify::uic_mqtt_dotdot_level_command_move_to_level_fields_t *>(
ctxt,
&move_to_level_fields,
Unify::uic_mqtt_dotdot_level_publish_move_to_level_command,
Unify::uic_mqtt_dotdot_level_publish_move_to_level_command_to_group);

set_command_handled_success(ctxt);
} break;
case Commands::Move::Id: {
Commands::Move::DecodableType move_command_data;
CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(), move_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(LOG_TAG,
"Failed processing Move command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_move_fields_t move_fields;
move_fields.move_mode
= static_cast<Unify::MoveStepMode>(move_command_data.moveMode);
move_fields.rate = move_command_data.rate;
move_fields.options_mask = move_command_data.optionMask;
move_fields.options_override = move_command_data.optionOverride;

send_unify_mqtt_command_with_callbacks<
const Unify::uic_mqtt_dotdot_level_command_move_fields_t *>(
ctxt,
&move_fields,
Unify::uic_mqtt_dotdot_level_publish_move_command,
Unify::uic_mqtt_dotdot_level_publish_move_command_to_group);
set_command_handled_success(ctxt);
} break;
case Commands::Step::Id: {
Commands::Step::DecodableType step_command_data;

CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(), step_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(LOG_TAG,
"Failed processing Step command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_step_fields_t step_fields;
step_fields.step_mode
= static_cast<Unify::MoveStepMode>(step_command_data.stepMode);
step_fields.step_size = step_command_data.stepSize;
step_fields.transition_time = step_command_data.transitionTime;
step_fields.options_mask = step_command_data.optionMask;
step_fields.options_override = step_command_data.optionOverride;
send_unify_mqtt_command_with_callbacks<
const Unify::uic_mqtt_dotdot_level_command_step_fields_t *>(
ctxt,
&step_fields,
Unify::uic_mqtt_dotdot_level_publish_step_command,
Unify::uic_mqtt_dotdot_level_publish_step_command_to_group);
set_command_handled_success(ctxt);
} break;
case Commands::Stop::Id: {
Commands::Stop::DecodableType stop_command_data;
CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(), stop_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(LOG_TAG,
"Failed processing Stop command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_stop_fields_t stop_fields;
stop_fields.options_mask = stop_command_data.optionMask;
stop_fields.options_override = stop_command_data.optionOverride;
send_unify_mqtt_command_with_callbacks<
const Unify::uic_mqtt_dotdot_level_command_stop_fields_t *>(
ctxt,
&stop_fields,
Unify::uic_mqtt_dotdot_level_publish_stop_command,
Unify::uic_mqtt_dotdot_level_publish_stop_command_to_group);
set_command_handled_success(ctxt);
} break;
case Commands::MoveToLevelWithOnOff::Id: {
Commands::MoveToLevelWithOnOff::DecodableType
move_to_level_with_on_off_command_data;
CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(),
move_to_level_with_on_off_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(
LOG_TAG,
"Failed processing MoveToLevelWithOnOff command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_move_to_level_with_on_off_fields_t
move_to_level_with_on_off_fields;
move_to_level_with_on_off_fields.level
= move_to_level_with_on_off_command_data.level;
move_to_level_with_on_off_fields.transition_time
= move_to_level_with_on_off_command_data.transitionTime;
move_to_level_with_on_off_fields.options_mask = 0;
move_to_level_with_on_off_fields.options_override = 0;
send_unify_mqtt_command_with_callbacks<
const Unify::
uic_mqtt_dotdot_level_command_move_to_level_with_on_off_fields_t *>(
ctxt,
&move_to_level_with_on_off_fields,
Unify::uic_mqtt_dotdot_level_publish_move_to_level_with_on_off_command,
Unify::
uic_mqtt_dotdot_level_publish_move_to_level_with_on_off_command_to_group);
set_command_handled_success(ctxt);
} break;
case Commands::MoveWithOnOff::Id: {
Commands::MoveWithOnOff::DecodableType move_with_on_off_command_data;
CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(), move_with_on_off_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(
LOG_TAG,
"Failed processing MoveWithOnOff command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_move_with_on_off_fields_t
move_with_on_off_fields;
move_with_on_off_fields.move_mode = static_cast<Unify::MoveStepMode>(
move_with_on_off_command_data.moveMode);
move_with_on_off_fields.rate = move_with_on_off_command_data.rate;
move_with_on_off_fields.options_mask = 0;
move_with_on_off_fields.options_override = 0;
send_unify_mqtt_command_with_callbacks<
const Unify::uic_mqtt_dotdot_level_command_move_with_on_off_fields_t *>(
ctxt,
&move_with_on_off_fields,
Unify::uic_mqtt_dotdot_level_publish_move_with_on_off_command,
Unify::uic_mqtt_dotdot_level_publish_move_with_on_off_command_to_group);
set_command_handled_success(ctxt);
} break;
case Commands::StepWithOnOff::Id: {
Commands::StepWithOnOff::DecodableType step_with_on_off_command_data;
CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(), step_with_on_off_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(
LOG_TAG,
"Failed processing StepWithOnOff command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_step_with_on_off_fields_t
step_with_on_off_fields;
step_with_on_off_fields.step_mode = static_cast<Unify::MoveStepMode>(
step_with_on_off_command_data.stepMode);
step_with_on_off_fields.step_size
= step_with_on_off_command_data.stepSize;
step_with_on_off_fields.transition_time
= step_with_on_off_command_data.transitionTime;

send_unify_mqtt_command_with_callbacks<
const Unify::uic_mqtt_dotdot_level_command_step_with_on_off_fields_t *>(
ctxt,
&step_with_on_off_fields,
Unify::uic_mqtt_dotdot_level_publish_step_with_on_off_command,
Unify::uic_mqtt_dotdot_level_publish_step_with_on_off_command_to_group);

set_command_handled_success(ctxt);
} break;
case Commands::StopWithOnOff::Id: {
Commands::StopWithOnOff::DecodableType stop_with_on_off_command_data;
CHIP_ERROR TLVError
= DataModel::Decode(ctxt.GetReader(), stop_with_on_off_command_data);

if (!CHIP_ERROR::IsSuccess(TLVError)) {
set_command_handled_failed(ctxt);
sl_log_error(
LOG_TAG,
"Failed processing StopWithOnOff command on Level Cluster");
return;
}

Unify::uic_mqtt_dotdot_level_command_stop_with_on_off_fields_t
stop_with_on_off_fields;
stop_with_on_off_fields.options_mask = 0;
stop_with_on_off_fields.options_override = 0;

send_unify_mqtt_command_with_callbacks<
const Unify::uic_mqtt_dotdot_level_command_stop_with_on_off_fields_t *>(
ctxt,
&stop_with_on_off_fields,
Unify::uic_mqtt_dotdot_level_publish_stop_with_on_off_command,
Unify::uic_mqtt_dotdot_level_publish_stop_with_on_off_command_to_group);
set_command_handled_success(ctxt);
} break;
default: {
sl_log_error(LOG_TAG,
"Unsupported command on Level Cluster: %d",
ctxt.mRequestPath.mCommandId);
return;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @defgroup level_command_translator
* @brief Level command translator for matter interface
*
* Translates commands from matter to unify and unify to matter.
*
* @{
*/

#ifndef LEVEL_COMMAND_TRANSLATOR_HPP
#define LEVEL_COMMAND_TRANSLATOR_HPP

#pragma once
#include "matter.h"
#include "command_translator_interface.hpp"

namespace unify::matter_bridge
{
class LevelClusterCommandHandler : public command_translator_interface
{
public:
LevelClusterCommandHandler(
const matter_node_state_monitor &node_state_monitor) :
command_translator_interface(
node_state_monitor, chip::app::Clusters::LevelControl::Id, "Groups")
{}
// CommandHandlerInterface
void InvokeCommand(chip::app::CommandHandlerInterface::HandlerContext
&handlerContext) override;
};
} // namespace unify::matter_bridge

#endif //LEVEL_COMMAND_TRANSLATOR_HPP
/** @} end level_command_translator */
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/
/****************************************************************************
* @file
* @brief Implementation for the OnOff Server Cluster
***************************************************************************/
#include <nlohmann/json.hpp>
#include <sstream>
#include <regex>
#include "matter.h"

#include <attribute_state_cache.hpp>
#include "on_off_attribute_translator.hpp"
#include "matter_device_translator.hpp"
#include "uic_mqtt.h"
#include "sl_log.h"

#define LOG_TAG "on_off_cluster_server"

using namespace std;
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::OnOff;
using namespace chip::app::Clusters::OnOff::Attributes;
using namespace unify::matter_bridge;

CHIP_ERROR
OnOffAttributeAccess::Read(const ConcreteReadAttributePath &aPath,
AttributeValueEncoder &aEncoder)
{
if (aPath.mClusterId != Clusters::OnOff::Id) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
return ReadAttributeState(aPath, aEncoder);
}

CHIP_ERROR OnOffAttributeAccess::Write(const ConcreteDataAttributePath &aPath,
AttributeValueDecoder &aDecoder)
{
if (aPath.mClusterId != Clusters::OnOff::Id) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
auto unify_node = m_node_state_monitor.bridged_endpoint(aPath.mEndpointId);

if (!unify_node) {
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}

std::string attribute_name;
nlohmann::json jsn;
switch (aPath.mAttributeId) {
case Attributes::OnOff::Id: {
sl_log_info(LOG_TAG, "OnOff attribute is not writable.\n");
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}
case Attributes::OnTime::Id: {
Attributes::OnTime::TypeInfo::DecodableType on_time_payload;
aDecoder.Decode(on_time_payload);
jsn["value"] = on_time_payload;
attribute_name = string("OnTime");
break;
}
case Attributes::OffWaitTime::Id: {
Attributes::OffWaitTime::TypeInfo::DecodableType off_wait_time_value;
aDecoder.Decode(off_wait_time_value);
jsn["value"] = off_wait_time_value;
attribute_name = string("OffWaitTime");
break;
}
case Attributes::StartUpOnOff::Id: {
OnOffStartUpOnOff start_up_on_off_value{OnOffStartUpOnOff::kOff};
aDecoder.Decode(start_up_on_off_value);
jsn["value"] = start_up_on_off_value;
attribute_name = string("StartUpOnOff");
break;
}
case Attributes::GlobalSceneControl::Id: {
Attributes::GlobalSceneControl::TypeInfo::DecodableType
global_scene_control_value;
aDecoder.Decode(global_scene_control_value);
jsn["value"] = global_scene_control_value;
attribute_name = string("GlobalSceneControl");
break;
}
default:
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}

if (!attribute_name.empty()) {
std::string onoff_topic;
std::string payload_str;
onoff_topic = "ucl/by-unid/" + unify_node->unify_unid + "/ep"
+ std::to_string(unify_node->unify_endpoint)
+ "/OnOff/Attributes/" + attribute_name + "/Desired";
payload_str = jsn.dump();
uic_mqtt_publish(onoff_topic.c_str(),
payload_str.c_str(),
payload_str.length(),
true);
}

return CHIP_NO_ERROR;
}

CHIP_ERROR OnOffAttributeAccess::ReadAttributeState(
const ConcreteReadAttributePath &aPath, AttributeValueEncoder &aEncoder)
{
sl_log_info(LOG_TAG, "read %i\n", aPath.mEndpointId);

ConcreteAttributePath atrpath = ConcreteAttributePath(aPath.mEndpointId,
aPath.mClusterId,
aPath.mAttributeId);
try {
switch (aPath.mAttributeId) {
case Attributes::OnOff::Id: {
Attributes::OnOff::TypeInfo::Type read_on_off_attr_state
= attribute_state_cache::get_instance()
.get<Attributes::OnOff::TypeInfo::Type>(atrpath);
return aEncoder.Encode(read_on_off_attr_state);
}
case Attributes::OnTime::Id: {
Attributes::OnTime::TypeInfo::Type read_on_time_attr_state
= attribute_state_cache::get_instance()
.get<Attributes::OnTime::TypeInfo::Type>(atrpath);
return aEncoder.Encode(read_on_time_attr_state);
}
case Attributes::StartUpOnOff::Id: {
uint8_t read_start_up_on_off_attr_state
= attribute_state_cache::get_instance().get<uint8_t>(atrpath);
return aEncoder.Encode(read_start_up_on_off_attr_state);
}
case Attributes::OffWaitTime::Id: {
Attributes::OffWaitTime::TypeInfo::Type read_off_wait_time_attr_state
= attribute_state_cache::get_instance()
.get<Attributes::OffWaitTime::TypeInfo::Type>(atrpath);
return aEncoder.Encode(read_off_wait_time_attr_state);
}
case Attributes::GlobalSceneControl::Id: {
Attributes::GlobalSceneControl::TypeInfo::Type
read_global_scene_attr_state
= attribute_state_cache::get_instance()
.get<Attributes::GlobalSceneControl::TypeInfo::Type>(atrpath);
return aEncoder.Encode(read_global_scene_attr_state);
}
default:
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}
} catch (const std::out_of_range &e) {
sl_log_info(
LOG_TAG,
"The request attribute Path is not found in the attribute state "
"contanier, %s\n",
e.what());
return CHIP_ERROR_NO_MESSAGE_HANDLER;
}
}

void OnOffAttributeAccess::reported_updated(const bridged_endpoint *ep,
const std::string &cluster,
const std::string &attribute,
const nlohmann::json &unify_value)
{
auto cluster_id = device_translator::instance().get_cluster_id(cluster);

if (!cluster_id.has_value() || (cluster_id.value() != Clusters::OnOff::Id)) {
return;
}

// get attribute id
auto attribute_id
= device_translator::instance().get_attribute_id(cluster, attribute);

if (!attribute_id.has_value()) {
return;
}

chip::EndpointId node_matter_endpoint = ep->matter_endpoint;
ConcreteAttributePath attrpath = ConcreteAttributePath(node_matter_endpoint,
Clusters::OnOff::Id,
attribute_id.value());

switch (attribute_id.value()) {
case Attributes::OnOff::Id: {
Attributes::OnOff::TypeInfo::Type value = unify_value;
sl_log_debug(LOG_TAG,
"OnOff attribute value is %s",
value ? "On" : "Off");
attribute_state_cache::get_instance()
.set<Attributes::OnOff::TypeInfo::Type>(attrpath, value);
MatterReportingAttributeChangeCallback(
node_matter_endpoint,
Clusters::OnOff::Id,
Attributes::OnOff::Id,

ZCL_BOOLEAN_ATTRIBUTE_TYPE,
reinterpret_cast<uint8_t *>(&value));
break;
}
case Attributes::OnTime::Id: {
Attributes::OnTime::TypeInfo::Type value = unify_value;
attribute_state_cache::get_instance()
.set<Attributes::OnTime::TypeInfo::Type>(attrpath, value);
MatterReportingAttributeChangeCallback(
node_matter_endpoint,
Clusters::OnOff::Id,
Attributes::OnTime::Id,

ZCL_INT16U_ATTRIBUTE_TYPE,
reinterpret_cast<uint8_t *>(&value));
break;
}
case Attributes::OffWaitTime::Id: {
Attributes::OffWaitTime::TypeInfo::Type value = unify_value;
attribute_state_cache::get_instance()
.set<Attributes::OffWaitTime::TypeInfo::Type>(attrpath, value);
MatterReportingAttributeChangeCallback(
node_matter_endpoint,
Clusters::OnOff::Id,
Attributes::OffWaitTime::Id,

ZCL_INT16U_ATTRIBUTE_TYPE,
reinterpret_cast<uint8_t *>(&value));
break;
}
case Attributes::StartUpOnOff::Id: {
uint8_t value = unify_value;
attribute_state_cache::get_instance().set<uint8_t>(attrpath, value);
MatterReportingAttributeChangeCallback(
node_matter_endpoint,
Clusters::OnOff::Id,
Attributes::StartUpOnOff::Id,

ZCL_INT8U_ATTRIBUTE_TYPE,
reinterpret_cast<uint8_t *>(&value));
break;
}
case Attributes::GlobalSceneControl::Id: {
Attributes::GlobalSceneControl::TypeInfo::Type value = unify_value;
attribute_state_cache::get_instance()
.set<Attributes::GlobalSceneControl::TypeInfo::Type>(attrpath, value);
MatterReportingAttributeChangeCallback(
node_matter_endpoint,
Clusters::OnOff::Id,
Attributes::GlobalSceneControl::Id,

ZCL_BOOLEAN_ATTRIBUTE_TYPE,
reinterpret_cast<uint8_t *>(&value));
break;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/******************************************************************************
* # License
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @file on_off_attribute_translator.h
* @ingroup components
*
* @brief OnOff cluster attribute state transition handler
*
* @{
*/

#include "attribute_translator_interface.hpp"

namespace unify::matter_bridge
{
class OnOffAttributeAccess : public attribute_translator_interface
{
public:
OnOffAttributeAccess(matter_node_state_monitor &node_state_monitor) :
attribute_translator_interface(node_state_monitor,
chip::app::Clusters::OnOff::Id)
{}

CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath &aPath,
chip::app::AttributeValueEncoder &aEncoder) override;
CHIP_ERROR Write(const chip::app::ConcreteDataAttributePath &aPath,
chip::app::AttributeValueDecoder &aDecoder) override;

private:
CHIP_ERROR
ReadAttributeState(const chip::app::ConcreteReadAttributePath &aPath,
chip::app::AttributeValueEncoder &aEncoder);

void reported_updated(const bridged_endpoint *ep,
const std::string& cluster,
const std::string& attribute,
const nlohmann::json &unify_value) override;

std::vector<const char *> unify_cluster_names() const override {return std::vector<const char *>({ "OnOff" });}
};

} // namespace unify::matter_bridge
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*******************************************************************************
* # License
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
******************************************************************************/
#include "on_off_command_translator.h"
#include <iostream>
#include <string>
#include <sstream>
#include "sl_log.h"

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::OnOff;
using namespace chip::DeviceLayer;
using namespace unify::matter_bridge;

#define LOG_TAG "on_off_cluster_server"

void OnOffClusterCommandHandler::InvokeCommand(
CommandHandlerInterface::HandlerContext &ctxt)
{
std::string cmd;

switch (ctxt.mRequestPath.mCommandId) {
case Commands::On::Id:
cmd = "On";
break;
case Commands::Off::Id:
cmd = "Off";
break;
case Commands::Toggle::Id:
cmd = "Toggle";
break;
}

if(!cmd.empty()) {
ctxt.mCommandHandler.AddStatus(ctxt.mRequestPath, Protocols::InteractionModel::Status::Success);
send_unify_mqtt_cmd(ctxt,cmd, nlohmann::json() );
} else {
ctxt.mCommandHandler.AddStatus(ctxt.mRequestPath, Protocols::InteractionModel::Status::UnsupportedCommand);
}
ctxt.SetCommandHandled();
}



Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/******************************************************************************
* # License
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @file on_off_command_translator.h
* @ingroup components
*
* @brief OnOff cluster command handler for matter interface
*
* @{
*/
#pragma once
#include "matter.h"
#include "command_translator_interface.hpp"

namespace unify::matter_bridge
{
class OnOffClusterCommandHandler : public command_translator_interface
{
public:
OnOffClusterCommandHandler(const matter_node_state_monitor& node_state_monitor) : command_translator_interface(node_state_monitor,chip::app::Clusters::OnOff::Id,"OnOff") {}
// CommandHandlerInterface
void InvokeCommand(chip::app::CommandHandlerInterface::HandlerContext
&handlerContext) override;

};

} // namespace unify::matter_bridge
52 changes: 52 additions & 0 deletions silabs_examples/unify-matter-bridge/linux/src/demo_uic_cli.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/******************************************************************************
* # License
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

#include "demo_uic_cli.h"

// UIC components
#include "sl_log.h"
#include "uic_stdin.hpp"

/// File descripter for output stream
static int out_stream;

// UIC demo application pre decleration of application commands functions
static sl_status_t handle_demo(const handle_args_t & arg);

#define LOG_TAG "demo_uic_cli"

// Command map
const std::map<std::string, std::pair<std::string, handler_func>> commands = {
{ "demo_command", { "Demo command doing nothing but adding a command to base CLI", handle_demo } }
};

// Public
void demo_uic_cli_setup()
{
uic_stdin_add_commands(commands);
uic_stdin_set_prompt_string("DEMO_UIC> ");
out_stream = uic_stdin_get_output_fd();
}

// Internal
static sl_status_t handle_demo(const handle_args_t & arg)
{
sl_log_debug(LOG_TAG, "UIC demo CLI printing args");

for (auto it = arg.begin(); it != arg.end(); ++it)
{
printf("arg: %s\n", it->c_str());
}

return SL_STATUS_OK;
}
43 changes: 43 additions & 0 deletions silabs_examples/unify-matter-bridge/linux/src/demo_uic_cli.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/******************************************************************************
* # License
* <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
*****************************************************************************/

/**
* @file zpc_stdin_command_handling.h
* @brief TODO: Write brief for zpc_stdin_command_handling
*
* TODO: Write component description for zpc_stdin_command_handling
*
* @{
*/

#ifndef DEMO_UIC_CLI_H
#define DEMO_UIC_CLI_H

#ifdef __cplusplus
extern "C" {
#endif

#include "sl_status.h"

/**
* @brief Add command line interface commands to the Unify process.
*
*/
void demo_uic_cli_setup();

#ifdef __cplusplus
}
#endif

#endif // DEMO_UIC_CLI_H
/** @} end demo_uic_cli */
Loading

0 comments on commit 7c92b22

Please sign in to comment.