Skip to content

Commit

Permalink
[nrfconnect] Added the Device Appearance attribute to factory data
Browse files Browse the repository at this point in the history
The appearance attribute of the Basic Information cluster has been
added to the Factory Data set.
That field allows for defining the visible finish of the product's
external case finish method, and color hue.

- The attribute has been added to the factory data provider
and the Factory Data Parser.

- The appearance field has been described in the factory data
guide.
  • Loading branch information
ArekBalysNordic committed May 10, 2023
1 parent ee81b19 commit 1708370
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 27 deletions.
92 changes: 66 additions & 26 deletions docs/guides/nrfconnect_factory_data_configuration.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions scripts/tools/nrfconnect/generate_nrfconnect_chip_factory_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
PUB_KEY_PREFIX = b'\x04'
INVALID_PASSCODES = [00000000, 11111111, 22222222, 33333333, 44444444,
55555555, 66666666, 77777777, 88888888, 99999999, 12345678, 87654321]
PRODUCT_FINISH_ENUM = {"other": 0, "matte": 1, "satin": 2, "polished": 3, "rugged": 4, "fabric": 5}
PRODUCT_COLOR_ENUM = {"black": 0, "navy": 1, "green": 2, "teal": 3, "maroon": 4, "purple": 5, "olive": 6, "gray": 7, "blue": 8, "lime": 9,
"aqua": 10, "red": 11, "fuchsia": 12, "yellow": 13, "white": 14, "nickel": 15, "chrome": 16, "brass": 18, "cooper": 19,
"silver": 19, "gold": 20}

sys.path.insert(0, os.path.join(MATTER_ROOT, 'scripts', 'tools', 'spake2p'))
from spake2p import generate_verifier # noqa: E402 isort:skip
Expand Down Expand Up @@ -325,6 +329,9 @@ def generate_json(self):
self._add_entry("rd_uid", rd_uid)
if self._args.enable_key:
self._add_entry("enable_key", HEX_PREFIX + self._args.enable_key)
if self._args.product_finish and self._args.product_color:
self._add_entry("product_appearance", {"finish": PRODUCT_FINISH_ENUM[self._args.product_finish],
"primary_color": PRODUCT_COLOR_ENUM[self._args.product_color]})
if self._args.user:
self._add_entry("user", self._user_data)

Expand Down Expand Up @@ -516,6 +523,10 @@ def base64_str(s): return base64.b64decode(s)
optional_arguments.add_argument("--generate_onboarding", action="store_true",
help=("Generate a Manual Code and QR Code according to provided factory data set."
"As a result a PNG image containing QRCode and a .txt file containing Manual Code will be available within output directory"))
optional_arguments.add_argument("--product_finish", type=str, choices=PRODUCT_FINISH_ENUM.keys(),
help="[string] Provide one of the product finishes")
optional_arguments.add_argument("--product_color", type=str, choices=PRODUCT_COLOR_ENUM.keys(),
help="[string] Provide one of the product colors.")
args = parser.parse_args()

if args.verbose:
Expand Down
14 changes: 14 additions & 0 deletions scripts/tools/nrfconnect/nrfconnect_factory_data.schema
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,20 @@
"minLength": 36,
"maxLength": 36
},
"product_appearance": {
"description": "Product appearance provides a description of the product's external case finish method (matte, satin, polished, rugged, fabric) and product's primary color",
"type": "object",
"properties": {
"finish": {
"type": "integer",
"enum": [0, 1, 2, 3, 4, 5]
},
"primary_color": {
"type": "integer",
"enum": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
}
}
},
"user": {
"description": "A user-specific additional data which should be added to factory data. This should be a Json format.",
"type": "object"
Expand Down
5 changes: 5 additions & 0 deletions scripts/tools/nrfconnect/tests/test_generate_factory_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ def test_generate_factory_data_all_specified(self):
'--discriminator', '0xFED',
'--rd_uid', '91a9c12a7c80700a31ddcfa7fce63e44',
'--enable_key', '00112233445566778899aabbccddeeff',
'--product_color', 'red',
'--product_finish', 'satin',
'--user', '{"name": "product_name", "version": 123, "revision": "0x123"}',
'-o', os.path.join(outdir, 'fd.json')
])
Expand Down Expand Up @@ -207,6 +209,7 @@ def test_generate_factory_data_all_specified(self):
self.assertEqual(factory_data.get('passcode'), 13243546)
self.assertEqual(factory_data.get('rd_uid'), 'hex:91a9c12a7c80700a31ddcfa7fce63e44')
self.assertEqual(factory_data.get('enable_key'), 'hex:00112233445566778899aabbccddeeff')
self.assertEqual(factory_data.get('product_appearance'), {'finish': 2, 'primary_color': 11})
self.assertEqual(factory_data.get('user'), {'name': 'product_name', 'version': 123, 'revision': '0x123'})

subprocess.check_call(['python3', os.path.join(TOOLS_DIR, 'nrfconnect_generate_partition.py'),
Expand Down Expand Up @@ -240,6 +243,8 @@ def test_generate_spake2p_verifier_default(self):
'--spake2_salt', 'U1BBS0UyUCBLZXkgU2FsdA==',
'--passcode', '20202021',
'--discriminator', '0xFED',
'--product_color', 'red',
'--product_finish', 'satin',
'--user', '{"name": "product_name", "version": 123, "revision": "0x123"}',
'-o', os.path.join(outdir, 'fd.json')
])
Expand Down
35 changes: 34 additions & 1 deletion src/platform/nrfconnect/FactoryDataParser.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <ctype.h>
#include <string.h>

#define MAX_FACTORY_DATA_NESTING_LEVEL 3
#define MAX_FACTORY_DATA_NESTING_LEVEL 4

static inline bool uint16_decode(zcbor_state_t * states, uint16_t * value)
{
Expand All @@ -37,6 +37,19 @@ static inline bool uint16_decode(zcbor_state_t * states, uint16_t * value)
return false;
}

static inline bool uint8_decode(zcbor_state_t * states, uint8_t * value)
{
uint32_t u32;

if (zcbor_uint32_decode(states, &u32))
{
*value = (uint8_t) u32;
return true;
}

return false;
}

static bool DecodeEntry(zcbor_state_t * states, void * buffer, size_t bufferSize, size_t * outlen)
{
struct zcbor_string tempString;
Expand Down Expand Up @@ -233,6 +246,26 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData
{
res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->enable_key);
}
else if (strncmp("product_appearance", (const char *) currentString.value, currentString.len) == 0)
{
res = res && zcbor_map_start_decode(states);
struct zcbor_string appearanceString;
while (res)
{
res = zcbor_tstr_decode(states, &appearanceString);
if (strncmp("finish", (const char *) appearanceString.value, appearanceString.len) == 0)
{
res = res && uint8_decode(states, &factoryData->product_appearance.finish);
}
else if (strncmp("primary_color", (const char *) appearanceString.value, appearanceString.len) == 0)
{
res = res && uint8_decode(states, &factoryData->product_appearance.primary_color);
break;
}
}
res = res && zcbor_map_end_decode(states);
factoryData->appearancePresent = res;
}
else if (strncmp("user", (const char *) currentString.value, currentString.len) == 0)
{
factoryData->user.data = (void *) states->payload;
Expand Down
6 changes: 6 additions & 0 deletions src/platform/nrfconnect/FactoryDataParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,17 @@ struct FactoryData
uint32_t passcode;
struct FactoryDataString enable_key;
struct FactoryDataString user;
struct ProductAppearance
{
uint8_t finish;
uint8_t primary_color;
} product_appearance;

bool vendorIdPresent;
bool productIdPresent;
bool hwVerPresent;
bool discriminatorPresent;
bool appearancePresent;
};

/**
Expand Down
22 changes: 22 additions & 0 deletions src/platform/nrfconnect/FactoryDataProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,28 @@ CHIP_ERROR FactoryDataProvider<FlashFactoryData>::GetEnableKey(MutableByteSpan &
return CHIP_NO_ERROR;
}

template <class FlashFactoryData>
CHIP_ERROR FactoryDataProvider<FlashFactoryData>::GetProductFinish(app::Clusters::BasicInformation::ProductFinishEnum * finish)
{
ReturnErrorCodeIf(!finish, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorCodeIf(!mFactoryData.appearancePresent, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);

*finish = static_cast<app::Clusters::BasicInformation::ProductFinishEnum>(mFactoryData.product_appearance.finish);

return CHIP_NO_ERROR;
}

template <class FlashFactoryData>
CHIP_ERROR FactoryDataProvider<FlashFactoryData>::GetProductPrimaryColor(app::Clusters::BasicInformation::ColorEnum * primaryColor)
{
ReturnErrorCodeIf(!primaryColor, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorCodeIf(!mFactoryData.appearancePresent, CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND);

*primaryColor = static_cast<app::Clusters::BasicInformation::ColorEnum>(mFactoryData.product_appearance.primary_color);

return CHIP_NO_ERROR;
}

template <class FlashFactoryData>
CHIP_ERROR FactoryDataProvider<FlashFactoryData>::GetUserData(MutableByteSpan & userData)
{
Expand Down
2 changes: 2 additions & 0 deletions src/platform/nrfconnect/FactoryDataProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ class FactoryDataProvider : public chip::Credentials::DeviceAttestationCredentia
CHIP_ERROR GetHardwareVersion(uint16_t & hardwareVersion) override;
CHIP_ERROR GetHardwareVersionString(char * buf, size_t bufSize) override;
CHIP_ERROR GetRotatingDeviceIdUniqueId(MutableByteSpan & uniqueIdSpan) override;
CHIP_ERROR GetProductFinish(app::Clusters::BasicInformation::ProductFinishEnum * finish) override;
CHIP_ERROR GetProductPrimaryColor(app::Clusters::BasicInformation::ColorEnum * primaryColor) override;

// ===== Members functions that are platform-specific
CHIP_ERROR GetEnableKey(MutableByteSpan & enableKey);
Expand Down

0 comments on commit 1708370

Please sign in to comment.