Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal for dual 'stabilised' recognition change for XMTZC04HM - WIP #71

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
'devices/BM_V23',
'devices/CGD1',
'devices/CGP1W',
'devices/GAEN',
'devices/H5072',
'devices/H5075',
'devices/H5102',
Expand Down
12 changes: 12 additions & 0 deletions docs/devices/GAEN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Google/Apple Exposure Notification

|Model Id|[MS-CDP](https://github.com/theengs/decoder/blob/development/src/devices/GAEN_json.h)|
|-|-|
|Brand|Generic|
|Model|GAEN|
|Short Description|Google/Apple Exposure Notification (GAEN) for digital contact tracing|
|Communication|BLE broadcast|
|Frequency|2.4Ghz|
|Power source|Dependent on device|
|Exchanged data|rolling proximity identifier, associated encrypted metadata|
|Encrypted|Yes|
8 changes: 4 additions & 4 deletions docs/devices/XMTZC04HM.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Xiaomi MiScale V1
# Xiaomi Mi Smart Scale 2

|Model Id|[XMTZC04HM](https://github.com/theengs/decoder/blob/development/src/devices/XMTZC04HM_json.h)|
|-|-|
|Brand|Xiaomi|
|Model|MiScale V1|
|Short Description|First version of the scale|
|Model|Mi Smart Scale 2|
|Short Description|Second version of the Mi Smart Scale|
|Communication|BLE broadcast|
|Frequency|2.4Ghz|
|Power source|3 AAA|
|Exchanged data|unit, weight|
|Exchanged data|weighing_mode, unit, weight|
|Encrypted|No|
10 changes: 5 additions & 5 deletions docs/devices/XMTZC05HM.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Xiaomi MiScale V2
# Xiaomi Mi Body Composition Scale 2

|Model Id|[XMTZC05HM](https://github.com/theengs/decoder/blob/development/src/devices/XMTZC05HM_json.h)|
|-|-|
|Brand|Xiaomi|
|Model|MiScale V2|
|Short Description|Second version of the scale|
|Model|Mi Body Scale 2|
|Short Description|Second version of the Mi Body Composition Scale|
|Communication|BLE broadcast|
|Frequency|2.4Ghz|
|Power source|3 AAA|
|Exchanged data|unit, weight, impedance|
|Power source|4 AAA|
|Exchanged data|weighing_mode, unit, weight, impedance|
|Encrypted|No|
261 changes: 139 additions & 122 deletions src/decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,145 +281,162 @@ int TheengsDecoder::decodeBLEJson(JsonObject& jsondata) {
JsonObject prop = kv.value().as<JsonObject>();
JsonArray prop_condition = prop["condition"];

if (prop_condition.isNull() || strstr((const char*)prop_condition[0], "servicedata") != nullptr ||
strstr((const char*)prop_condition[0], "manufacturerdata") != nullptr) {
if (prop_condition.isNull() ||
(svc_data && svc_data[prop_condition[1].as<int>()] == *prop_condition[2].as<const char*>()) ||
(mfg_data && mfg_data[prop_condition[1].as<int>()] == *prop_condition[2].as<const char*>())) {
JsonArray decoder = prop["decoder"];
if (strstr((const char*)decoder[0], "value_from_hex_data") != nullptr) {
const char* src = svc_data;
if (strstr((const char*)decoder[1], "manufacturerdata")) {
src = mfg_data;
}
int cond_size = prop_condition.size();
bool cond_met = prop_condition.isNull();

for (int i = 0; i < cond_size; i += 4) {
if (svc_data &&
strstr((const char*)prop_condition[i], "servicedata") != nullptr &&
svc_data[prop_condition[i + 1].as<int>()] == *prop_condition[i + 2].as<const char*>()) {
cond_met = true;
} else if (mfg_data &&
strstr((const char*)prop_condition[i], "manufacturerdata") != nullptr &&
mfg_data[prop_condition[i + 1].as<int>()] == *prop_condition[i + 2].as<const char*>()) {
cond_met = true;
}

/* use a double for all values and cast later if required */
double temp_val;
static long cal_val = 0;
if (!cond_met && cond_size > (i + 3) && *prop_condition[i + 3].as<const char*>() == '|') {
continue;
} else if (cond_met && cond_size > (i + 3) && *prop_condition[i + 3].as<const char*>() == '&') {
cond_met = false;
continue;
}
}

if (data_index_is_valid(src, decoder[2].as<int>(), decoder[3].as<int>())) {
decoder_function dec_fun = &TheengsDecoder::value_from_hex_string;
if (cond_met) {
JsonArray decoder = prop["decoder"];
if (strstr((const char*)decoder[0], "value_from_hex_data") != nullptr) {
const char* src = svc_data;
if (strstr((const char*)decoder[1], "manufacturerdata")) {
src = mfg_data;
}

if (strstr((const char*)decoder[0], "bf") != nullptr) {
dec_fun = &TheengsDecoder::bf_value_from_hex_string;
}
/* use a double for all values and cast later if required */
double temp_val;
static long cal_val = 0;

temp_val = (this->*dec_fun)(src, decoder[2].as<int>(),
decoder[3].as<int>(),
decoder[4].as<bool>(),
decoder[5].isNull() ? true : decoder[5].as<bool>());
if (data_index_is_valid(src, decoder[2].as<int>(), decoder[3].as<int>())) {
decoder_function dec_fun = &TheengsDecoder::value_from_hex_string;

} else {
break;
if (strstr((const char*)decoder[0], "bf") != nullptr) {
dec_fun = &TheengsDecoder::bf_value_from_hex_string;
}

/* Do any required post processing of the value */
if (prop.containsKey("post_proc")) {
JsonArray post_proc = prop["post_proc"];
for (unsigned int i = 0; i < post_proc.size(); i += 2) {
if (cal_val && post_proc[i + 1].as<const char*>() != NULL &&
strncmp(post_proc[i + 1].as<const char*>(), ".cal", 4) == 0) {
switch (*post_proc[i].as<const char*>()) {
case '/':
temp_val /= cal_val;
break;
case '*':
temp_val *= cal_val;
break;
case '-':
temp_val -= cal_val;
break;
case '+':
temp_val += cal_val;
break;
temp_val = (this->*dec_fun)(src, decoder[2].as<int>(),
decoder[3].as<int>(),
decoder[4].as<bool>(),
decoder[5].isNull() ? true : decoder[5].as<bool>());

} else {
break;
}

/* Do any required post processing of the value */
if (prop.containsKey("post_proc")) {
JsonArray post_proc = prop["post_proc"];
for (unsigned int i = 0; i < post_proc.size(); i += 2) {
if (cal_val && post_proc[i + 1].as<const char*>() != NULL &&
strncmp(post_proc[i + 1].as<const char*>(), ".cal", 4) == 0) {
switch (*post_proc[i].as<const char*>()) {
case '/':
temp_val /= cal_val;
break;
case '*':
temp_val *= cal_val;
break;
case '-':
temp_val -= cal_val;
break;
case '+':
temp_val += cal_val;
break;
}
} else {
switch (*post_proc[i].as<const char*>()) {
case '/':
temp_val /= post_proc[i + 1].as<double>();
break;
case '*':
temp_val *= post_proc[i + 1].as<double>();
break;
case '-':
temp_val -= post_proc[i + 1].as<double>();
break;
case '+':
temp_val += post_proc[i + 1].as<double>();
break;
case '%': {
long val = (long)temp_val;
temp_val = val % post_proc[i + 1].as<long>();
break;
}
case '<': {
long val = (long)temp_val;
temp_val = val << post_proc[i + 1].as<unsigned int>();
break;
}
case '>': {
long val = (long)temp_val;
temp_val = val >> post_proc[i + 1].as<unsigned int>();
break;
}
} else {
switch (*post_proc[i].as<const char*>()) {
case '/':
temp_val /= post_proc[i + 1].as<double>();
break;
case '*':
temp_val *= post_proc[i + 1].as<double>();
break;
case '-':
temp_val -= post_proc[i + 1].as<double>();
break;
case '+':
temp_val += post_proc[i + 1].as<double>();
break;
case '%': {
long val = (long)temp_val;
temp_val = val % post_proc[i + 1].as<long>();
break;
}
case '<': {
long val = (long)temp_val;
temp_val = val << post_proc[i + 1].as<unsigned int>();
break;
}
case '>': {
long val = (long)temp_val;
temp_val = val >> post_proc[i + 1].as<unsigned int>();
break;
}
case '!': {
bool val = (bool)temp_val;
temp_val = !val;
break;
}
case '&': {
long val = (long)temp_val;
temp_val = val & post_proc[i + 1].as<unsigned int>();
break;
}
case '!': {
bool val = (bool)temp_val;
temp_val = !val;
break;
}
case '&': {
long val = (long)temp_val;
temp_val = val & post_proc[i + 1].as<unsigned int>();
break;
}
}
}
}
}

/* If there is any underscores at the beginning of the property name, there is multiple
* properties of this type, we need remove the underscores for creating the key.
*/
std::string _key = sanitizeJsonKey(kv.key().c_str());

/* calculation values extracted from data are not added to the deocded outupt
* instead we store them teporarily to use with the next data properties.
*/
if (_key == ".cal") {
cal_val = (long)temp_val;
continue;
}

/* Cast to a differnt value type if specified */
if (prop.containsKey("is_bool")) {
jsondata[_key] = (bool)temp_val;
} else {
jsondata[_key] = temp_val;
}
/* If there is any underscores at the beginning of the property name, there is multiple
* properties of this type, we need remove the underscores for creating the key.
*/
std::string _key = sanitizeJsonKey(kv.key().c_str());

/* calculation values extracted from data are not added to the deocded outupt
* instead we store them teporarily to use with the next data properties.
*/
if (_key == ".cal") {
cal_val = (long)temp_val;
continue;
}

/* If the property is temp in C, make sure to convert and add temp in F */
if (_key.find("tempc", 0, 5) != std::string::npos) {
double tc = jsondata[_key];
_key[4] = 'f';
jsondata[_key] = tc * 1.8 + 32;
_key[4] = 'c';
}
/* Cast to a differnt value type if specified */
if (prop.containsKey("is_bool")) {
jsondata[_key] = (bool)temp_val;
} else {
jsondata[_key] = temp_val;
}

success = i_main;
DEBUG_PRINT("found value = %s : %.2f\n", _key.c_str(), jsondata[_key].as<double>());
} else if (strstr((const char*)decoder[0], "static_value") != nullptr) {
jsondata[sanitizeJsonKey(kv.key().c_str())] = decoder[1];
success = i_main;
} else if (strstr((const char*)decoder[0], "string_from_hex_data") != nullptr) {
const char* src = svc_data;
if (strstr((const char*)decoder[1], "manufacturerdata")) {
src = mfg_data;
}
/* If the property is temp in C, make sure to convert and add temp in F */
if (_key.find("tempc", 0, 5) != std::string::npos) {
double tc = jsondata[_key];
_key[4] = 'f';
jsondata[_key] = tc * 1.8 + 32;
_key[4] = 'c';
}

std::string value(src + decoder[2].as<int>(), decoder[3].as<int>());
jsondata[sanitizeJsonKey(kv.key().c_str())] = value;
success = i_main;
success = i_main;
DEBUG_PRINT("found value = %s : %.2f\n", _key.c_str(), jsondata[_key].as<double>());
} else if (strstr((const char*)decoder[0], "static_value") != nullptr) {
jsondata[sanitizeJsonKey(kv.key().c_str())] = decoder[1];
success = i_main;
} else if (strstr((const char*)decoder[0], "string_from_hex_data") != nullptr) {
const char* src = svc_data;
if (strstr((const char*)decoder[1], "manufacturerdata")) {
src = mfg_data;
}

std::string value(src + decoder[2].as<int>(), decoder[3].as<int>());
jsondata[sanitizeJsonKey(kv.key().c_str())] = value;
success = i_main;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class TheengsDecoder {
RUUVITAG_RAWV2,
BM_V23,
MS_CDP,
GAEN,
BLE_ID_MAX
};

Expand Down
2 changes: 2 additions & 0 deletions src/devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "devices/CGH1_json.h"
#include "devices/CGP1W_json.h"
#include "devices/CGPR1_json.h"
#include "devices/GAEN_json.h"
#include "devices/H5072_json.h"
#include "devices/H5075_json.h"
#include "devices/H5102_json.h"
Expand Down Expand Up @@ -94,4 +95,5 @@ const char* _devices[][2] = {
{_RuuviTag_RAWv2_json, _RuuviTag_RAWv2_json_props},
{_BM_V23_json, _BM_V23_json_props},
{_MS_CDP_json, _MS_CDP_json_props},
{_GAEN_json, _GAEN_json_props},
};
32 changes: 32 additions & 0 deletions src/devices/GAEN_json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const char* _GAEN_json = "{\"brand\":\"GENERIC\",\"model\":\"GAEN\",\"model_id\":\"GAEN\",\"condition\":[\"uuid\",\"index\",0,\"fd6f\"],\"properties\":{\"rpi\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",0,32]},\"aem\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",32,8]}}}";

/*R""""(
{
"brand":"GENERIC",
"model":"GAEN",
"model_id":"GAEN",
"condition":["uuid", "index", 0, "fd6f"],
"properties":{
"rpi":{
"decoder":["string_from_hex_data", "servicedata", 0, 32]
},
"aem":{
"decoder":["string_from_hex_data", "servicedata", 32, 8]
}
}
})"""";*/

const char* _GAEN_json_props = "{\"properties\":{\"rpi\":{\"unit\":\"hex\",\"name\":\"rolling proximity identifier\"},\"aem\":{\"unit\":\"hex\",\"name\":\"associated encrypted metadata\"}}}";
/*R""""(
{
"properties":{
"rpi":{
"unit":"hex",
"name":"rolling proximity identifier"
},
"aem":{
"unit":"hex",
"name":"associated encrypted metadata"
}
}
})"""";*/
Loading