[ESP32] Fix few attributes with fixed quality in DeviceInfoProvider (#32893)
* [ESP32] Fix few attributes with fixed quality in DeviceInfoProvider
Fixed labels, supported locales, supported calendar types were being
read from the nvs(flash) and during OTA its a hassle if one wants to
upgrade these values. Added few APIs to set the data for these
attributes in ESP32DeviceInfoProvider.
* Restyled by clang-format
* Restyled by prettier-markdown
* fix the lint errors
* Add back the original Device info provider which reads from the nvs
Add StaticESP32DeviceInfoProvider along with APIs to set data
Remove changes from example and add a guide along with usage
---------
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/docs/guides/esp32/README.md b/docs/guides/esp32/README.md
index e487a58..443058a 100644
--- a/docs/guides/esp32/README.md
+++ b/docs/guides/esp32/README.md
@@ -18,3 +18,4 @@
- [Matter OTA](ota.md)
- [Generating and Using ESP Secure Cert Partition](secure_cert_partition.md)
- [BLE Settings](ble_settings.md)
+- [Providers](providers.md)
diff --git a/docs/guides/esp32/factory_data.md b/docs/guides/esp32/factory_data.md
index 9ea2e12..638d3aa 100644
--- a/docs/guides/esp32/factory_data.md
+++ b/docs/guides/esp32/factory_data.md
@@ -44,13 +44,9 @@
- Serial Number
- Unique identifier
-- Device information
- - Fixed Labels
- - Supported locales
- - Supported calendar types
- - Supported modes
- - Note: As per spec at max size of label should be 64 and `\0` will be
- added at the end.
+- Supported modes
+ - Note: As per spec at max size of label should be 64 and `\0` will be
+ added at the end.
### Configuration Options
diff --git a/docs/guides/esp32/providers.md b/docs/guides/esp32/providers.md
new file mode 100644
index 0000000..59b91b6
--- /dev/null
+++ b/docs/guides/esp32/providers.md
@@ -0,0 +1,76 @@
+## Providers Implemented for ESP32 Platform
+
+The ESP32 platform has implemented several providers that can be used with data
+stored in the factory or by setting fixed data.
+
+Below are the providers that have been implemented:
+
+- [Commissionable Data Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L47)
+ This provider reads the discriminator and setup pincode related parameters
+ from the factory partition.
+- [Device Attestation Credentials Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56)
+ This provider manages the attestation data.
+- [Device Instance Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L86)
+ This provider reads basic device information from the factory partition.
+- [Device Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32DeviceInfoProvider.h#L31)
+ This provider provides fixed labels, supported calendar types, and supported
+ locales from the factory partition.
+- [Supported Modes](https://github.com/project-chip/connectedhomeip/blob/master/examples/platform/esp32/mode-support/static-supported-modes-manager.h#L28)
+ This provider offers the supported modes for the mode-select cluster.
+
+More information can be found in the [factory data guide](factory_data.md).
+
+### Device Info Provider
+
+Currently, there are two implementations for this provider:
+
+1. [Reads data stored in the factory partition](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56)
+ _(This will be deprecated in the future)_
+2. [Provides APIs to set fixed data that gets read later](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/StaticESP32DeviceInfoProvider.h)
+
+- New products should use the `StaticESP32DeviceInfoProvider`. Utilize the
+ `Set...()` APIs to set the fixed data.
+- Existing products using the first implementation can continue to use it if
+ they do not wish to change the data.
+- For products using the first implementation and wanting to change the fixed
+ data via OTA, they should switch to the second implementation in the OTA
+ image and use the `Set...()` APIs to set the fixed data.
+
+#### Example:
+
+```cpp
+#include <platform/ESP32/StaticESP32FactoryDataProvider.h>
+
+DeviceLayer::StaticESP32DeviceInfoProvider deviceInfoProvider;
+
+// Define array for Supported Calendar Types
+using namespace chip::app::Clusters::TimeFormatLocalization::CalendarTypeEnum;
+CalendarTypeEnum supportedCalendarTypes[] = {
+ CalendarTypeEnum::kGregorian, CalendarTypeEnum::kCoptic,
+ CalendarTypeEnum::kEthiopian, CalendarTypeEnum::kChinese,
+};
+
+// Define array for Supported Locales
+const char* supportedLocales[] = {
+ "en-US",
+ "en-EU",
+};
+
+// Define array for Fixed labels { EndpointId, Label, Value }
+struct StaticESP32DeviceInfoProvider::FixedLabelEntry fixedLabels[] = {
+ { 0, "Room", "Bedroom 2" },
+ { 0, "Orientation", "North" },
+ { 0, "Direction", "Up" },
+};
+
+Span<CalendarTypeEnum> sSupportedCalendarTypes(supportedCalendarTypes);
+Span<const char*> sSupportedLocales(supportedLocales);
+Span<StaticESP32DeviceInfoProvider::FixedLabelEntry> sFixedLabels(fixedLabels);
+
+{
+ deviceInfoProvider.SetSupportedLocales(sSupportedLocales);
+ deviceInfoProvider.SetSupportedCalendarTypes(sSupportedCalendarTypes);
+ deviceInfoProvider.SetFixedLabels(sFixedLabels);
+ DeviceLayer::SetDeviceInfoProvider(&deviceInfoProvider);
+}
+```
diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py
index a4ccce5..ff00dae 100755
--- a/scripts/tools/generate_esp32_chip_factory_bin.py
+++ b/scripts/tools/generate_esp32_chip_factory_bin.py
@@ -18,15 +18,12 @@
import argparse
import base64
-import enum
import logging
import os
import sys
from types import SimpleNamespace
import cryptography.x509
-from bitarray import bitarray
-from bitarray.util import ba2int
from esp_secure_cert.tlv_format import generate_partition_ds, generate_partition_no_ds, tlv_priv_key_t, tlv_priv_key_type_t
CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('scripts', 'tools'))]
@@ -152,52 +149,9 @@
'encoding': 'hex2bin',
'value': None,
},
- # DeviceInfoProvider
- 'cal-types': {
- 'type': 'data',
- 'encoding': 'u32',
- 'value': None,
- },
- 'locale-sz': {
- 'type': 'data',
- 'encoding': 'u32',
- 'value': None,
- },
-
- # Other device info provider keys are dynamically generated
- # in the respective functions.
}
-class CalendarTypes(enum.Enum):
- Buddhist = 0
- Chinese = 1
- Coptic = 2
- Ethiopian = 3
- Gregorian = 4
- Hebrew = 5
- Indian = 6
- Islamic = 7
- Japanese = 8
- Korean = 9
- Persian = 10
- Taiwanese = 11
-
-
-# Supported Calendar types is stored as a bit array in one uint32_t.
-def calendar_types_to_uint32(calendar_types):
- result = bitarray(32, endian='little')
- result.setall(0)
- for calendar_type in calendar_types:
- try:
- result[CalendarTypes[calendar_type].value] = 1
- except KeyError:
- logging.error('Unknown calendar type: %s', calendar_type)
- logging.error('Supported calendar types: %s', ', '.join(CalendarTypes.__members__))
- sys.exit(1)
- return ba2int(result)
-
-
def ishex(s):
try:
_ = int(s, 16)
@@ -205,31 +159,6 @@
except ValueError:
return False
-# get_fixed_label_dict() converts the list of strings to per endpoint dictionaries.
-# example input : ['0/orientation/up', '1/orientation/down', '2/orientation/down']
-# example output : {'0': [{'orientation': 'up'}], '1': [{'orientation': 'down'}], '2': [{'orientation': 'down'}]}
-
-
-def get_fixed_label_dict(fixed_labels):
- fl_dict = {}
- for fl in fixed_labels:
- _l = fl.split('/')
-
- if len(_l) != 3:
- logging.error('Invalid fixed label: %s', fl)
- sys.exit(1)
-
- if not (ishex(_l[0]) and (len(_l[1]) > 0 and len(_l[1]) < 16) and (len(_l[2]) > 0 and len(_l[2]) < 16)):
- logging.error('Invalid fixed label: %s', fl)
- sys.exit(1)
-
- if _l[0] not in fl_dict.keys():
- fl_dict[_l[0]] = list()
-
- fl_dict[_l[0]].append({_l[1]: _l[2]})
-
- return fl_dict
-
# get_supported_modes_dict() converts the list of strings to per endpoint dictionaries.
# example with semantic tags
# input : ['0/label1/1/"1\0x8000, 2\0x8000" 1/label2/1/"1\0x8000, 2\0x8000"']
@@ -373,52 +302,6 @@
if args.hw_ver_str:
FACTORY_DATA['hw-ver-str']['value'] = args.hw_ver_str
- if args.calendar_types:
- FACTORY_DATA['cal-types']['value'] = calendar_types_to_uint32(args.calendar_types)
-
- # Supported locale is stored as multiple entries, key format: "locale/<index>, example key: "locale/0"
- if args.locales:
- FACTORY_DATA['locale-sz']['value'] = len(args.locales)
-
- for i in range(len(args.locales)):
- _locale = {
- 'type': 'data',
- 'encoding': 'string',
- 'value': args.locales[i]
- }
- FACTORY_DATA.update({'locale/{:x}'.format(i): _locale})
-
- # Each endpoint can contains the fixed lables
- # - fl-sz/<index> : number of fixed labels for the endpoint
- # - fl-k/<ep>/<index> : fixed label key for the endpoint and index
- # - fl-v/<ep>/<index> : fixed label value for the endpoint and index
- if args.fixed_labels:
- dict = get_fixed_label_dict(args.fixed_labels)
- for key in dict.keys():
- _sz = {
- 'type': 'data',
- 'encoding': 'u32',
- 'value': len(dict[key])
- }
- FACTORY_DATA.update({'fl-sz/{:x}'.format(int(key)): _sz})
-
- for i in range(len(dict[key])):
- entry = dict[key][i]
-
- _label_key = {
- 'type': 'data',
- 'encoding': 'string',
- 'value': list(entry.keys())[0]
- }
- _label_value = {
- 'type': 'data',
- 'encoding': 'string',
- 'value': list(entry.values())[0]
- }
-
- FACTORY_DATA.update({'fl-k/{:x}/{:x}'.format(int(key), i): _label_key})
- FACTORY_DATA.update({'fl-v/{:x}/{:x}'.format(int(key), i): _label_value})
-
# SupportedModes are stored as multiple entries
# - sm-sz/<ep> : number of supported modes for the endpoint
# - sm-label/<ep>/<index> : supported modes label key for the endpoint and index
@@ -584,13 +467,6 @@
help=('128-bit unique identifier for generating rotating device identifier, '
'provide 32-byte hex string, e.g. "1234567890abcdef1234567890abcdef"'))
- # These will be used by DeviceInfoProvider
- parser.add_argument('--calendar-types', nargs='+',
- help=('List of supported calendar types.\nSupported Calendar Types: Buddhist, Chinese, Coptic, Ethiopian, '
- 'Gregorian, Hebrew, Indian, Islamic, Japanese, Korean, Persian, Taiwanese'))
- parser.add_argument('--locales', nargs='+', help='List of supported locales, Language Tag as defined by BCP47, eg. en-US en-GB')
- parser.add_argument('--fixed-labels', nargs='+',
- help='List of fixed labels, eg: "0/orientation/up" "1/orientation/down" "2/orientation/down"')
parser.add_argument('--supported-modes', type=str, nargs='+', required=False,
help='List of supported modes, eg: mode1/label1/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode2/label2/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode3/label3/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"')
diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn
index b1d3650..167d0eb 100644
--- a/src/platform/ESP32/BUILD.gn
+++ b/src/platform/ESP32/BUILD.gn
@@ -185,6 +185,8 @@
sources += [
"ESP32DeviceInfoProvider.cpp",
"ESP32DeviceInfoProvider.h",
+ "StaticESP32DeviceInfoProvider.cpp",
+ "StaticESP32DeviceInfoProvider.h",
]
}
diff --git a/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp b/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp
new file mode 100644
index 0000000..d65bdf8
--- /dev/null
+++ b/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp
@@ -0,0 +1,122 @@
+/*
+
+ * Copyright (c) 2024 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.
+ */
+#include <lib/support/CodeUtils.h>
+#include <platform/ESP32/StaticESP32DeviceInfoProvider.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+StaticESP32DeviceInfoProvider & StaticESP32DeviceInfoProvider::GetDefaultInstance(void)
+{
+ static StaticESP32DeviceInfoProvider sInstance;
+ return sInstance;
+}
+
+DeviceInfoProvider::FixedLabelIterator * StaticESP32DeviceInfoProvider::IterateFixedLabel(EndpointId endpoint)
+{
+ return chip::Platform::New<StaticFixedLabelIteratorImpl>(endpoint, mFixedLabels);
+}
+
+StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::StaticFixedLabelIteratorImpl(EndpointId endpoint,
+ const Span<FixedLabelEntry> & labels)
+{
+ mEndpoint = endpoint;
+ mLabels = labels;
+ mIndex = 0;
+}
+
+size_t StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Count()
+{
+ size_t count = 0;
+ for (size_t i = 0; i < mLabels.size(); i++)
+ {
+ const FixedLabelEntry & entry = mLabels.data()[i];
+
+ if (entry.endpointId == mEndpoint)
+ {
+ count++;
+ }
+ }
+ return count;
+}
+
+bool StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Next(FixedLabelType & output)
+{
+ ChipLogDetail(DeviceLayer, "Get the fixed label with index:%u at endpoint:%d", static_cast<unsigned>(mIndex), mEndpoint);
+
+ while (mIndex < mLabels.size())
+ {
+ const FixedLabelEntry & entry = mLabels.data()[mIndex++];
+ if (entry.endpointId == mEndpoint)
+ {
+ output.label = entry.label;
+ output.value = entry.value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+DeviceInfoProvider::SupportedLocalesIterator * StaticESP32DeviceInfoProvider::IterateSupportedLocales()
+{
+ return chip::Platform::New<StaticSupportedLocalesIteratorImpl>(mSupportedLocales);
+}
+
+StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::StaticSupportedLocalesIteratorImpl(
+ const Span<CharSpan> & locales)
+{
+ mLocales = locales;
+}
+
+size_t StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Count()
+{
+ return mLocales.empty() ? 0 : mLocales.size();
+}
+
+bool StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Next(CharSpan & output)
+{
+ VerifyOrReturnValue(mIndex < mLocales.size(), false);
+ output = mLocales.data()[mIndex++];
+ return true;
+}
+
+DeviceInfoProvider::SupportedCalendarTypesIterator * StaticESP32DeviceInfoProvider::IterateSupportedCalendarTypes()
+{
+ return chip::Platform::New<StaticSupportedCalendarTypesIteratorImpl>(mSupportedCalendarTypes);
+}
+
+StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::StaticSupportedCalendarTypesIteratorImpl(
+ const Span<CalendarType> & calendarTypes)
+{
+ mCalendarTypes = calendarTypes;
+}
+
+size_t StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Count()
+{
+ return mCalendarTypes.empty() ? 0 : mCalendarTypes.size();
+}
+
+bool StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Next(CalendarType & output)
+{
+ VerifyOrReturnValue(mIndex < mCalendarTypes.size(), false);
+ output = mCalendarTypes.data()[mIndex++];
+ return true;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
diff --git a/src/platform/ESP32/StaticESP32DeviceInfoProvider.h b/src/platform/ESP32/StaticESP32DeviceInfoProvider.h
new file mode 100644
index 0000000..860110c
--- /dev/null
+++ b/src/platform/ESP32/StaticESP32DeviceInfoProvider.h
@@ -0,0 +1,141 @@
+/*
+ *
+ * Copyright (c) 2024 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.
+ */
+#pragma once
+
+#include <platform/ESP32/ESP32DeviceInfoProvider.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+class StaticESP32DeviceInfoProvider : public ESP32DeviceInfoProvider
+{
+public:
+ StaticESP32DeviceInfoProvider() = default;
+ ~StaticESP32DeviceInfoProvider() override {}
+
+ // Iterators
+ FixedLabelIterator * IterateFixedLabel(EndpointId endpoint);
+ SupportedLocalesIterator * IterateSupportedLocales();
+ SupportedCalendarTypesIterator * IterateSupportedCalendarTypes();
+
+ static StaticESP32DeviceInfoProvider & GetDefaultInstance();
+
+ struct FixedLabelEntry
+ {
+ EndpointId endpointId;
+ CharSpan label;
+ CharSpan value;
+ };
+
+ /**
+ * @brief API to set the supported calendar types
+ *
+ * @param[in] supportedCalendarTypes Span of type chip::app::Clusters::TimeFormatLocalization::CalendarTypeEnum
+ * containing the supported calendar types. The underlying data must remain allocated throughout
+ * the lifetime of the device, as the API does not make a copy.
+ *
+ * @return CHIP_ERROR indicating the success or failure of the operation.
+ */
+ CHIP_ERROR SetSupportedCalendarTypes(const Span<CalendarType> & supportedCalendarTypes)
+ {
+ VerifyOrReturnError(!supportedCalendarTypes.empty(), CHIP_ERROR_INVALID_ARGUMENT);
+ mSupportedCalendarTypes = supportedCalendarTypes;
+ return CHIP_NO_ERROR;
+ }
+
+ /**
+ * @brief API to set the supported Locales
+ *
+ * @param[in] supportedLocales Span of type chip::CharSpan containing the supported locales.
+ * The underlying data must remain allocated throughout the lifetime of the device,
+ * as the API does not make a copy.
+ *
+ * @return CHIP_ERROR indicating the success or failure of the operation.
+ */
+ CHIP_ERROR SetSupportedLocales(const Span<CharSpan> & supportedLocales)
+ {
+ VerifyOrReturnError(!supportedLocales.empty(), CHIP_ERROR_INVALID_ARGUMENT);
+ mSupportedLocales = supportedLocales;
+ return CHIP_NO_ERROR;
+ }
+
+ /**
+ * @brief API to set the fixed labels
+ *
+ * @param[in] fixedLabels Span of type chip::DeviceLayer::StaticESP32DeviceInfoProvider::FixedLabelEntry
+ * containing the fixed labels for supported endpoints.
+ * The underlying data must remain allocated throughout the lifetime of the device,
+ * as the API does not make a copy.
+ *
+ * @return CHIP_ERROR indicating the success or failure of the operation.
+ */
+ CHIP_ERROR SetFixedLabels(const Span<FixedLabelEntry> & supportedFixedLabels)
+ {
+ VerifyOrReturnError(!supportedFixedLabels.empty(), CHIP_ERROR_INVALID_ARGUMENT);
+ mFixedLabels = supportedFixedLabels;
+ return CHIP_NO_ERROR;
+ }
+
+protected:
+ class StaticFixedLabelIteratorImpl : public FixedLabelIterator
+ {
+ public:
+ StaticFixedLabelIteratorImpl(EndpointId endpoint, const Span<FixedLabelEntry> & labels);
+ size_t Count();
+ bool Next(FixedLabelType & output);
+ void Release() { chip::Platform::Delete(this); }
+
+ private:
+ EndpointId mEndpoint = 0;
+ size_t mIndex = 0;
+ Span<FixedLabelEntry> mLabels;
+ };
+
+ class StaticSupportedLocalesIteratorImpl : public SupportedLocalesIterator
+ {
+ public:
+ StaticSupportedLocalesIteratorImpl(const Span<CharSpan> & locales);
+ size_t Count();
+ bool Next(CharSpan & output);
+ void Release() { chip::Platform::Delete(this); }
+
+ private:
+ size_t mIndex = 0;
+ Span<CharSpan> mLocales;
+ };
+
+ class StaticSupportedCalendarTypesIteratorImpl : public SupportedCalendarTypesIterator
+ {
+ public:
+ StaticSupportedCalendarTypesIteratorImpl(const Span<CalendarType> & calendarTypes);
+ size_t Count();
+ bool Next(CalendarType & output);
+ void Release() { chip::Platform::Delete(this); }
+
+ private:
+ size_t mIndex = 0;
+ Span<CalendarType> mCalendarTypes;
+ };
+
+private:
+ Span<CalendarType> mSupportedCalendarTypes;
+ Span<CharSpan> mSupportedLocales;
+ Span<FixedLabelEntry> mFixedLabels;
+};
+
+} // namespace DeviceLayer
+} // namespace chip