Skip to content
Open
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
83 changes: 81 additions & 2 deletions src/decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,48 @@ double TheengsDecoder::value_from_hex_string(const char* data_str,
return value;
}

/*
* @brief Extracts bit-aligned values from a hex string.
* Bit 0 is the least significant bit of the first byte.
*/
double TheengsDecoder::value_from_bit_string(const char* data_str,
int bit_offset, int bit_length,
bool canBeNegative) {
if (bit_offset < 0 || bit_length <= 0 || bit_length > 63) {
return 0;
}

size_t data_chars = strlen(data_str);
size_t required_chars = ((size_t)(bit_offset + bit_length + 7) / 8) * 2;
if (data_chars < required_chars) {
return 0;
}

unsigned long long value = 0;
for (int i = 0; i < bit_length; ++i) {
int abs_bit = bit_offset + i;
size_t byte_idx = (size_t)abs_bit / 8;
uint8_t bit_idx = (uint8_t)(abs_bit % 8);
size_t hex_idx = byte_idx * 2;

uint8_t byte = (uint8_t)((getBinaryData(data_str[hex_idx]) << 4) |
getBinaryData(data_str[hex_idx + 1]));
if ((byte >> bit_idx) & 0x01) {
value |= (1ULL << i);
}
}

long long signed_value = (long long)value;
if (canBeNegative) {
unsigned long long sign_bit = (1ULL << (bit_length - 1));
if (value & sign_bit) {
signed_value -= (long long)(1ULL << bit_length);
}
}

return (double)signed_value;
}

/*
* @brief Removes the underscores at the beginning of key strings
* when duplicate properties exist in a device.
Expand Down Expand Up @@ -695,7 +737,8 @@ int TheengsDecoder::decodeBLEJson(JsonObject& jsondata) {

if (checkPropCondition(prop["condition"], svc_data, mfg_data, dev_name)) {
JsonArray decoder = prop["decoder"];
if (strstr((const char*)decoder[0], "value_from_hex_data") != nullptr) {
if (strstr((const char*)decoder[0], "value_from_hex_data") != nullptr ||
strstr((const char*)decoder[0], "value_from_bit_data") != nullptr) {
const char* src = svc_data;
if (strstr((const char*)decoder[1], MFG_DATA)) {
src = mfg_data;
Expand All @@ -706,7 +749,21 @@ int TheengsDecoder::decodeBLEJson(JsonObject& jsondata) {
static double cal_val = 0;
std::string proc_str = "";

if (data_index_is_valid(src, decoder[2].as<int>(), decoder[3].as<int>())) {
if (strstr((const char*)decoder[0], "value_from_bit_data") != nullptr) {
int bit_offset = decoder[2].as<int>();
int bit_length = decoder[3].as<int>();
size_t required_chars = ((size_t)(bit_offset + bit_length + 7) / 8) * 2;

if (data_index_is_valid(src, 0, required_chars)) {
temp_val = value_from_bit_string(
src,
bit_offset,
bit_length,
decoder[4].isNull() ? true : decoder[4].as<bool>());
} else {
break;
}
} else if (data_index_is_valid(src, decoder[2].as<int>(), decoder[3].as<int>())) {
decoder_function dec_fun = &TheengsDecoder::value_from_hex_string;

if (strstr((const char*)decoder[0], "bf") != nullptr) {
Expand Down Expand Up @@ -819,6 +876,28 @@ int TheengsDecoder::decodeBLEJson(JsonObject& jsondata) {
}
}

/* Optional enum-style lookup for numeric decoders. */
if (prop.containsKey("lookup")) {
JsonArray lookup = prop["lookup"];
for (unsigned int i = 0; i + 1 < lookup.size(); i += 2) {
long long lookup_key = LLONG_MIN;

if (lookup[i].is<const char*>()) {
const char* key_str = lookup[i].as<const char*>();
if (key_str != nullptr) {
lookup_key = strtoll(key_str, NULL, 16);
}
}

if (lookup_key != LLONG_MIN &&
(long long)temp_val == lookup_key &&
lookup[i + 1].is<const char*>()) {
proc_str = lookup[i + 1].as<const char*>();
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.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class TheengsDecoder {
UT363BT,
VICTRON_ENCR,
VICTBSC,
VICTORIONTR,
VICTORIONXS,
VICTSBP,
VICTSBS,
Expand All @@ -202,6 +203,7 @@ class TheengsDecoder {
private:
void reverse_hex_data(const char* in, char* out, int l);
double value_from_hex_string(const char* data_str, int offset, int data_length, bool reverse, bool canBeNegative = true, bool isFloat = false);
double value_from_bit_string(const char* data_str, int bit_offset, int bit_length, bool canBeNegative = true);
double bf_value_from_hex_string(const char* data_str, int offset, int data_length, bool reverse, bool canBeNegative = true, bool isFloat = false);
bool data_index_is_valid(const char* str, size_t index, size_t len);
bool data_length_is_valid(size_t data_len, size_t default_min, const JsonArray& condition, int *idx);
Expand Down
2 changes: 2 additions & 0 deletions src/devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
#include "devices/UT363BT_json.h"
#include "devices/VICTRON__ENCR_json.h"
#include "devices/VICTRON_BSC_json.h"
#include "devices/VICTRON_ORIONTR_json.h"
#include "devices/VICTRON_ORIONXS_json.h"
#include "devices/VICTRON_SBP_json.h"
#include "devices/VICTRON_SBS_json.h"
Expand Down Expand Up @@ -276,6 +277,7 @@ const char* _devices[][2] = {
{_UT363BT_json, _UT363BT_json_props},
{_VICTRON_ENCR_json, _VICTRON_ENCR_json_props},
{_VICTBSC_json, _VICTBSC_json_props},
{_VICTORIONTR_json, _VICTORIONTR_json_props},
{_VICTORIONXS_json, _VICTORIONXS_json_props},
{_VICTSBP_json, _VICTSBP_json_props},
{_VICTSBS_json, _VICTSBS_json_props},
Expand Down
83 changes: 83 additions & 0 deletions src/devices/VICTRON_BAT_MON_json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const char* _VICTRON_BAT_MON_json = "{\"brand\":\"Victron Energy\",\"model\":\"Bat Mon\",\"model_id\":\"VICTRON_BAT_MON\",\"tag\":\"0c48\",\"condition\":[\"manufacturerdata\",\"=\",50,\"index\",0,\"e10211\",\"&\",\"manufacturerdata\",\"index\",8,\"83a3\",\"&\",\"manufacturerdata\",\"index\",12,\"02ffff\"],\"properties\":{\"ttg\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",80,16,false]},\"volt\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",96,16,true],\"post_proc\":[\"/\",100]},\"alarm_reason\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",112,16,false]},\"aux_voltage\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",128,16,true],\"post_proc\":[\"/\",100]},\"aux_input\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",144,2,false],\"lookup\":[\"00\",\"auxiliary voltage\",\"01\",\"mid-point voltage\",\"02\",\"battery temperature\",\"03\",\"none\"]},\"current\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",146,22,true],\"post_proc\":[\"/\",1000]},\"consumed_ah\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",168,20,true],\"post_proc\":[\"/\",-10]},\"soc\":{\"decoder\":[\"value_from_bit_data\",\"manufacturerdata\",188,10,false],\"post_proc\":[\"/\",10]}}}";
/*R""""(
{
"brand":"Victron Energy",
"model":"Bat Mon",
"model_id":"VICTRON_BAT_MON",
"tag":"0c48",
"condition":["manufacturerdata", "=", 50, "index", 0, "e10211", "&", "manufacturerdata", "index", 8, "83a3", "&", "manufacturerdata", "index", 12, "02ffff"],
"properties":{
"ttg":{
"decoder":["value_from_bit_data", "manufacturerdata", 80, 16, false]
},
"volt":{
"decoder":["value_from_bit_data", "manufacturerdata", 96, 16, true],
"post_proc":["/", 100]
},
"alarm_reason":{
"decoder":["value_from_bit_data", "manufacturerdata", 112, 16, false]
},
"aux_voltage":{
"decoder":["value_from_bit_data", "manufacturerdata", 128, 16, true],
"post_proc":["/", 100]
},
"aux_input":{
"decoder":["value_from_bit_data", "manufacturerdata", 144, 2, false],
"lookup":["00", "auxiliary voltage",
"01", "mid-point voltage",
"02", "battery temperature",
"03", "none"]
},
"current":{
"decoder":["value_from_bit_data", "manufacturerdata", 146, 22, true],
"post_proc":["/", 1000]
},
"consumed_ah":{
"decoder":["value_from_bit_data", "manufacturerdata", 168, 20, true],
"post_proc":["/", -10]
},
"soc":{
"decoder":["value_from_bit_data", "manufacturerdata", 188, 10, false],
"post_proc":["/", 10]
}
}
})"""";*/

const char* _VICTRON_BAT_MON_json_props = "{\"properties\":{\"ttg\":{\"unit\":\"min\",\"name\":\"time to go\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"alarm_reason\":{\"unit\":\"int\",\"name\":\"alarm reason\"},\"aux_voltage\":{\"unit\":\"V\",\"name\":\"auxiliary voltage\"},\"aux_input\":{\"unit\":\"string\",\"name\":\"aux input\"},\"current\":{\"unit\":\"A\",\"name\":\"current\"},\"consumed_ah\":{\"unit\":\"Ah\",\"name\":\"consumed ampere hours\"},\"soc\":{\"unit\":\"%\",\"name\":\"state of charge\"}}}";
/*R""""(
{
"properties":{
"ttg":{
"unit":"min",
"name":"time to go"
},
"volt":{
"unit":"V",
"name":"voltage"
},
"alarm_reason":{
"unit":"int",
"name":"alarm reason"
},
"aux_voltage":{
"unit":"V",
"name":"auxiliary voltage"
},
"aux_input":{
"unit":"string",
"name":"aux input"
},
"current":{
"unit":"A",
"name":"current"
},
"consumed_ah":{
"unit":"Ah",
"name":"consumed ampere hours"
},
"soc":{
"unit":"%",
"name":"state of charge"
}
}
})"""";*/
86 changes: 86 additions & 0 deletions src/devices/VICTRON_ORIONTR_json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const char* _VICTORIONTR_json = "{\"brand\":\"Victron Energy\",\"model\":\"Orion TR\",\"model_id\":\"VICTORIONTR\",\"tag\":\"0c48\",\"condition\":[\"manufacturerdata\",\"=\",40,\"index\",0,\"e10211\",\"&\",\"manufacturerdata\",\"index\",8,\"d0a3\",\"&\",\"manufacturerdata\",\"index\",12,\"04ffff\"],\"properties\":{\"device_state\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",20,2],\"lookup\":[\"00\",\"off\",\"01\",\"low power\",\"02\",\"fault\",\"03\",\"bulk\",\"04\",\"absorption\",\"05\",\"float\",\"06\",\"storage\",\"07\",\"equalize manual\",\"09\",\"inverting\",\"0b\",\"power_supply\",\"f5\",\"starting up\",\"f6\",\"repeated absorption\",\"f7\",\"recondition\",\"f8\",\"battery safe\",\"f9\",\"active\",\"fc\",\"external control\",\"ff\",\"N/A\"]},\"volt_in\":{\"condition\":[\"manufacturerdata\",24,\"!\",\"ff7f\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,true,true],\"post_proc\":[\"/\",100]},\"current_out\":{\"condition\":[\"manufacturerdata\",28,\"!\",\"ff7f\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,true,true],\"post_proc\":[\"/\",10]},\"volt_out\":{\"condition\":[\"manufacturerdata\",32,\"!\",\"ffff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,4,true,false],\"post_proc\":[\"/\",100]},\"current_in\":{\"condition\":[\"manufacturerdata\",36,\"!\",\"ffff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",36,4,true,false],\"post_proc\":[\"/\",10]},\"error_code\":{\"condition\":[\"manufacturerdata\",22,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",22,2]}}}";
/*R""""(
{
"brand":"Victron Energy",
"model":"Orion TR",
"model_id":"VICTORIONTR",
"tag":"0c48",
"condition":["manufacturerdata", "=", 40, "index", 0, "e10211", "&", "manufacturerdata", "index", 8, "d0a3", "&", "manufacturerdata", "index", 12, "04ffff"],
"properties":{
"device_state":{
"decoder":["string_from_hex_data", "manufacturerdata", 20, 2],
"lookup":["00", "off",
"01", "low power",
"02", "fault",
"03", "bulk",
"04", "absorption",
"05", "float",
"06", "storage",
"07", "equalize manual",
"09", "inverting",
"0b", "power_supply",
"f5", "starting up",
"f6", "repeated absorption",
"f7", "recondition",
"f8", "battery safe",
"f9", "active",
"fc", "external control",
"ff", "N/A"]
},
"volt_in":{
"condition":["manufacturerdata", 24, "!", "ff7f"],
"decoder":["value_from_hex_data", "manufacturerdata", 24, 4, true, true],
"post_proc":["/", 100]
},
"current_out":{
"condition":["manufacturerdata", 28, "!", "ff7f"],
"decoder":["value_from_hex_data", "manufacturerdata", 28, 4, true, true],
"post_proc":["/", 10]
},
"volt_out":{
"condition":["manufacturerdata", 32, "!", "ffff"],
"decoder":["value_from_hex_data", "manufacturerdata", 32, 4, true, false],
"post_proc":["/", 100]
},
"current_in":{
"condition":["manufacturerdata", 36, "!", "ffff"],
"decoder":["value_from_hex_data", "manufacturerdata", 36, 4, true, false],
"post_proc":["/", 10]
},
"error_code":{
"condition":["manufacturerdata", 22, "!", "ff"],
"decoder":["value_from_hex_data", "manufacturerdata", 22, 2]
}
}
})"""";*/

const char* _VICTORIONTR_json_props = "{\"properties\":{\"device_state\":{\"unit\":\"string\",\"name\":\"device state\"},\"volt_out\":{\"unit\":\"V\",\"name\":\"voltage\"},\"current_out\":{\"unit\":\"A\",\"name\":\"current\"},\"volt_in\":{\"unit\":\"V\",\"name\":\"voltage\"},\"current_in\":{\"unit\":\"A\",\"name\":\"current\"},\"error_code\":{\"unit\":\"int\",\"name\":\"error code\"}}}";
/*R""""(
{
"properties":{
"device_state":{
"unit":"string",
"name":"device state"
},
"volt_out": {
"unit": "V",
"name": "voltage"
},
"current_out": {
"unit": "A",
"name": "current"
},
"volt_in": {
"unit": "V",
"name": "voltage"
},
"current_in": {
"unit": "A",
"name": "current"
},
"error_code":{
"unit":"int",
"name":"error code"
}
}
})"""";*/
Loading
Loading