Add more ui, tweak memory usage, add device configs (#578)
This commit is contained in:
parent
b8ea0d477d
commit
f7d23a1ae9
|
@ -0,0 +1,214 @@
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignArrayOfStructures: None
|
||||||
|
AlignConsecutiveAssignments:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: true
|
||||||
|
AlignConsecutiveBitFields:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveDeclarations:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveMacros:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: MultiLine
|
||||||
|
AttributeMacros:
|
||||||
|
- __capability
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeConceptDeclarations: Always
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 999
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
QualifierAlignment: Leave
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
EmptyLineAfterAccessModifier: Never
|
||||||
|
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
PackConstructorInitializers: BinPack
|
||||||
|
BasedOnStyle: ''
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IfMacros:
|
||||||
|
- KJ_IF_MAYBE
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentCaseBlocks: false
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentExternBlock: AfterExternBlock
|
||||||
|
IndentRequiresClause: true
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
InsertBraces: false
|
||||||
|
InsertTrailingCommas: None
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
LambdaBodyIndentation: Signature
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCBreakBeforeNestedBlockParam: true
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakOpenParenthesis: 0
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PenaltyIndentedWhitespace: 0
|
||||||
|
PointerAlignment: Right
|
||||||
|
PPIndentWidth: -1
|
||||||
|
ReferenceAlignment: Pointer
|
||||||
|
ReflowComments: true
|
||||||
|
RemoveBracesLLVM: false
|
||||||
|
RequiresClausePosition: OwnLine
|
||||||
|
SeparateDefinitionBlocks: Leave
|
||||||
|
ShortNamespaceLines: 1
|
||||||
|
SortIncludes: CaseSensitive
|
||||||
|
SortJavaStaticImport: Before
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeParensOptions:
|
||||||
|
AfterControlStatements: true
|
||||||
|
AfterForeachMacros: true
|
||||||
|
AfterFunctionDefinitionName: false
|
||||||
|
AfterFunctionDeclarationName: false
|
||||||
|
AfterIfMacros: true
|
||||||
|
AfterOverloadedOperator: false
|
||||||
|
AfterRequiresInClause: false
|
||||||
|
AfterRequiresInExpression: false
|
||||||
|
BeforeNonEmptyParentheses: false
|
||||||
|
SpaceAroundPointerQualifiers: Default
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: Never
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInLineCommentPrefix:
|
||||||
|
Minimum: 1
|
||||||
|
Maximum: -1
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
BitFieldColonSpacing: Both
|
||||||
|
Standard: Latest
|
||||||
|
StatementAttributeLikeMacros:
|
||||||
|
- Q_EMIT
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TabWidth: 8
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Never
|
||||||
|
WhitespaceSensitiveMacros:
|
||||||
|
- STRINGIZE
|
||||||
|
- PP_STRINGIZE
|
||||||
|
- BOOST_PP_STRINGIZE
|
||||||
|
- NS_SWIFT_NAME
|
||||||
|
- CF_SWIFT_NAME
|
|
@ -1,9 +1,9 @@
|
||||||
#include "BleFingerprint.h"
|
#include "BleFingerprint.h"
|
||||||
#include "BleFingerprintCollection.h"
|
#include "BleFingerprintCollection.h"
|
||||||
|
#include "mbedtls/aes.h"
|
||||||
#include "rssi.h"
|
#include "rssi.h"
|
||||||
#include "string_utils.h"
|
#include "string_utils.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "mbedtls/aes.h"
|
|
||||||
|
|
||||||
class ClientCallbacks : public BLEClientCallbacks
|
class ClientCallbacks : public BLEClientCallbacks
|
||||||
{
|
{
|
||||||
|
@ -15,21 +15,33 @@ class ClientCallbacks : public BLEClientCallbacks
|
||||||
|
|
||||||
static ClientCallbacks clientCB;
|
static ClientCallbacks clientCB;
|
||||||
|
|
||||||
bool BleFingerprint::shouldHide(const String& s)
|
bool BleFingerprint::shouldHide(const String &s) {
|
||||||
{
|
|
||||||
if (BleFingerprintCollection::include.length() > 0 && !prefixExists(BleFingerprintCollection::include, s)) return true;
|
if (BleFingerprintCollection::include.length() > 0 && !prefixExists(BleFingerprintCollection::include, s)) return true;
|
||||||
return (BleFingerprintCollection::exclude.length() > 0 && prefixExists(BleFingerprintCollection::exclude, s));
|
return (BleFingerprintCollection::exclude.length() > 0 && prefixExists(BleFingerprintCollection::exclude, s));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BleFingerprint::setId(const String& newId, short newIdType, const String& newName)
|
bool BleFingerprint::setId(const String &newId, short newIdType, const String &newName) {
|
||||||
{
|
|
||||||
if (newIdType < idType && idType > 0) return false;
|
if ((idType < 0 || newIdType < 0) ? newIdType >= idType : newIdType <= idType) return false;
|
||||||
|
//Serial.printf("setId: %s %d %s\n", newId.c_str(), newIdType, newName.c_str());
|
||||||
|
|
||||||
hidden = shouldHide(newId);
|
|
||||||
ignore = newIdType < 0;
|
ignore = newIdType < 0;
|
||||||
|
idType = newIdType;
|
||||||
|
|
||||||
if ((idType != newIdType) || !id.equals(newId)) {
|
DeviceConfig dc;
|
||||||
|
if (BleFingerprintCollection::findDeviceConfig(newId, dc))
|
||||||
|
{
|
||||||
|
if (!dc.alias.isEmpty())
|
||||||
|
return setId(dc.alias, ID_TYPE_ALIAS, dc.name);
|
||||||
|
if (!dc.name.isEmpty())
|
||||||
|
name = dc.name;
|
||||||
|
} else
|
||||||
|
if (!newName.isEmpty() && name!=newName)
|
||||||
|
name = newName;
|
||||||
|
|
||||||
|
if (id != newId) {
|
||||||
|
|
||||||
|
hidden = shouldHide(newId);
|
||||||
countable = !ignore && !hidden && !BleFingerprintCollection::countIds.isEmpty() && prefixExists(BleFingerprintCollection::countIds, newId);
|
countable = !ignore && !hidden && !BleFingerprintCollection::countIds.isEmpty() && prefixExists(BleFingerprintCollection::countIds, newId);
|
||||||
bool newQuery = !ignore && !BleFingerprintCollection::query.isEmpty() && prefixExists(BleFingerprintCollection::query, newId);
|
bool newQuery = !ignore && !BleFingerprintCollection::query.isEmpty() && prefixExists(BleFingerprintCollection::query, newId);
|
||||||
if (newQuery != allowQuery)
|
if (newQuery != allowQuery)
|
||||||
|
@ -41,19 +53,16 @@ bool BleFingerprint::setId(const String& newId, short newIdType, const String& n
|
||||||
{
|
{
|
||||||
qryDelayMillis = 30000;
|
qryDelayMillis = 30000;
|
||||||
lastQryMillis = millis();
|
lastQryMillis = millis();
|
||||||
} else if (rssi < -70)
|
} else if (rssi < -70) {
|
||||||
{
|
|
||||||
qryDelayMillis = 5000;
|
qryDelayMillis = 5000;
|
||||||
lastQryMillis = millis();
|
lastQryMillis = millis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id = newId;
|
id = newId;
|
||||||
idType = newIdType;
|
|
||||||
added = false;
|
added = false;
|
||||||
}
|
}
|
||||||
if (!newName.isEmpty() && !name.equals(newName)) name = newName;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,14 +127,12 @@ int bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_dat
|
||||||
|
|
||||||
struct encryption_block
|
struct encryption_block
|
||||||
{
|
{
|
||||||
uint8_t key[16];
|
uint8_t key[16];
|
||||||
uint8_t plain_text[16];
|
uint8_t plain_text[16];
|
||||||
uint8_t cipher_text[16];
|
uint8_t cipher_text[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
int ble_ll_resolv_rpa(const uint8_t *rpa, const uint8_t *irk)
|
bool ble_ll_resolv_rpa(const uint8_t *rpa, const uint8_t *irk) {
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
struct encryption_block ecb;
|
struct encryption_block ecb;
|
||||||
|
|
||||||
auto irk32 = (const uint32_t *)irk;
|
auto irk32 = (const uint32_t *)irk;
|
||||||
|
@ -137,7 +144,6 @@ int ble_ll_resolv_rpa(const uint8_t *rpa, const uint8_t *irk)
|
||||||
key32[2] = irk32[2];
|
key32[2] = irk32[2];
|
||||||
key32[3] = irk32[3];
|
key32[3] = irk32[3];
|
||||||
|
|
||||||
|
|
||||||
pt32[0] = 0;
|
pt32[0] = 0;
|
||||||
pt32[1] = 0;
|
pt32[1] = 0;
|
||||||
pt32[2] = 0;
|
pt32[2] = 0;
|
||||||
|
@ -149,17 +155,11 @@ int ble_ll_resolv_rpa(const uint8_t *rpa, const uint8_t *irk)
|
||||||
|
|
||||||
auto err = bt_encrypt_be(ecb.key, ecb.plain_text, ecb.cipher_text);
|
auto err = bt_encrypt_be(ecb.key, ecb.plain_text, ecb.cipher_text);
|
||||||
|
|
||||||
if ((ecb.cipher_text[15] == rpa[0]) && (ecb.cipher_text[14] == rpa[1]) &&
|
if (ecb.cipher_text[15] != rpa[0] || ecb.cipher_text[14] != rpa[1] || ecb.cipher_text[13] != rpa[2]) return false;
|
||||||
(ecb.cipher_text[13] == rpa[2])) {
|
|
||||||
|
|
||||||
rc = 1;
|
// Serial.printf("RPA resolved %d %02x%02x%02x %02x%02x%02x\n", err, rpa[0], rpa[1], rpa[2], ecb.cipher_text[15], ecb.cipher_text[14], ecb.cipher_text[13]);
|
||||||
} else {
|
|
||||||
rc = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Serial.printf("RPA resolved %d %d %02x%02x%02x %02x%02x%02x\n", rc, err, rpa[0], rpa[1], rpa[2], ecb.cipher_text[15], ecb.cipher_text[14], ecb.cipher_text[13]);
|
return true;
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BleFingerprint::fingerprintAddress()
|
void BleFingerprint::fingerprintAddress()
|
||||||
|
@ -171,26 +171,23 @@ void BleFingerprint::fingerprintAddress()
|
||||||
{
|
{
|
||||||
switch (addressType)
|
switch (addressType)
|
||||||
{
|
{
|
||||||
case BLE_ADDR_PUBLIC:
|
case BLE_ADDR_PUBLIC:
|
||||||
case BLE_ADDR_PUBLIC_ID:
|
case BLE_ADDR_PUBLIC_ID:
|
||||||
setId(mac, ID_TYPE_PUBLIC_MAC);
|
setId(mac, ID_TYPE_PUBLIC_MAC);
|
||||||
|
break;
|
||||||
|
case BLE_ADDR_RANDOM: {
|
||||||
|
auto naddress = address.getNative();
|
||||||
|
auto irks = BleFingerprintCollection::getIrks();
|
||||||
|
auto it = std::find_if(irks.begin(), irks.end(), [naddress](uint8_t *irk) { return ble_ll_resolv_rpa(naddress, irk); });
|
||||||
|
if (it != irks.end()) {
|
||||||
|
auto irk_hex = hexStr(*it, 16);
|
||||||
|
setId(String("irk:") + irk_hex.c_str(), ID_TYPE_KNOWN_IRK);
|
||||||
break;
|
break;
|
||||||
case BLE_ADDR_RANDOM:
|
|
||||||
{
|
|
||||||
auto naddress = address.getNative();
|
|
||||||
auto irks = BleFingerprintCollection::irks;
|
|
||||||
auto it = std::find_if(irks.begin(), irks.end(), [naddress](std::pair<uint8_t *, String> &p) {
|
|
||||||
auto irk = std::get<0>(p);
|
|
||||||
return ble_ll_resolv_rpa(naddress, irk);
|
|
||||||
});
|
|
||||||
if (it != irks.end()) {
|
|
||||||
setId(std::get<1>(*it), ID_TYPE_KNOWN_IRK);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
default:
|
}
|
||||||
setId(mac, ID_TYPE_MAC);
|
default:
|
||||||
break;
|
setId(mac, ID_TYPE_RAND_MAC);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,7 +292,7 @@ void BleFingerprint::fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDe
|
||||||
{ // found COVID-19 exposure tracker
|
{ // found COVID-19 exposure tracker
|
||||||
calRssi = BleFingerprintCollection::refRssi + EXPOSURE_TX;
|
calRssi = BleFingerprintCollection::refRssi + EXPOSURE_TX;
|
||||||
setId("exp:" + String(strServiceData.length()), ID_TYPE_EXPOSURE);
|
setId("exp:" + String(strServiceData.length()), ID_TYPE_EXPOSURE);
|
||||||
disc = hexStr(strServiceData).c_str();
|
//disc = hexStr(strServiceData).c_str();
|
||||||
}
|
}
|
||||||
else if (uuid == smartTagUUID)
|
else if (uuid == smartTagUUID)
|
||||||
{ // found Samsung smart tag
|
{ // found Samsung smart tag
|
||||||
|
@ -392,7 +389,7 @@ void BleFingerprint::fingerprintManufactureData(NimBLEAdvertisedDevice *advertis
|
||||||
BLEBeacon oBeacon = BLEBeacon();
|
BLEBeacon oBeacon = BLEBeacon();
|
||||||
oBeacon.setData(strManufacturerData);
|
oBeacon.setData(strManufacturerData);
|
||||||
calRssi = oBeacon.getSignalPower();
|
calRssi = oBeacon.getSignalPower();
|
||||||
setId(Sprintf("iBeacon:%s-%u-%u", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), calRssi != 3 ? ID_TYPE_IBEACON : ID_TYPE_ECHO_LOST );
|
setId(Sprintf("iBeacon:%s-%u-%u", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), calRssi != 3 ? ID_TYPE_IBEACON : ID_TYPE_ECHO_LOST);
|
||||||
}
|
}
|
||||||
else if (strManufacturerData.length() >= 4 && strManufacturerData[2] == 0x10)
|
else if (strManufacturerData.length() >= 4 && strManufacturerData[2] == 0x10)
|
||||||
{
|
{
|
||||||
|
@ -440,11 +437,12 @@ void BleFingerprint::fingerprintManufactureData(NimBLEAdvertisedDevice *advertis
|
||||||
{
|
{
|
||||||
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
||||||
setId(Sprintf("msft:cdp:%02x%02x", strManufacturerData[3], strManufacturerData[5]), ID_TYPE_MSFT);
|
setId(Sprintf("msft:cdp:%02x%02x", strManufacturerData[3], strManufacturerData[5]), ID_TYPE_MSFT);
|
||||||
disc = Sprintf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
/*disc = Sprintf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||||
strManufacturerData[6], strManufacturerData[7], strManufacturerData[8], strManufacturerData[9], strManufacturerData[10],
|
strManufacturerData[6], strManufacturerData[7], strManufacturerData[8], strManufacturerData[9], strManufacturerData[10],
|
||||||
strManufacturerData[11], strManufacturerData[12], strManufacturerData[13], strManufacturerData[14], strManufacturerData[15],
|
strManufacturerData[11], strManufacturerData[12], strManufacturerData[13], strManufacturerData[14], strManufacturerData[15],
|
||||||
strManufacturerData[16], strManufacturerData[17], strManufacturerData[18], strManufacturerData[19], strManufacturerData[20],
|
strManufacturerData[16], strManufacturerData[17], strManufacturerData[18], strManufacturerData[19], strManufacturerData[20],
|
||||||
strManufacturerData[21], strManufacturerData[22], strManufacturerData[23], strManufacturerData[24], strManufacturerData[25]);
|
strManufacturerData[21], strManufacturerData[22], strManufacturerData[23], strManufacturerData[24], strManufacturerData[25]);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
else if (manuf == "0075") // samsung
|
else if (manuf == "0075") // samsung
|
||||||
{
|
{
|
||||||
|
@ -549,7 +547,7 @@ void BleFingerprint::fill(JsonObject *doc)
|
||||||
|
|
||||||
bool BleFingerprint::report(JsonObject *doc)
|
bool BleFingerprint::report(JsonObject *doc)
|
||||||
{
|
{
|
||||||
if (ignore || idType == 0 || hidden)
|
if (ignore || idType <= ID_TYPE_RAND_MAC || hidden)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (reported || !hasValue)
|
if (reported || !hasValue)
|
||||||
|
@ -666,3 +664,8 @@ bool BleFingerprint::shouldCount()
|
||||||
|
|
||||||
return counting;
|
return counting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BleFingerprint::expire()
|
||||||
|
{
|
||||||
|
lastSeenMillis = 0;
|
||||||
|
}
|
||||||
|
|
|
@ -16,18 +16,21 @@
|
||||||
|
|
||||||
#define ID_TYPE_TX_POW short(1)
|
#define ID_TYPE_TX_POW short(1)
|
||||||
|
|
||||||
|
#define NO_ID_TYPE short(0)
|
||||||
|
|
||||||
#define ID_TYPE_ECHO_LOST short(-10)
|
#define ID_TYPE_ECHO_LOST short(-10)
|
||||||
#define ID_TYPE_MISC_APPLE short(-5)
|
#define ID_TYPE_MISC_APPLE short(-5)
|
||||||
|
|
||||||
#define ID_TYPE_MAC short(0)
|
#define ID_TYPE_RAND_MAC short(1)
|
||||||
#define ID_TYPE_AD short(10)
|
#define ID_TYPE_AD short(10)
|
||||||
#define ID_TYPE_SD short(15)
|
#define ID_TYPE_SD short(15)
|
||||||
#define ID_TYPE_MD short(20)
|
#define ID_TYPE_MD short(20)
|
||||||
#define ID_TYPE_MISC short(30)
|
#define ID_TYPE_MISC short(30)
|
||||||
#define ID_TYPE_FINDMY short(32)
|
#define ID_TYPE_FINDMY short(32)
|
||||||
#define ID_TYPE_NAME short(35)
|
#define ID_TYPE_NAME short(35)
|
||||||
#define ID_TYPE_PUBLIC_MAC short(50)
|
#define ID_TYPE_MSFT short(40)
|
||||||
#define ID_TYPE_MSFT short(100)
|
#define ID_TYPE_UNIQUE short(50)
|
||||||
|
#define ID_TYPE_PUBLIC_MAC short(55)
|
||||||
#define ID_TYPE_SONOS short(105)
|
#define ID_TYPE_SONOS short(105)
|
||||||
#define ID_TYPE_GARMIN short(107)
|
#define ID_TYPE_GARMIN short(107)
|
||||||
#define ID_TYPE_MITHERM short(110)
|
#define ID_TYPE_MITHERM short(110)
|
||||||
|
@ -38,7 +41,7 @@
|
||||||
#define ID_TYPE_ITRACK short(127)
|
#define ID_TYPE_ITRACK short(127)
|
||||||
#define ID_TYPE_NUT short(128)
|
#define ID_TYPE_NUT short(128)
|
||||||
#define ID_TYPE_TRACKR short(130)
|
#define ID_TYPE_TRACKR short(130)
|
||||||
#define ID_TYPE_TILE short( 135)
|
#define ID_TYPE_TILE short(135)
|
||||||
#define ID_TYPE_MEATER short(140)
|
#define ID_TYPE_MEATER short(140)
|
||||||
#define ID_TYPE_TRACTIVE short(142)
|
#define ID_TYPE_TRACTIVE short(142)
|
||||||
#define ID_TYPE_VANMOOF short(145)
|
#define ID_TYPE_VANMOOF short(145)
|
||||||
|
@ -51,7 +54,7 @@
|
||||||
#define ID_TYPE_EBEACON short(220)
|
#define ID_TYPE_EBEACON short(220)
|
||||||
#define ID_TYPE_ABEACON short(230)
|
#define ID_TYPE_ABEACON short(230)
|
||||||
#define ID_TYPE_IBEACON short(240)
|
#define ID_TYPE_IBEACON short(240)
|
||||||
|
#define ID_TYPE_ALIAS short(250)
|
||||||
|
|
||||||
class BleFingerprintCollection;
|
class BleFingerprintCollection;
|
||||||
|
|
||||||
|
@ -79,6 +82,8 @@ public:
|
||||||
|
|
||||||
String getMac() const { return SMacf(address); }
|
String getMac() const { return SMacf(address); }
|
||||||
|
|
||||||
|
short getIdType() const { return idType; }
|
||||||
|
|
||||||
String const getDiscriminator() { return disc; }
|
String const getDiscriminator() { return disc; }
|
||||||
|
|
||||||
float getDistance() const { return output.value.position; }
|
float getDistance() const { return output.value.position; }
|
||||||
|
@ -91,7 +96,7 @@ public:
|
||||||
|
|
||||||
NimBLEAddress const getAddress() { return address; }
|
NimBLEAddress const getAddress() { return address; }
|
||||||
|
|
||||||
unsigned long getMsSinceLastSeen() const { return millis() - lastSeenMillis; };
|
unsigned long getMsSinceLastSeen() const { return lastSeenMillis ? millis() - lastSeenMillis : 4294967295; };
|
||||||
|
|
||||||
unsigned long getMsSinceFirstSeen() const { return millis() - firstSeenMillis; };
|
unsigned long getMsSinceFirstSeen() const { return millis() - firstSeenMillis; };
|
||||||
|
|
||||||
|
@ -115,6 +120,8 @@ public:
|
||||||
bool shouldCount();
|
bool shouldCount();
|
||||||
void fingerprintAddress();
|
void fingerprintAddress();
|
||||||
|
|
||||||
|
void expire();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static bool shouldHide(const String &s);
|
static bool shouldHide(const String &s);
|
||||||
|
@ -122,7 +129,7 @@ private:
|
||||||
bool hasValue = false, added = false, close = false, reported = false, ignore = false, allowQuery = false, didQuery = false, rmAsst = false, hidden = false, connectable = false, countable = false, counting = false;
|
bool hasValue = false, added = false, close = false, reported = false, ignore = false, allowQuery = false, didQuery = false, rmAsst = false, hidden = false, connectable = false, countable = false, counting = false;
|
||||||
NimBLEAddress address;
|
NimBLEAddress address;
|
||||||
String id, name, disc;
|
String id, name, disc;
|
||||||
short int idType = 0;
|
short int idType = NO_ID_TYPE;
|
||||||
int rssi = -100, calRssi = NO_RSSI, mdRssi = NO_RSSI, asRssi = NO_RSSI, newest = NO_RSSI, recent = NO_RSSI, oldest = NO_RSSI;
|
int rssi = -100, calRssi = NO_RSSI, mdRssi = NO_RSSI, asRssi = NO_RSSI, newest = NO_RSSI, recent = NO_RSSI, oldest = NO_RSSI;
|
||||||
unsigned int qryAttempts = 0, qryDelayMillis = 0;
|
unsigned int qryAttempts = 0, qryDelayMillis = 0;
|
||||||
float raw = 0, lastReported = 0, temp = 0, humidity = 0;
|
float raw = 0, lastReported = 0, temp = 0, humidity = 0;
|
||||||
|
@ -139,11 +146,8 @@ private:
|
||||||
bool filter();
|
bool filter();
|
||||||
|
|
||||||
void fingerprint(NimBLEAdvertisedDevice *advertisedDevice);
|
void fingerprint(NimBLEAdvertisedDevice *advertisedDevice);
|
||||||
|
|
||||||
void fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceAdvCount, bool haveTxPower, int8_t txPower);
|
void fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceAdvCount, bool haveTxPower, int8_t txPower);
|
||||||
|
|
||||||
void fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceDataCount, bool haveTxPower, int8_t txPower);
|
void fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceDataCount, bool haveTxPower, int8_t txPower);
|
||||||
|
|
||||||
void fingerprintManufactureData(NimBLEAdvertisedDevice *advertisedDevice, bool haveTxPower, int8_t txPower);
|
void fingerprintManufactureData(NimBLEAdvertisedDevice *advertisedDevice, bool haveTxPower, int8_t txPower);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,22 +5,33 @@
|
||||||
String BleFingerprintCollection::include{}, BleFingerprintCollection::exclude{}, BleFingerprintCollection::query{}, BleFingerprintCollection::knownMacs{}, BleFingerprintCollection::knownIrks{}, BleFingerprintCollection::countIds{};
|
String BleFingerprintCollection::include{}, BleFingerprintCollection::exclude{}, BleFingerprintCollection::query{}, BleFingerprintCollection::knownMacs{}, BleFingerprintCollection::knownIrks{}, BleFingerprintCollection::countIds{};
|
||||||
float BleFingerprintCollection::skipDistance = 0.0f, BleFingerprintCollection::maxDistance = 0.0f, BleFingerprintCollection::absorption = 3.5f, BleFingerprintCollection::countEnter = 2, BleFingerprintCollection::countExit = 4;
|
float BleFingerprintCollection::skipDistance = 0.0f, BleFingerprintCollection::maxDistance = 0.0f, BleFingerprintCollection::absorption = 3.5f, BleFingerprintCollection::countEnter = 2, BleFingerprintCollection::countExit = 4;
|
||||||
int BleFingerprintCollection::refRssi = 0, BleFingerprintCollection::forgetMs = 0, BleFingerprintCollection::skipMs = 0, BleFingerprintCollection::countMs = 10000;
|
int BleFingerprintCollection::refRssi = 0, BleFingerprintCollection::forgetMs = 0, BleFingerprintCollection::skipMs = 0, BleFingerprintCollection::countMs = 10000;
|
||||||
std::vector<std::pair<uint8_t*,String>> BleFingerprintCollection::irks;
|
std::vector<DeviceConfig> BleFingerprintCollection::deviceConfigs;
|
||||||
|
std::vector<uint8_t *> BleFingerprintCollection::irks;
|
||||||
|
|
||||||
bool BleFingerprintCollection::config(String& id, String& json) {
|
bool BleFingerprintCollection::config(String &id, String &json) {
|
||||||
DynamicJsonDocument doc(1024);
|
DynamicJsonDocument doc(1024);
|
||||||
deserializeJson(doc, json);
|
deserializeJson(doc, json);
|
||||||
|
|
||||||
|
DeviceConfig config = {};
|
||||||
|
config.id = id;
|
||||||
|
auto alias = doc["id"].as<String>();
|
||||||
|
if (alias != id) config.alias = alias;
|
||||||
|
config.calRssi = doc["rssi@1m"];
|
||||||
|
config.name = doc["name"].as<String>();
|
||||||
|
deviceConfigs.push_back(config);
|
||||||
|
|
||||||
auto p = id.indexOf("irk:");
|
auto p = id.indexOf("irk:");
|
||||||
if (p == 0) {
|
if (p == 0) {
|
||||||
auto irk_hex = id.substring(4);
|
auto irk_hex = id.substring(4);
|
||||||
uint8_t* irk = new uint8_t[16];
|
uint8_t *irk = new uint8_t[16];
|
||||||
if (!hextostr(irk_hex, irk, 16))
|
if (!hextostr(irk_hex, irk, 16))
|
||||||
return false;
|
return false;
|
||||||
irks.push_back({irk, doc["id"]});
|
irks.push_back(irk);
|
||||||
|
|
||||||
for(auto it = std::begin(fingerprints); it != std::end(fingerprints); ++it)
|
for (auto it = std::begin(fingerprints); it != std::end(fingerprints); ++it)
|
||||||
(*it)->fingerprintAddress();
|
(*it)->fingerprintAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,14 +39,14 @@ void BleFingerprintCollection::connectToWifi() {
|
||||||
std::istringstream iss(BleFingerprintCollection::knownIrks.c_str());
|
std::istringstream iss(BleFingerprintCollection::knownIrks.c_str());
|
||||||
std::string irk_hex;
|
std::string irk_hex;
|
||||||
while (iss >> irk_hex) {
|
while (iss >> irk_hex) {
|
||||||
uint8_t* irk = new uint8_t[16];
|
uint8_t *irk = new uint8_t[16];
|
||||||
if (!hextostr(irk_hex.c_str(), irk, 16))
|
if (!hextostr(irk_hex.c_str(), irk, 16))
|
||||||
continue;
|
continue;
|
||||||
irks.push_back({irk, String("irk:") + irk_hex.c_str()});
|
irks.push_back(irk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BleFingerprintCollection::command(String& command, String& pay) {
|
bool BleFingerprintCollection::command(String &command, String &pay) {
|
||||||
|
|
||||||
if (command == "max_distance")
|
if (command == "max_distance")
|
||||||
{
|
{
|
||||||
|
@ -116,21 +127,22 @@ BleFingerprint *BleFingerprintCollection::getFingerprintInternal(BLEAdvertisedDe
|
||||||
{
|
{
|
||||||
auto mac = advertisedDevice->getAddress();
|
auto mac = advertisedDevice->getAddress();
|
||||||
|
|
||||||
auto it = std::find_if(fingerprints.begin(), fingerprints.end(), [mac](BleFingerprint *f)
|
auto it = std::find_if(fingerprints.rbegin(), fingerprints.rend(), [mac](BleFingerprint *f) { return f->getAddress() == mac; });
|
||||||
{ return f->getAddress() == mac; });
|
if (it != fingerprints.rend())
|
||||||
if (it != fingerprints.end())
|
|
||||||
return *it;
|
return *it;
|
||||||
|
|
||||||
auto created = new BleFingerprint(this, advertisedDevice, ONE_EURO_FCMIN, ONE_EURO_BETA, ONE_EURO_DCUTOFF);
|
auto created = new BleFingerprint(this, advertisedDevice, ONE_EURO_FCMIN, ONE_EURO_BETA, ONE_EURO_DCUTOFF);
|
||||||
auto it2 = std::find_if(fingerprints.begin(), fingerprints.end(), [created](BleFingerprint *f)
|
auto it2 = std::find_if(fingerprints.begin(), fingerprints.end(), [created](BleFingerprint *f) { return f->getId() == created->getId(); });
|
||||||
{ return f->getId() == created->getId(); });
|
|
||||||
if (it2 != fingerprints.end())
|
if (it2 != fingerprints.end())
|
||||||
{
|
{
|
||||||
auto found = *it2;
|
auto found = *it2;
|
||||||
|
//Serial.printf("Detected mac switch for fingerprint id %s\n", found->getId().c_str());
|
||||||
created->setInitial(found->getRssi(), found->getDistance());
|
created->setInitial(found->getRssi(), found->getDistance());
|
||||||
|
if (found->getIdType()>ID_TYPE_UNIQUE)
|
||||||
|
found->expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
fingerprints.push_front(created);
|
fingerprints.push_back(created);
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,18 +156,28 @@ BleFingerprint *BleFingerprintCollection::getFingerprint(BLEAdvertisedDevice *ad
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::list<BleFingerprint *> BleFingerprintCollection::getCopy()
|
const std::vector<BleFingerprint *> BleFingerprintCollection::getCopy() {
|
||||||
{
|
|
||||||
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
|
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
|
||||||
log_e("Couldn't take semaphore!");
|
log_e("Couldn't take semaphore!");
|
||||||
cleanupOldFingerprints();
|
cleanupOldFingerprints();
|
||||||
std::list<BleFingerprint *> copy(fingerprints);
|
std::vector<BleFingerprint *> copy(fingerprints);
|
||||||
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
|
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
|
||||||
log_e("Couldn't give semaphore!");
|
log_e("Couldn't give semaphore!");
|
||||||
return std::move(copy);
|
return std::move(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::list<BleFingerprint *>* const BleFingerprintCollection::getNative()
|
const std::vector<BleFingerprint *> *const BleFingerprintCollection::getNative() { return &fingerprints; }
|
||||||
{
|
|
||||||
return &fingerprints;
|
std::vector<uint8_t *> BleFingerprintCollection::getIrks() { return irks; }
|
||||||
|
|
||||||
|
bool BleFingerprintCollection::findDeviceConfig(const String &id, DeviceConfig &config) {
|
||||||
|
auto it = std::find_if(deviceConfigs.begin(), deviceConfigs.end(), [id](DeviceConfig dc) { return dc.id == id; });
|
||||||
|
if (it == deviceConfigs.end()) return false;
|
||||||
|
config = (*it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DeviceConfig> BleFingerprintCollection::getConfigs()
|
||||||
|
{
|
||||||
|
return deviceConfigs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,13 @@
|
||||||
#define ALLOW_BLE_CONTROLLER_RESTART_AFTER_SECS 1800
|
#define ALLOW_BLE_CONTROLLER_RESTART_AFTER_SECS 1800
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct DeviceConfig {
|
||||||
|
String id;
|
||||||
|
String alias;
|
||||||
|
String name;
|
||||||
|
uint8_t calRssi = 127;
|
||||||
|
};
|
||||||
|
|
||||||
class BleFingerprintCollection : public BLEAdvertisedDeviceCallbacks
|
class BleFingerprintCollection : public BLEAdvertisedDeviceCallbacks
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -22,13 +29,12 @@ public:
|
||||||
}
|
}
|
||||||
BleFingerprint *getFingerprint(BLEAdvertisedDevice *advertisedDevice);
|
BleFingerprint *getFingerprint(BLEAdvertisedDevice *advertisedDevice);
|
||||||
void cleanupOldFingerprints();
|
void cleanupOldFingerprints();
|
||||||
const std::list<BleFingerprint *>* const getNative();
|
const std::vector<BleFingerprint *> *const getNative();
|
||||||
const std::list<BleFingerprint *> getCopy();
|
const std::vector<BleFingerprint *> getCopy();
|
||||||
void setDisable(bool disable) { _disable = disable; }
|
void setDisable(bool disable) { _disable = disable; }
|
||||||
void connectToWifi();
|
void connectToWifi();
|
||||||
bool command(String& command, String& pay);
|
bool command(String& command, String& pay);
|
||||||
bool config(String& id, String& json);
|
bool config(String &id, String &json);
|
||||||
static std::vector<std::pair<uint8_t*,String>> irks;
|
|
||||||
static String knownMacs, knownIrks, include, exclude, query;
|
static String knownMacs, knownIrks, include, exclude, query;
|
||||||
static float skipDistance, maxDistance, absorption;
|
static float skipDistance, maxDistance, absorption;
|
||||||
static int refRssi, forgetMs, skipMs;
|
static int refRssi, forgetMs, skipMs;
|
||||||
|
@ -36,13 +42,20 @@ public:
|
||||||
static float countEnter, countExit;
|
static float countEnter, countExit;
|
||||||
static int countMs;
|
static int countMs;
|
||||||
|
|
||||||
|
static std::vector<uint8_t *> getIrks();
|
||||||
|
static bool findDeviceConfig(const String &id, DeviceConfig &config);
|
||||||
|
|
||||||
|
static std::vector<DeviceConfig> getConfigs();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static std::vector<DeviceConfig> deviceConfigs;
|
||||||
|
static std::vector<uint8_t *> irks;
|
||||||
bool _disable = false;
|
bool _disable = false;
|
||||||
|
|
||||||
unsigned long lastCleanup = 0;
|
unsigned long lastCleanup = 0;
|
||||||
|
|
||||||
SemaphoreHandle_t fingerprintSemaphore;
|
SemaphoreHandle_t fingerprintSemaphore;
|
||||||
std::list<BleFingerprint *> fingerprints;
|
std::vector<BleFingerprint *> fingerprints;
|
||||||
BleFingerprint *getFingerprintInternal(BLEAdvertisedDevice *advertisedDevice);
|
BleFingerprint *getFingerprintInternal(BLEAdvertisedDevice *advertisedDevice);
|
||||||
|
|
||||||
void onResult(BLEAdvertisedDevice *advertisedDevice) override
|
void onResult(BLEAdvertisedDevice *advertisedDevice) override
|
||||||
|
|
|
@ -46,6 +46,15 @@ String kebabify(const String &text)
|
||||||
return kebabify(s).c_str();
|
return kebabify(s).c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string hexStr(const uint8_t *data, int len) {
|
||||||
|
std::string s(len * 2, ' ');
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
s[2 * i] = hexmap[(data[i] & 0xF0) >> 4];
|
||||||
|
s[2 * i + 1] = hexmap[data[i] & 0x0F];
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
std::string hexStr(const char *data, unsigned int len)
|
std::string hexStr(const char *data, unsigned int len)
|
||||||
{
|
{
|
||||||
std::string s(len * 2, ' ');
|
std::string s(len * 2, ' ');
|
||||||
|
|
|
@ -4,35 +4,35 @@
|
||||||
|
|
||||||
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8))
|
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8))
|
||||||
|
|
||||||
static BLEUUID eddystoneUUID((uint16_t)0xFEAA);
|
const BLEUUID eddystoneUUID((uint16_t)0xFEAA);
|
||||||
static BLEUUID tileUUID((uint16_t)0xFEED);
|
const BLEUUID tileUUID((uint16_t)0xFEED);
|
||||||
static BLEUUID exposureUUID((uint16_t)0xFD6F);
|
const BLEUUID exposureUUID((uint16_t)0xFD6F);
|
||||||
static BLEUUID smartTagUUID((uint16_t)0xFD5A);
|
const BLEUUID smartTagUUID((uint16_t)0xFD5A);
|
||||||
static BLEUUID sonosUUID((uint16_t)0xFE07);
|
const BLEUUID sonosUUID((uint16_t)0xFE07);
|
||||||
static BLEUUID itagUUID((uint16_t)0xffe0);
|
const BLEUUID itagUUID((uint16_t)0xffe0);
|
||||||
static BLEUUID miThermUUID(uint16_t(0x181A));
|
const BLEUUID miThermUUID(uint16_t(0x181A));
|
||||||
static BLEUUID trackrUUID((uint16_t)0x0F3E);
|
const BLEUUID trackrUUID((uint16_t)0x0F3E);
|
||||||
static BLEUUID vanmoofUUID(0x6acc5540, 0xe631, 0x4069, 0x944db8ca7598ad50);
|
const BLEUUID vanmoofUUID(0x6acc5540, 0xe631, 0x4069, 0x944db8ca7598ad50);
|
||||||
static BLEUUID tractiveUUID(0x20130001, 0x0719, 0x4b6e, 0xbe5d158ab92fa5a4);
|
const BLEUUID tractiveUUID(0x20130001, 0x0719, 0x4b6e, 0xbe5d158ab92fa5a4);
|
||||||
|
|
||||||
static BLEUUID nutUUID((uint16_t)0x1803);
|
const BLEUUID nutUUID((uint16_t)0x1803);
|
||||||
|
|
||||||
static BLEUUID fitbitUUID(0xadabfb00, 0x6e7d, 0x4601, 0xbda2bffaa68956ba);
|
const BLEUUID fitbitUUID(0xadabfb00, 0x6e7d, 0x4601, 0xbda2bffaa68956ba);
|
||||||
|
|
||||||
static BLEUUID roomAssistantService(0x5403c8a7, 0x5c96, 0x47e9, 0x9ab859e373d875a7);
|
const BLEUUID roomAssistantService(0x5403c8a7, 0x5c96, 0x47e9, 0x9ab859e373d875a7);
|
||||||
static BLEUUID rootAssistantCharacteristic(0x21c46f33, 0xe813, 0x4407, 0x86012ad281030052);
|
const BLEUUID rootAssistantCharacteristic(0x21c46f33, 0xe813, 0x4407, 0x86012ad281030052);
|
||||||
|
|
||||||
static BLEUUID meaterService(0xa75cc7fc, 0xc956, 0x488f, 0xac2a2dbc08b63a04);
|
const BLEUUID meaterService(0xa75cc7fc, 0xc956, 0x488f, 0xac2a2dbc08b63a04);
|
||||||
static BLEUUID meaterCharacteristic(0x7edda774, 0x045e, 0x4bbf, 0x909b45d1991a2876);
|
const BLEUUID meaterCharacteristic(0x7edda774, 0x045e, 0x4bbf, 0x909b45d1991a2876);
|
||||||
|
|
||||||
static BLEUUID genericAccessService(uint16_t(0x1800));
|
const BLEUUID genericAccessService(uint16_t(0x1800));
|
||||||
static BLEUUID nameChar(uint16_t(0x2A00));
|
const BLEUUID nameChar(uint16_t(0x2A00));
|
||||||
|
|
||||||
static BLEUUID deviceInformationService(uint16_t(0x180A));
|
const BLEUUID deviceInformationService(uint16_t(0x180A));
|
||||||
static BLEUUID modelChar(uint16_t(0x2A24));
|
const BLEUUID modelChar(uint16_t(0x2A24));
|
||||||
static BLEUUID fwRevChar(uint16_t(0x2A26));
|
const BLEUUID fwRevChar(uint16_t(0x2A26));
|
||||||
static BLEUUID hwRevChar(uint16_t(0x2A27));
|
const BLEUUID hwRevChar(uint16_t(0x2A27));
|
||||||
static BLEUUID manufChar(uint16_t(0x2A29));
|
const BLEUUID manufChar(uint16_t(0x2A29));
|
||||||
|
|
||||||
static int median_of_3(int a, int b, int c)
|
static int median_of_3(int a, int b, int c)
|
||||||
{
|
{
|
||||||
|
|
|
@ -181,9 +181,7 @@ namespace Enrollment
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Setup()
|
void Setup() {
|
||||||
{
|
|
||||||
//NimBLEDevice::setOwnAddrType(BLE_ADDR_RANDOM, true);
|
|
||||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT);
|
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT);
|
||||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||||
|
|
||||||
|
@ -272,7 +270,7 @@ namespace Enrollment
|
||||||
if (command == "enroll")
|
if (command == "enroll")
|
||||||
{
|
{
|
||||||
id = pay.equals("PRESS") ? "" : pay;
|
id = pay.equals("PRESS") ? "" : pay;
|
||||||
enrolling = !enrolling;
|
enrolling = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -15,8 +15,21 @@
|
||||||
|
|
||||||
namespace HttpServer
|
namespace HttpServer
|
||||||
{
|
{
|
||||||
bool deserializeState(JsonObject root ){return false;}
|
void serializeConfigs(JsonObject& root)
|
||||||
bool deserializeConfig(JsonObject doc, bool fromFS = false){return false;}
|
{
|
||||||
|
JsonArray devices = root.createNestedArray("configs");
|
||||||
|
|
||||||
|
auto f = BleFingerprintCollection::getConfigs();
|
||||||
|
for (auto it = f.begin(); it != f.end(); ++it)
|
||||||
|
{
|
||||||
|
JsonObject node = devices.createNestedObject();
|
||||||
|
node["id"]=it->id;
|
||||||
|
node["alias"] = it->alias;
|
||||||
|
node["name"]=it->name;
|
||||||
|
node["rss@1m"]=it->calRssi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void serializeDevices(JsonObject& root)
|
void serializeDevices(JsonObject& root)
|
||||||
{
|
{
|
||||||
JsonArray devices = root.createNestedArray("devices");
|
JsonArray devices = root.createNestedArray("devices");
|
||||||
|
@ -31,20 +44,30 @@ namespace HttpServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool servingJson = false;
|
||||||
void serveJson(AsyncWebServerRequest* request) {
|
void serveJson(AsyncWebServerRequest* request) {
|
||||||
byte subJson = 0;
|
if (servingJson) request->send(429, "Too Many Requests", "Too Many Requests");
|
||||||
|
servingJson = true;
|
||||||
const String& url = request->url();
|
const String& url = request->url();
|
||||||
|
short subJson = 0;
|
||||||
if (url.indexOf("devices") > 0) subJson = 1;
|
if (url.indexOf("devices") > 0) subJson = 1;
|
||||||
|
if (url.indexOf("configs") > 0) subJson = 2;
|
||||||
|
|
||||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, JSON_BUFFER_SIZE);
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, JSON_BUFFER_SIZE);
|
||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
//root["room"] = room;
|
root["room"] = room;
|
||||||
switch (subJson)
|
switch (subJson)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
serializeDevices(root); break;
|
serializeDevices(root);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
serializeConfigs(root);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
response->setLength();
|
response->setLength();
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
servingJson = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serializeConfig(){};
|
void serializeConfig(){};
|
||||||
|
@ -60,14 +83,10 @@ namespace HttpServer
|
||||||
request->send(response);
|
request->send(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
server->on("/devices", HTTP_GET, [](AsyncWebServerRequest *request)
|
server->on("/devices", HTTP_GET, serveIndexHtml);
|
||||||
{ serveIndexHtml(request); });
|
server->on("/bundle.css", HTTP_GET, serveBundleCss);
|
||||||
server->on("/bundle.css", HTTP_GET, [](AsyncWebServerRequest *request)
|
server->on("/index.js", HTTP_GET, serveIndexJs);
|
||||||
{ serveBundleCss(request); });
|
server->on("/json", HTTP_GET, serveJson);
|
||||||
server->on("/index.js", HTTP_GET, [](AsyncWebServerRequest *request)
|
|
||||||
{ serveIndexJs(request); });
|
|
||||||
server->on("/json", HTTP_GET, [](AsyncWebServerRequest *request)
|
|
||||||
{ serveJson(request); });
|
|
||||||
|
|
||||||
AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request, JsonVariant &json)
|
AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request, JsonVariant &json)
|
||||||
{
|
{
|
||||||
|
@ -83,25 +102,9 @@ namespace HttpServer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const String& url = request->url();
|
const String& url = request->url();
|
||||||
isConfig = url.indexOf("cfg") > -1;
|
|
||||||
if (!isConfig)
|
|
||||||
verboseResponse = deserializeState(root);
|
|
||||||
else
|
|
||||||
verboseResponse = deserializeConfig(root);
|
|
||||||
}
|
}
|
||||||
if (verboseResponse)
|
request->send(200, "application/json", F("{\"success\":true}"));
|
||||||
{
|
});
|
||||||
if (!isConfig)
|
|
||||||
{
|
|
||||||
serveJson(request);
|
|
||||||
return; //if JSON contains "v"
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
serializeConfig(); //Save new settings to FS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request->send(200, "application/json", F("{\"success\":true}")); });
|
|
||||||
server->addHandler(handler);
|
server->addHandler(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,193 @@
|
||||||
/*
|
/*
|
||||||
* Binary array for the Web UI.
|
* Binary array for the Web UI.
|
||||||
* gzip is used for smaller size and improved speeds.
|
* Gzip is used for smaller size and improved speeds.
|
||||||
*
|
|
||||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
|
||||||
* to find out how to easily modify the web UI source!
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Autogenerated do not edit!!
|
// Autogenerated do not edit!!
|
||||||
const uint16_t BUNDLE_CSS_L = 356;
|
const uint16_t BUNDLE_CSS_L = 2904;
|
||||||
const uint8_t BUNDLE_CSS[] PROGMEM = {
|
const uint8_t BUNDLE_CSS[] PROGMEM = {
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x95, 0x51, 0xdb, 0x4a, 0x03, 0x31,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xed, 0x1a, 0x5d, 0x6f, 0xdc, 0xb8,
|
||||||
0x10, 0x7d, 0xef, 0x57, 0x04, 0x44, 0x50, 0x68, 0xca, 0xb6, 0xb4, 0x56, 0xd2, 0x27, 0xf1, 0x45,
|
0xf1, 0xb9, 0xf7, 0x2b, 0xd4, 0x0b, 0x02, 0xd8, 0x01, 0xa5, 0x48, 0xda, 0xd5, 0xae, 0xad, 0x45,
|
||||||
0xc1, 0x37, 0xbf, 0x60, 0x36, 0x99, 0xdd, 0x1d, 0x9b, 0x4d, 0x96, 0x64, 0x7a, 0xa7, 0xff, 0x6e,
|
0x8b, 0x5e, 0x0f, 0x77, 0xe8, 0x01, 0x97, 0x7b, 0xb8, 0xb4, 0x4f, 0x49, 0x1e, 0xb8, 0x12, 0x77,
|
||||||
0xb2, 0x5a, 0xb5, 0x0a, 0x8a, 0x04, 0x12, 0x32, 0x73, 0xce, 0xcc, 0x39, 0x33, 0x2d, 0x90, 0x1b,
|
0xc5, 0x58, 0x12, 0x55, 0x4a, 0xb2, 0x77, 0xad, 0x6e, 0x7f, 0x7b, 0x87, 0xa4, 0x48, 0x51, 0x5a,
|
||||||
0xc5, 0x35, 0x5a, 0x46, 0xd9, 0xd4, 0x85, 0xaf, 0xe6, 0x87, 0x0e, 0x8c, 0x21, 0x57, 0xab, 0x71,
|
0xd9, 0xeb, 0xb4, 0x3d, 0xa0, 0x05, 0x62, 0xc4, 0xb1, 0x38, 0x33, 0x1c, 0x0e, 0xe7, 0x8b, 0x9c,
|
||||||
0xc0, 0xf6, 0xd8, 0x8c, 0xbf, 0x65, 0x19, 0xb7, 0x2c, 0xc1, 0x52, 0xed, 0x94, 0x46, 0xc7, 0x18,
|
0x91, 0x7e, 0xf7, 0xf6, 0xcd, 0xef, 0x9d, 0x06, 0xd3, 0xfc, 0x81, 0x96, 0x69, 0x52, 0xd7, 0xce,
|
||||||
0x8e, 0x2a, 0x78, 0xcf, 0xe2, 0x30, 0x10, 0x42, 0x7b, 0xeb, 0x83, 0x8c, 0xba, 0xc1, 0x16, 0x95,
|
0xfd, 0xc2, 0x0b, 0xbc, 0xb5, 0xf3, 0x0f, 0xe7, 0xdd, 0x4f, 0x7f, 0x75, 0x7e, 0xa6, 0x09, 0x29,
|
||||||
0x48, 0x98, 0x86, 0x85, 0x81, 0xb0, 0x5c, 0xa4, 0x5c, 0xe5, 0x1d, 0xcb, 0x0a, 0x5a, 0xb2, 0x3b,
|
0x6b, 0x02, 0xa3, 0xac, 0x69, 0xaa, 0x3a, 0x7e, 0xfb, 0xd6, 0x22, 0xf5, 0x12, 0x56, 0xbc, 0x79,
|
||||||
0x25, 0x1e, 0x33, 0x71, 0x28, 0xee, 0xd6, 0xe8, 0x28, 0xbd, 0x0f, 0x68, 0xd7, 0xc8, 0xa4, 0x21,
|
0xfb, 0x06, 0xc5, 0x78, 0xd7, 0x10, 0x8e, 0xe2, 0x2d, 0xd9, 0x31, 0x4e, 0xba, 0x2d, 0xe3, 0x29,
|
||||||
0x85, 0x02, 0x81, 0x1d, 0x8a, 0x08, 0x2e, 0xca, 0x88, 0x81, 0xaa, 0x0f, 0x6e, 0xa4, 0x7d, 0x2a,
|
0xe1, 0xb1, 0xef, 0xd4, 0x2c, 0xa7, 0xa9, 0xf3, 0x8a, 0x44, 0x64, 0x4d, 0xb6, 0x9b, 0x2d, 0x3b,
|
||||||
0x3a, 0xbe, 0xe9, 0xb6, 0x39, 0x64, 0xc9, 0x25, 0x49, 0x98, 0x5b, 0x28, 0x31, 0x99, 0xe6, 0xe0,
|
0xb8, 0x35, 0x7d, 0xa4, 0xe5, 0x3e, 0x56, 0x14, 0x2e, 0x40, 0x4e, 0x93, 0xb9, 0xae, 0xdb, 0x3c,
|
||||||
0x49, 0x80, 0x12, 0xa1, 0x2e, 0xe1, 0x6a, 0x32, 0x9b, 0x0d, 0xc5, 0xe7, 0x55, 0x8c, 0x6e, 0xe7,
|
0xb8, 0x09, 0x2b, 0x1b, 0x52, 0x36, 0xf1, 0xb7, 0xdf, 0x9e, 0xb2, 0xa6, 0xc8, 0x3b, 0xf7, 0x81,
|
||||||
0xd7, 0x99, 0x59, 0x82, 0x5e, 0xd6, 0xc1, 0xaf, 0x9c, 0x91, 0xef, 0xf0, 0x8b, 0xc9, 0x34, 0x9f,
|
0x6c, 0xef, 0x68, 0xe3, 0x36, 0xe4, 0xd0, 0x08, 0x16, 0xc4, 0xc5, 0xe9, 0xe7, 0xb6, 0x6e, 0xe2,
|
||||||
0xbe, 0xc2, 0x5b, 0xab, 0x9d, 0xe3, 0x06, 0x23, 0x45, 0x25, 0x9c, 0x77, 0x98, 0x59, 0xbd, 0xed,
|
0xc0, 0xf7, 0x5f, 0x6f, 0x76, 0x40, 0xed, 0xee, 0x70, 0x41, 0xf3, 0x63, 0xdc, 0x52, 0xb7, 0xc6,
|
||||||
0x80, 0xce, 0x24, 0x4d, 0x69, 0x36, 0xc2, 0x77, 0x4c, 0x6d, 0x52, 0xf4, 0x84, 0x35, 0x95, 0x64,
|
0x65, 0xed, 0xd6, 0x84, 0xd3, 0x1d, 0xaa, 0x8f, 0x75, 0x43, 0x0a, 0xb7, 0xa5, 0xc8, 0xc5, 0x55,
|
||||||
0x89, 0x77, 0x19, 0x25, 0x37, 0x58, 0x2e, 0x29, 0xf9, 0xec, 0xab, 0xb4, 0x69, 0x2e, 0x4d, 0x8f,
|
0x95, 0x13, 0x57, 0x01, 0xd0, 0x9f, 0x73, 0x5a, 0xde, 0xbd, 0xc3, 0xc9, 0x7b, 0x39, 0xfc, 0x11,
|
||||||
0x06, 0xc7, 0xc9, 0x16, 0x41, 0x44, 0xd3, 0xc3, 0x5a, 0xbf, 0x97, 0x3e, 0x6e, 0x7f, 0xe0, 0xea,
|
0xe6, 0xa3, 0xf7, 0x64, 0xcf, 0x88, 0xf3, 0xb7, 0x9f, 0xd0, 0xaf, 0x6c, 0xcb, 0x1a, 0x86, 0xfe,
|
||||||
0x00, 0xbb, 0xa8, 0xc1, 0xe2, 0xd7, 0x62, 0x7d, 0xeb, 0xec, 0x5e, 0x82, 0x79, 0x59, 0xc5, 0xe4,
|
0x42, 0xf2, 0x7b, 0xd2, 0xd0, 0x04, 0x3b, 0xbf, 0x90, 0x96, 0xa0, 0xef, 0x38, 0xc5, 0x39, 0xfa,
|
||||||
0x77, 0x5c, 0x14, 0x97, 0x8b, 0xc1, 0x71, 0xc0, 0x50, 0x5a, 0x3c, 0xed, 0xc7, 0x44, 0xa8, 0xe6,
|
0x05, 0x30, 0xce, 0x7b, 0xe0, 0x8f, 0xac, 0x45, 0xbe, 0x13, 0x9c, 0x9d, 0xef, 0x59, 0xce, 0xb8,
|
||||||
0x7c, 0xfe, 0x3b, 0x6c, 0xc8, 0x70, 0xa3, 0x32, 0xfe, 0x38, 0xa2, 0xf8, 0xec, 0xc3, 0xdf, 0x14,
|
0xf3, 0x43, 0xc1, 0x3e, 0x53, 0xc3, 0x6b, 0x3a, 0x7c, 0x7f, 0x2c, 0xb6, 0xac, 0xe7, 0x62, 0xd1,
|
||||||
0xbd, 0x0a, 0x31, 0x4d, 0xa5, 0xf3, 0xd4, 0x6f, 0x37, 0xd1, 0xee, 0x2d, 0xe9, 0xe5, 0x7f, 0x79,
|
0x6f, 0x40, 0x1a, 0xe2, 0x66, 0x84, 0xee, 0x33, 0xd8, 0x91, 0x17, 0x6d, 0xdc, 0x82, 0x3d, 0xba,
|
||||||
0x1c, 0xce, 0xf3, 0x82, 0x1b, 0x11, 0xd1, 0xa2, 0xfe, 0x45, 0xe2, 0x2b, 0x4d, 0xc8, 0x83, 0x61,
|
0x0d, 0xde, 0xca, 0xad, 0xc6, 0xcb, 0x8d, 0xcb, 0xec, 0xd1, 0xf0, 0x78, 0xda, 0xb2, 0xf4, 0xd8,
|
||||||
0x8d, 0x02, 0x00, 0x00
|
0xd9, 0xb3, 0x69, 0x99, 0x81, 0x4c, 0xcd, 0xa6, 0xc0, 0x7c, 0x4f, 0xcb, 0xd8, 0x3f, 0x65, 0xbc,
|
||||||
|
0xb7, 0x86, 0xdb, 0xb0, 0xca, 0x7d, 0xa0, 0x69, 0x93, 0xc5, 0x41, 0x75, 0xd8, 0x24, 0x62, 0x79,
|
||||||
|
0x43, 0xde, 0xcf, 0xf6, 0x4f, 0x78, 0xbb, 0xe5, 0xf1, 0x03, 0x00, 0xc9, 0xd5, 0x87, 0x86, 0x36,
|
||||||
|
0x39, 0xf9, 0x74, 0x3d, 0x56, 0x7e, 0x4a, 0x12, 0xc6, 0x71, 0x43, 0x59, 0x19, 0xb7, 0x25, 0xb0,
|
||||||
|
0x15, 0x8b, 0x3b, 0x29, 0x6b, 0x1a, 0x92, 0x6e, 0x2e, 0x11, 0x9c, 0xb2, 0x00, 0x65, 0x21, 0xca,
|
||||||
|
0x16, 0x28, 0x5b, 0xa2, 0x2c, 0x42, 0xd9, 0xaa, 0x93, 0xa6, 0x93, 0x9b, 0xd1, 0xa2, 0x48, 0xc8,
|
||||||
|
0xc3, 0x68, 0x37, 0x27, 0xdc, 0x8d, 0xc5, 0x9d, 0x2e, 0xa4, 0xe9, 0xb6, 0xa8, 0x6e, 0x38, 0x2b,
|
||||||
|
0xf7, 0x9d, 0xcd, 0x04, 0x34, 0x0e, 0x62, 0x9c, 0x12, 0x96, 0x12, 0x74, 0xb7, 0x4d, 0x51, 0xc5,
|
||||||
|
0x09, 0x58, 0xaf, 0xa8, 0xba, 0x89, 0xdb, 0x14, 0xac, 0x64, 0x75, 0x85, 0x13, 0x82, 0xde, 0xff,
|
||||||
|
0xf8, 0x0e, 0x9e, 0xdd, 0x5f, 0xc9, 0xbe, 0xcd, 0x31, 0x47, 0xef, 0x48, 0x99, 0x33, 0x04, 0x20,
|
||||||
|
0x9c, 0x30, 0xf4, 0x3d, 0x2b, 0xc1, 0xa9, 0x71, 0x8d, 0x7e, 0xa6, 0x5b, 0xa2, 0x96, 0x77, 0x04,
|
||||||
|
0x35, 0x20, 0x5a, 0x4e, 0x09, 0x07, 0x6f, 0x79, 0x40, 0x86, 0xd5, 0x66, 0xd8, 0x5f, 0x40, 0x8a,
|
||||||
|
0x53, 0x5d, 0xe0, 0x3c, 0xb7, 0xf6, 0x7c, 0xe3, 0xbf, 0x3e, 0xd5, 0x2d, 0x48, 0xdd, 0x56, 0x16,
|
||||||
|
0x74, 0x1d, 0xbd, 0x1e, 0x79, 0x84, 0xbf, 0xa9, 0x58, 0x4d, 0xe5, 0x46, 0x39, 0xc9, 0x61, 0xc9,
|
||||||
|
0x7b, 0xb2, 0xb9, 0x27, 0x5c, 0xf8, 0x66, 0xee, 0xe2, 0x9c, 0xee, 0xcb, 0x78, 0x8b, 0x6b, 0x22,
|
||||||
|
0xa6, 0x08, 0x6e, 0x60, 0xef, 0xa6, 0x61, 0x45, 0xec, 0x7a, 0x61, 0x24, 0xd6, 0x04, 0xde, 0x60,
|
||||||
|
0x7a, 0x18, 0x8a, 0x11, 0xf8, 0x4e, 0xae, 0xe3, 0x13, 0xa2, 0x2c, 0xcf, 0x71, 0x55, 0x93, 0x58,
|
||||||
|
0x3f, 0x6c, 0x06, 0xc4, 0x54, 0xdb, 0x10, 0xef, 0x22, 0x20, 0xfd, 0xd3, 0xb6, 0x05, 0xee, 0x25,
|
||||||
|
0xa2, 0x65, 0xd5, 0x36, 0x88, 0x55, 0xcd, 0x9e, 0xb3, 0xb6, 0x42, 0xb0, 0x3c, 0x49, 0x1a, 0x24,
|
||||||
|
0x48, 0x31, 0x27, 0x53, 0x7b, 0xd9, 0x9a, 0x1e, 0xc1, 0x94, 0x66, 0x4c, 0x0c, 0x8f, 0xcd, 0xbe,
|
||||||
|
0x79, 0xce, 0xb1, 0x37, 0x15, 0x4e, 0x53, 0x91, 0x49, 0x8c, 0x44, 0x4a, 0x84, 0x4e, 0x4a, 0xdb,
|
||||||
|
0x70, 0x08, 0x4f, 0x48, 0x27, 0x45, 0x5c, 0x32, 0xd0, 0xca, 0x87, 0xe6, 0x58, 0x91, 0x3f, 0x28,
|
||||||
|
0xba, 0x4f, 0x48, 0x8d, 0x38, 0xa9, 0x49, 0xa3, 0x07, 0xa0, 0xb6, 0x82, 0xc2, 0x48, 0x91, 0x18,
|
||||||
|
0x8f, 0x87, 0x9c, 0x41, 0x30, 0xb0, 0x4a, 0x48, 0xac, 0x30, 0x9b, 0x2d, 0x4e, 0xee, 0xc4, 0x8e,
|
||||||
|
0xcb, 0xb4, 0x57, 0x92, 0x5c, 0xa9, 0x82, 0x3d, 0x97, 0x8d, 0x8d, 0xa4, 0x05, 0xde, 0x13, 0xb5,
|
||||||
|
0x78, 0x2c, 0xa3, 0x79, 0xc7, 0x92, 0xb6, 0xe6, 0x20, 0x70, 0xc7, 0xda, 0x46, 0x6c, 0x2c, 0xc6,
|
||||||
|
0x6d, 0xc3, 0x7a, 0x24, 0x78, 0x1f, 0x2d, 0xef, 0xc1, 0x94, 0x69, 0x27, 0x73, 0x64, 0x86, 0x53,
|
||||||
|
0xf6, 0xa0, 0x66, 0x57, 0x9c, 0xed, 0x41, 0xd4, 0xba, 0x7b, 0xca, 0xe2, 0x71, 0xac, 0xa5, 0xa5,
|
||||||
|
0x65, 0x09, 0xb6, 0xab, 0x2b, 0x5a, 0xba, 0xbd, 0x4a, 0x06, 0x1c, 0x2c, 0x3a, 0xc6, 0x75, 0xbd,
|
||||||
|
0x5e, 0xa5, 0x14, 0xbd, 0x12, 0x60, 0xaf, 0x49, 0xf6, 0x69, 0x6e, 0xf7, 0x42, 0xa9, 0x3b, 0x4a,
|
||||||
|
0xf2, 0x74, 0xd3, 0x4b, 0xef, 0xb2, 0xdd, 0x0e, 0xf4, 0x17, 0xbb, 0x61, 0x75, 0xb0, 0x44, 0x50,
|
||||||
|
0x2c, 0xac, 0xd0, 0x9c, 0x63, 0xa6, 0xb4, 0x62, 0xe6, 0xec, 0x28, 0xe4, 0xe5, 0xb6, 0xca, 0x19,
|
||||||
|
0x4e, 0xdd, 0x8b, 0xfa, 0x17, 0x6e, 0x62, 0xc2, 0xbd, 0x6e, 0x0b, 0x70, 0x87, 0x63, 0x97, 0xd2,
|
||||||
|
0xba, 0xca, 0xf1, 0x31, 0xce, 0x69, 0x0d, 0x5a, 0x80, 0x84, 0x7e, 0xda, 0xe6, 0x2c, 0xb9, 0xfb,
|
||||||
|
0x7b, 0xcb, 0x1a, 0x82, 0xd2, 0x14, 0xa5, 0x39, 0xda, 0xd1, 0x7d, 0x0b, 0x71, 0x7f, 0x96, 0x80,
|
||||||
|
0x50, 0xc6, 0x51, 0x25, 0x52, 0x42, 0x67, 0x32, 0xa6, 0xdc, 0x27, 0xec, 0xed, 0x1c, 0x82, 0x72,
|
||||||
|
0xb2, 0x27, 0x65, 0xda, 0x0d, 0xae, 0x57, 0x90, 0xb2, 0x45, 0x90, 0xd1, 0xdb, 0xbc, 0x93, 0x8b,
|
||||||
|
0xd7, 0xcd, 0x31, 0x57, 0x1b, 0x9c, 0x73, 0x54, 0x13, 0x1d, 0x60, 0x50, 0xe1, 0xf8, 0xda, 0xa4,
|
||||||
|
0x27, 0x19, 0x4c, 0xb1, 0xf2, 0x05, 0xd8, 0x49, 0x42, 0x32, 0x99, 0xb3, 0x4c, 0x38, 0x9d, 0xa3,
|
||||||
|
0xfa, 0x00, 0x7b, 0x75, 0x9b, 0xe0, 0x05, 0xde, 0x6d, 0x18, 0x24, 0x1b, 0xda, 0x1c, 0xe3, 0x40,
|
||||||
|
0xb3, 0x9a, 0xe7, 0xf2, 0x12, 0x06, 0x1f, 0x38, 0xcb, 0x87, 0x58, 0xe9, 0x0d, 0x92, 0xb4, 0xbc,
|
||||||
|
0x06, 0xea, 0x8a, 0x51, 0x38, 0x97, 0xf9, 0x29, 0x06, 0x8d, 0x8b, 0x44, 0x92, 0x6a, 0x44, 0x4a,
|
||||||
|
0x76, 0xb8, 0xcd, 0x21, 0x51, 0xb7, 0x29, 0x65, 0x28, 0xc1, 0xe0, 0xcc, 0x35, 0x22, 0xc5, 0x96,
|
||||||
|
0xa4, 0x88, 0xee, 0x38, 0x2e, 0x08, 0xa2, 0xc5, 0x1e, 0xb1, 0xed, 0x67, 0x91, 0x24, 0xea, 0xfb,
|
||||||
|
0x3d, 0xba, 0xa7, 0x29, 0x61, 0xc6, 0x70, 0xd2, 0x5c, 0xd3, 0x9c, 0x56, 0xd0, 0x34, 0xcd, 0xc9,
|
||||||
|
0x49, 0x4c, 0x54, 0xd4, 0x96, 0xc7, 0x82, 0x76, 0x0f, 0xfa, 0x30, 0x83, 0xec, 0x71, 0x3a, 0xbb,
|
||||||
|
0x81, 0xc8, 0x5b, 0x44, 0x9f, 0xcd, 0x44, 0x26, 0x06, 0x03, 0xb8, 0x07, 0x30, 0xc6, 0x1c, 0xfc,
|
||||||
|
0xa8, 0xe1, 0x32, 0x96, 0x21, 0xc1, 0x92, 0x81, 0x74, 0x00, 0x19, 0x2a, 0xce, 0x1a, 0x18, 0xeb,
|
||||||
|
0x51, 0x7d, 0x47, 0x1e, 0x06, 0x72, 0x39, 0x32, 0x94, 0x35, 0x6c, 0x46, 0xf0, 0x0a, 0xec, 0xe1,
|
||||||
|
0x51, 0x0f, 0x2b, 0x5c, 0x02, 0xce, 0x19, 0x06, 0x47, 0x33, 0xa0, 0x25, 0x04, 0xd1, 0x23, 0x83,
|
||||||
|
0x24, 0xee, 0xe8, 0xa9, 0x60, 0x95, 0xdc, 0xad, 0x4b, 0x5c, 0x81, 0x8f, 0x71, 0x9a, 0x34, 0x25,
|
||||||
|
0xe4, 0x84, 0x18, 0x92, 0xc3, 0x81, 0x42, 0xe2, 0x3a, 0x2a, 0x2a, 0xd8, 0x16, 0x2d, 0x71, 0x6e,
|
||||||
|
0x26, 0xc1, 0x31, 0x95, 0x91, 0xd4, 0x7d, 0x24, 0x9c, 0x69, 0x58, 0xd9, 0x16, 0x10, 0x3a, 0x89,
|
||||||
|
0xab, 0xe2, 0x61, 0x0a, 0xed, 0x35, 0x72, 0x46, 0xcc, 0x71, 0x22, 0x8f, 0x9f, 0x1e, 0x2e, 0xd2,
|
||||||
|
0x18, 0x64, 0x1b, 0x11, 0xff, 0x36, 0x44, 0xa5, 0x84, 0xde, 0x2c, 0x3e, 0xdc, 0x31, 0xce, 0x50,
|
||||||
|
0xbd, 0xcf, 0xed, 0x76, 0x3b, 0x0b, 0xa7, 0x80, 0x7c, 0xbf, 0xc5, 0x57, 0xd1, 0x2d, 0x0a, 0x16,
|
||||||
|
0x3e, 0x0a, 0x97, 0x2b, 0xe4, 0x45, 0xd7, 0xe7, 0xf3, 0xfb, 0xc4, 0xe8, 0x3b, 0xbe, 0xf3, 0xca,
|
||||||
|
0x87, 0x1f, 0x8b, 0x62, 0x1e, 0xf5, 0x1c, 0x54, 0x2d, 0x4c, 0xd2, 0x29, 0x76, 0x9b, 0xb7, 0x5c,
|
||||||
|
0xef, 0x6b, 0xcb, 0x85, 0xcb, 0x49, 0x5d, 0xf7, 0x10, 0x71, 0x33, 0xe5, 0xb8, 0x36, 0x3b, 0xdf,
|
||||||
|
0x73, 0x7c, 0x94, 0x86, 0xd5, 0x80, 0xac, 0x25, 0xda, 0x45, 0x7a, 0x08, 0xa4, 0x76, 0xf0, 0x6c,
|
||||||
|
0x63, 0x15, 0xdc, 0xb4, 0xdc, 0xc2, 0xd6, 0xa4, 0xa2, 0x58, 0x0f, 0x52, 0x0e, 0x97, 0xb4, 0x5e,
|
||||||
|
0x68, 0x2d, 0x02, 0x1c, 0x2a, 0x12, 0x3c, 0x92, 0xcb, 0x00, 0xcf, 0x04, 0x34, 0xa8, 0xa9, 0xa4,
|
||||||
|
0x06, 0x71, 0x26, 0xb2, 0xc1, 0x9c, 0xcb, 0x6e, 0x50, 0xe3, 0x4d, 0x18, 0xb0, 0xce, 0x1b, 0x53,
|
||||||
|
0xf8, 0x74, 0x97, 0x03, 0x42, 0x6d, 0xd7, 0xca, 0xff, 0x1a, 0xf5, 0x35, 0x66, 0xbf, 0xc6, 0xec,
|
||||||
|
0xd7, 0x98, 0xfd, 0xdf, 0x8e, 0xd9, 0xaf, 0xb1, 0xfa, 0x35, 0x56, 0xbf, 0xc6, 0xea, 0xff, 0x43,
|
||||||
|
0xac, 0x7a, 0x42, 0x3c, 0x0c, 0x95, 0x1a, 0xef, 0xac, 0x7b, 0xf2, 0x9f, 0x0a, 0x92, 0x52, 0xec,
|
||||||
|
0x5c, 0x15, 0x50, 0x07, 0x2a, 0xf0, 0x6a, 0x09, 0x9e, 0x74, 0xdd, 0x59, 0xe4, 0xc3, 0xd5, 0x5a,
|
||||||
|
0xe2, 0x4e, 0x33, 0x93, 0xd6, 0xab, 0x9b, 0x27, 0x27, 0x49, 0xdc, 0xdc, 0xa4, 0xc0, 0x0f, 0x97,
|
||||||
|
0x4f, 0xce, 0x52, 0xc8, 0xd9, 0x69, 0xe1, 0xcd, 0xd3, 0x12, 0x2a, 0xe4, 0xec, 0xb4, 0x68, 0xb1,
|
||||||
|
0x7a, 0x7a, 0x9a, 0x44, 0x9e, 0x4e, 0x9e, 0xee, 0xa7, 0x74, 0x67, 0x1d, 0x96, 0x93, 0x57, 0xb8,
|
||||||
|
0xa2, 0xd2, 0xd0, 0x55, 0xa0, 0xac, 0x93, 0x3d, 0xb7, 0x70, 0x97, 0x1a, 0xe2, 0x06, 0x1c, 0x2a,
|
||||||
|
0x4d, 0xaf, 0x38, 0xd8, 0x74, 0x6e, 0x4e, 0x76, 0xa6, 0x44, 0x91, 0x00, 0x3e, 0x94, 0xd9, 0x82,
|
||||||
|
0x76, 0x35, 0x22, 0x0c, 0xbc, 0x08, 0x98, 0x8c, 0x49, 0x15, 0x0c, 0x88, 0x8f, 0x03, 0x71, 0xdf,
|
||||||
|
0xd2, 0x19, 0x93, 0x8b, 0xae, 0x8e, 0x21, 0x3e, 0x18, 0xc1, 0x7a, 0xce, 0xe7, 0x7c, 0x15, 0x61,
|
||||||
|
0xe3, 0x06, 0x7e, 0x67, 0x71, 0x08, 0x35, 0x87, 0x7c, 0x86, 0xc3, 0xc9, 0xdb, 0xe5, 0xe4, 0x60,
|
||||||
|
0xea, 0x34, 0x31, 0x38, 0x79, 0x99, 0x48, 0x97, 0x84, 0x98, 0x16, 0x02, 0x78, 0xd6, 0x7d, 0x26,
|
||||||
|
0xc0, 0xc0, 0xb8, 0x07, 0x69, 0xa6, 0x19, 0x6c, 0x61, 0xe8, 0x6b, 0x4a, 0xd0, 0x83, 0xbb, 0x0e,
|
||||||
|
0xb5, 0x4b, 0xde, 0xf4, 0x90, 0x95, 0x06, 0x18, 0x9a, 0xe0, 0xe3, 0x5b, 0x4d, 0x15, 0x81, 0xdf,
|
||||||
|
0x7a, 0xd2, 0xb0, 0xee, 0xae, 0xcd, 0xf3, 0xce, 0xf6, 0x28, 0x89, 0x12, 0x66, 0x75, 0xd7, 0x87,
|
||||||
|
0xdc, 0x32, 0xf0, 0x8d, 0x6f, 0x84, 0x87, 0x70, 0x64, 0x0f, 0x9d, 0x79, 0x82, 0x0a, 0xd7, 0x93,
|
||||||
|
0x4d, 0x30, 0x65, 0x35, 0xf5, 0x08, 0x7b, 0x63, 0xad, 0x36, 0x92, 0x24, 0x85, 0xdc, 0xa5, 0xe6,
|
||||||
|
0xa4, 0x94, 0x13, 0x95, 0x9c, 0x01, 0xd4, 0x16, 0x65, 0x8f, 0x7f, 0xe0, 0xb8, 0xea, 0xcc, 0x53,
|
||||||
|
0x2c, 0xfe, 0x3b, 0x79, 0xa2, 0xf5, 0x50, 0xbb, 0x09, 0x11, 0x35, 0x72, 0x27, 0x8b, 0x58, 0xd9,
|
||||||
|
0x8d, 0xa8, 0x63, 0x05, 0x3a, 0x79, 0xa2, 0x65, 0x4d, 0x77, 0x47, 0x38, 0x62, 0x30, 0x6f, 0x3a,
|
||||||
|
0x3d, 0xd2, 0xcd, 0x6e, 0xc9, 0x4e, 0xa2, 0x4e, 0x5e, 0x4a, 0x45, 0xcd, 0xeb, 0x1e, 0xff, 0x18,
|
||||||
|
0x97, 0xac, 0xb9, 0xfa, 0x90, 0x41, 0x2d, 0x4c, 0xca, 0x4f, 0xd7, 0xff, 0x1c, 0x0f, 0xd5, 0x31,
|
||||||
|
0xac, 0x69, 0x5d, 0x4e, 0x20, 0x87, 0xd4, 0xe2, 0xe8, 0x34, 0xed, 0x75, 0xe1, 0x36, 0xbd, 0x4e,
|
||||||
|
0x20, 0x23, 0x25, 0x57, 0x41, 0x75, 0x78, 0x73, 0x8f, 0xf9, 0xd5, 0xec, 0xc4, 0xeb, 0xeb, 0xcd,
|
||||||
|
0x59, 0xaf, 0xd8, 0xcc, 0xba, 0x0a, 0x1c, 0xd7, 0x79, 0x66, 0xea, 0xb5, 0x11, 0x5a, 0xe4, 0x3f,
|
||||||
|
0x37, 0xf4, 0xfd, 0x2f, 0x90, 0xdd, 0xb4, 0x1f, 0xc6, 0x1d, 0x48, 0x38, 0xb4, 0xae, 0xc2, 0xf0,
|
||||||
|
0xd6, 0x09, 0x17, 0x01, 0xfc, 0x46, 0x6f, 0xa7, 0xcb, 0xf7, 0xd3, 0xc4, 0xda, 0x0f, 0x19, 0xa8,
|
||||||
|
0x5a, 0xf6, 0x5c, 0xdd, 0x92, 0x49, 0xeb, 0x48, 0x88, 0x3c, 0x72, 0x45, 0x47, 0x46, 0x59, 0x48,
|
||||||
|
0x36, 0xe7, 0xe0, 0xc8, 0xce, 0xf7, 0xba, 0x07, 0xca, 0x71, 0x4a, 0xdb, 0x3a, 0xee, 0x1d, 0x4f,
|
||||||
|
0xe3, 0xa5, 0xa3, 0x8d, 0x29, 0x6e, 0xe1, 0x07, 0x72, 0x86, 0x21, 0x29, 0xd2, 0x29, 0x8b, 0xc5,
|
||||||
|
0x7a, 0xc4, 0x64, 0x8a, 0x0e, 0x15, 0x56, 0x41, 0x35, 0xd2, 0x34, 0xe4, 0x01, 0xb1, 0x77, 0xa5,
|
||||||
|
0xc8, 0xfd, 0xdd, 0x6a, 0x6f, 0x2b, 0x65, 0xda, 0x75, 0x94, 0x8a, 0x89, 0x22, 0xa7, 0xff, 0x1d,
|
||||||
|
0x14, 0x33, 0x4c, 0x13, 0x4a, 0x81, 0x91, 0x34, 0x06, 0x04, 0xca, 0x8b, 0xb9, 0x2e, 0x17, 0x4e,
|
||||||
|
0xb8, 0x5c, 0xc2, 0xef, 0xea, 0x19, 0xae, 0x70, 0xac, 0xbb, 0xd1, 0x17, 0x30, 0x5d, 0xdc, 0x3a,
|
||||||
|
0xab, 0x1b, 0xf8, 0x77, 0x49, 0xd0, 0xf0, 0x0b, 0x78, 0xce, 0xf9, 0xc5, 0x19, 0x57, 0x38, 0xd4,
|
||||||
|
0xc9, 0x97, 0x48, 0x1a, 0xdd, 0x3a, 0x70, 0x43, 0x7a, 0x6e, 0xf7, 0x95, 0x1b, 0x9a, 0x06, 0x60,
|
||||||
|
0xef, 0x35, 0x95, 0x1b, 0x0c, 0xa0, 0x50, 0xc3, 0x96, 0x06, 0xa6, 0x32, 0x69, 0x75, 0x74, 0x6f,
|
||||||
|
0x34, 0x48, 0x27, 0xf4, 0x50, 0x64, 0x69, 0x0d, 0x93, 0xd9, 0x58, 0x91, 0x8a, 0x73, 0x42, 0x83,
|
||||||
|
0xed, 0x83, 0x42, 0xc3, 0xc6, 0x27, 0x05, 0x90, 0x47, 0x53, 0xf2, 0x70, 0x96, 0x5e, 0x0b, 0x07,
|
||||||
|
0x9a, 0x5e, 0x4e, 0x65, 0x59, 0x4d, 0x65, 0x59, 0x69, 0xe6, 0xfe, 0x98, 0xb9, 0x3f, 0x61, 0xeb,
|
||||||
|
0x4b, 0x86, 0xfe, 0x94, 0x9f, 0x3f, 0x62, 0xa6, 0x88, 0x16, 0x53, 0x22, 0x6f, 0x1d, 0x4d, 0x97,
|
||||||
|
0xf5, 0xd6, 0x66, 0x57, 0x8b, 0xf1, 0xc2, 0x13, 0x62, 0xb5, 0xba, 0x21, 0x3f, 0x0e, 0x56, 0x30,
|
||||||
|
0xdc, 0xc3, 0x73, 0xee, 0xa1, 0xe1, 0x1e, 0x4c, 0xb8, 0xcf, 0xa9, 0xcc, 0xd6, 0xd8, 0x19, 0xf7,
|
||||||
|
0x73, 0xe6, 0x86, 0xf7, 0x72, 0x62, 0x8f, 0x19, 0x63, 0x48, 0x52, 0xf9, 0xb6, 0x42, 0x90, 0xa8,
|
||||||
|
0xf7, 0x16, 0xaa, 0xf3, 0x29, 0xc6, 0x3d, 0xaa, 0x3f, 0x4c, 0x2c, 0xa4, 0x3e, 0x4b, 0x24, 0x48,
|
||||||
|
0xb2, 0xb2, 0xb1, 0x12, 0xd0, 0x23, 0xc3, 0x83, 0xfd, 0xae, 0xa9, 0xf7, 0x20, 0xfb, 0x9d, 0x4a,
|
||||||
|
0x68, 0x89, 0xb0, 0x1f, 0x91, 0x06, 0xe1, 0x19, 0x71, 0xa0, 0x15, 0x2d, 0xe9, 0x0f, 0xb5, 0x45,
|
||||||
|
0xdf, 0xdb, 0x65, 0x44, 0x3d, 0x90, 0x2e, 0x26, 0x62, 0xdc, 0x9c, 0x53, 0x87, 0x5a, 0xcd, 0xea,
|
||||||
|
0xfd, 0x6e, 0x61, 0xf3, 0x9e, 0x21, 0x37, 0x7e, 0x2c, 0xc9, 0xc4, 0xfb, 0xbd, 0xd1, 0x0b, 0xbf,
|
||||||
|
0xb5, 0xef, 0xf7, 0xa8, 0x92, 0xf1, 0x02, 0xe7, 0x23, 0xe4, 0xd2, 0x20, 0xc5, 0x95, 0xb1, 0x2d,
|
||||||
|
0x46, 0xc8, 0xc8, 0x20, 0x73, 0xa9, 0x58, 0x1b, 0xb7, 0x10, 0xb8, 0xb6, 0xaa, 0x08, 0x4f, 0x70,
|
||||||
|
0x4d, 0xa6, 0xaf, 0x99, 0x0c, 0xe2, 0xe4, 0xe5, 0x04, 0x2b, 0x33, 0xc3, 0x4d, 0xf2, 0x00, 0xc7,
|
||||||
|
0xc0, 0x58, 0xf2, 0x55, 0x18, 0x0d, 0x24, 0x51, 0x37, 0xbb, 0x2d, 0xc9, 0x5b, 0xe6, 0xc3, 0x95,
|
||||||
|
0xce, 0x5c, 0x12, 0x34, 0xe4, 0xae, 0x21, 0x61, 0xad, 0x23, 0xe7, 0x26, 0x72, 0x6e, 0x6f, 0x87,
|
||||||
|
0x74, 0x65, 0x93, 0x8a, 0x84, 0x35, 0x70, 0xbb, 0xb9, 0xc8, 0x0d, 0xf2, 0xe9, 0x32, 0x70, 0xec,
|
||||||
|
0x03, 0x65, 0x96, 0x9b, 0x75, 0x4e, 0x3d, 0xc9, 0x6a, 0xf6, 0x78, 0x7a, 0x5a, 0xb6, 0xdb, 0x8b,
|
||||||
|
0xb2, 0x05, 0x6b, 0x48, 0xcb, 0xce, 0xe2, 0x45, 0x3b, 0x8d, 0x2e, 0x73, 0xf3, 0xd7, 0x4e, 0x10,
|
||||||
|
0x2c, 0x1d, 0xa8, 0x20, 0x9e, 0x66, 0x68, 0xbd, 0x2f, 0x31, 0x7c, 0x67, 0xde, 0xc5, 0xa8, 0xa6,
|
||||||
|
0x82, 0x45, 0xfb, 0xe2, 0x05, 0x67, 0x26, 0x3d, 0xbd, 0xee, 0x6f, 0xba, 0xe4, 0x8e, 0xe6, 0x22,
|
||||||
|
0xd1, 0xa8, 0x3f, 0xf1, 0x70, 0xf6, 0x41, 0x51, 0x7c, 0x3d, 0x5c, 0xfb, 0x86, 0x72, 0xd8, 0x02,
|
||||||
|
0xea, 0x42, 0xd8, 0x02, 0x99, 0x12, 0xd8, 0x82, 0x0d, 0xc5, 0xaf, 0x05, 0x54, 0x65, 0xaf, 0x05,
|
||||||
|
0xd0, 0x85, 0xad, 0x0d, 0x12, 0x25, 0xad, 0x35, 0xb6, 0x8a, 0x78, 0x61, 0x74, 0x11, 0x83, 0xb2,
|
||||||
|
0x76, 0x53, 0xc7, 0x78, 0xdd, 0x59, 0x90, 0xb4, 0xed, 0xbf, 0x10, 0xf0, 0x82, 0xa8, 0xde, 0x58,
|
||||||
|
0x88, 0x0a, 0x78, 0xc0, 0xc2, 0xc7, 0x58, 0xce, 0x41, 0xd3, 0xbb, 0x00, 0xb2, 0xaf, 0xa1, 0x08,
|
||||||
|
0x94, 0x92, 0xcb, 0x2f, 0x0b, 0xee, 0x08, 0x7a, 0xe2, 0x63, 0x08, 0x45, 0xf9, 0x6f, 0xaf, 0x30,
|
||||||
|
0xcb, 0xcd, 0x5e, 0xf7, 0xb7, 0xe3, 0xfc, 0xf2, 0x1d, 0x35, 0xb4, 0x10, 0x49, 0x6b, 0xd7, 0x96,
|
||||||
|
0x7d, 0x41, 0xd4, 0x6e, 0x69, 0xe2, 0x6e, 0xc9, 0x23, 0x25, 0xfc, 0xca, 0x5b, 0x22, 0x1f, 0x79,
|
||||||
|
0x21, 0x0a, 0x44, 0x1d, 0xd0, 0x6b, 0x5d, 0x5e, 0xe7, 0x66, 0xad, 0x11, 0xd6, 0x50, 0x1b, 0x32,
|
||||||
|
0x30, 0xfd, 0xc7, 0xd8, 0xba, 0xa3, 0xc6, 0x12, 0xf4, 0x5f, 0xbc, 0xa9, 0x0e, 0x4b, 0xc8, 0x7b,
|
||||||
|
0xe0, 0xfa, 0x8b, 0x97, 0xb8, 0x75, 0xd6, 0x37, 0x4e, 0x18, 0x5c, 0x5a, 0x60, 0x94, 0x62, 0xed,
|
||||||
|
0x25, 0xfe, 0x83, 0x44, 0x2b, 0x3f, 0x18, 0xf8, 0x18, 0x3f, 0x82, 0x62, 0x62, 0xf9, 0xdc, 0x3d,
|
||||||
|
0xca, 0x8f, 0x30, 0x0e, 0x50, 0xf6, 0x1a, 0xac, 0xae, 0xf3, 0xc4, 0xee, 0xe0, 0x80, 0xea, 0x09,
|
||||||
|
0xed, 0x26, 0xec, 0x33, 0x95, 0x55, 0xb0, 0x84, 0xf4, 0x70, 0x0b, 0x59, 0x35, 0x5a, 0x58, 0xfb,
|
||||||
|
0x1b, 0x4d, 0xb3, 0x04, 0xd1, 0x2f, 0xfe, 0xc5, 0xcb, 0xed, 0x7e, 0x1d, 0xfd, 0x25, 0x43, 0x58,
|
||||||
|
0x1d, 0xfa, 0x2f, 0xbd, 0xec, 0xaf, 0x21, 0x26, 0x5f, 0x0a, 0x88, 0x0f, 0x05, 0x74, 0x9b, 0xa6,
|
||||||
|
0xe2, 0x64, 0x07, 0x55, 0xa3, 0x92, 0xc5, 0xad, 0x93, 0x8c, 0x14, 0x24, 0x4e, 0x31, 0xbf, 0xbb,
|
||||||
|
0xee, 0x3c, 0xf1, 0x67, 0xf0, 0x8a, 0x9b, 0x97, 0x5f, 0xdd, 0xcf, 0x55, 0x3a, 0xb6, 0xd5, 0x98,
|
||||||
|
0xf1, 0xea, 0xe5, 0x8c, 0xcf, 0x8f, 0xd8, 0x39, 0xc6, 0x83, 0x0f, 0x2c, 0x2e, 0x1e, 0x3e, 0xa1,
|
||||||
|
0x0f, 0xb5, 0x4b, 0x00, 0xee, 0x1b, 0x3c, 0x73, 0x9a, 0x4d, 0xd9, 0x06, 0x97, 0xd9, 0xce, 0x45,
|
||||||
|
0xc4, 0x25, 0xb6, 0xcb, 0xcb, 0x47, 0x65, 0xb4, 0x72, 0x82, 0xd5, 0xc2, 0x09, 0xd6, 0xd1, 0x25,
|
||||||
|
0xb6, 0x93, 0x98, 0x5e, 0x7d, 0x69, 0xc0, 0xbd, 0x4c, 0xd5, 0x76, 0xd4, 0xc9, 0xab, 0xc8, 0x8b,
|
||||||
|
0x42, 0xee, 0x25, 0x17, 0x92, 0x67, 0x1a, 0xa4, 0x75, 0xf1, 0x31, 0x96, 0x7d, 0xa9, 0x65, 0xdf,
|
||||||
|
0x97, 0x0a, 0xa3, 0xd7, 0x27, 0x09, 0x95, 0x7d, 0x1b, 0xd3, 0x64, 0x1a, 0x1a, 0x46, 0x00, 0x52,
|
||||||
|
0x04, 0xba, 0xd1, 0x83, 0xe5, 0x76, 0xcf, 0xfa, 0x3e, 0xaa, 0x6b, 0xa1, 0x90, 0x6a, 0x82, 0xa8,
|
||||||
|
0x65, 0xce, 0xea, 0xa9, 0xf0, 0xbc, 0xde, 0xd0, 0x6d, 0x36, 0x39, 0xe7, 0x60, 0xcf, 0x91, 0x55,
|
||||||
|
0x47, 0x38, 0x57, 0x34, 0xda, 0x73, 0xe4, 0xee, 0x97, 0xa3, 0xfb, 0x79, 0x7f, 0x15, 0x9f, 0x5c,
|
||||||
|
0xcf, 0x27, 0x53, 0xe6, 0x6e, 0xe8, 0x0a, 0x6d, 0x6e, 0xb6, 0x68, 0x44, 0x3d, 0x77, 0xcd, 0x7d,
|
||||||
|
0xa6, 0xaf, 0x5c, 0xa4, 0x4a, 0x07, 0x67, 0x25, 0xd7, 0x62, 0xaa, 0x81, 0xc5, 0x13, 0x9c, 0x4c,
|
||||||
|
0xb3, 0x39, 0xdf, 0x2b, 0xd5, 0x84, 0x63, 0xd5, 0x2c, 0xce, 0x15, 0xa3, 0x58, 0x7d, 0x23, 0x3b,
|
||||||
|
0x82, 0x5e, 0x7d, 0x4f, 0xe0, 0x22, 0xe4, 0xa6, 0x35, 0xde, 0xad, 0x9b, 0xf1, 0xc8, 0xee, 0xa7,
|
||||||
|
0x7b, 0xb4, 0xfe, 0x3e, 0xa7, 0xc9, 0xdd, 0xa5, 0x39, 0x08, 0x08, 0xdf, 0x33, 0x7e, 0x99, 0xf7,
|
||||||
|
0xe4, 0x43, 0x9b, 0x86, 0x8f, 0xf1, 0x4e, 0x93, 0x39, 0xea, 0x3b, 0xb7, 0xa7, 0x65, 0xfa, 0xa6,
|
||||||
|
0xc0, 0xb4, 0xd4, 0xe8, 0x6c, 0xef, 0xb3, 0xdd, 0x7a, 0xdc, 0x89, 0xc8, 0x82, 0x09, 0xf6, 0xbc,
|
||||||
|
0xba, 0xfc, 0x66, 0xa0, 0x09, 0xf0, 0x22, 0x49, 0xf8, 0xb1, 0x33, 0xef, 0x96, 0x7c, 0x7f, 0xe9,
|
||||||
|
0x5b, 0x5f, 0xef, 0x2d, 0x41, 0x91, 0x76, 0x9d, 0x04, 0x32, 0x6c, 0x9e, 0x2c, 0x8e, 0xfe, 0x05,
|
||||||
|
0xb4, 0x46, 0x69, 0xaa, 0x6b, 0x2c, 0x00, 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
void serveBundleCss(AsyncWebServerRequest* request) {
|
void serveBundleCss(AsyncWebServerRequest* request) {
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Binary array for the Web UI.
|
* Binary array for the Web UI.
|
||||||
* gzip is used for smaller size and improved speeds.
|
* Gzip is used for smaller size and improved speeds.
|
||||||
*
|
|
||||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
|
||||||
* to find out how to easily modify the web UI source!
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Autogenerated do not edit!!
|
// Autogenerated do not edit!!
|
||||||
|
|
1843
src/ui_index_js.h
1843
src/ui_index_js.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -14,17 +14,26 @@
|
||||||
"@rollup/plugin-image": "^2.1.1",
|
"@rollup/plugin-image": "^2.1.1",
|
||||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||||
|
"autoprefixer": "^10.4.8",
|
||||||
"concurrently": "^7.3.0",
|
"concurrently": "^7.3.0",
|
||||||
|
"cssnano": "^5.1.12",
|
||||||
"mime": "^3.0.0",
|
"mime": "^3.0.0",
|
||||||
"pascal-case": "^3.1.2",
|
"pascal-case": "^3.1.2",
|
||||||
|
"postcss": "^8.4.14",
|
||||||
|
"postcss-load-config": "^4.0.1",
|
||||||
"rollup": "^2.77.2",
|
"rollup": "^2.77.2",
|
||||||
"rollup-plugin-css-only": "^3.1.0",
|
"rollup-plugin-css-only": "^3.1.0",
|
||||||
"rollup-plugin-generate-html-template": "^1.7.0",
|
"rollup-plugin-generate-html-template": "^1.7.0",
|
||||||
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
"rollup-plugin-svelte": "^7.1.0",
|
"rollup-plugin-svelte": "^7.1.0",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"svelte": "^3.49.0",
|
"svelte": "^3.49.0",
|
||||||
|
"svelte-preprocess": "^4.10.7",
|
||||||
|
"tailwindcss": "^3.1.7",
|
||||||
"vite": "^3.0.0"
|
"vite": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"svelte-spa-router": "^3.2.0",
|
||||||
"svelte-table": "^0.5.0"
|
"svelte-table": "^0.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
cssnano: {
|
||||||
|
preset: 'default',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
import svelte from 'rollup-plugin-svelte';
|
import svelte from 'rollup-plugin-svelte';
|
||||||
import css from 'rollup-plugin-css-only';
|
|
||||||
import image from '@rollup/plugin-image';
|
import image from '@rollup/plugin-image';
|
||||||
import resolve from '@rollup/plugin-node-resolve';
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import { terser } from "rollup-plugin-terser";
|
||||||
|
import postcss from 'rollup-plugin-postcss'
|
||||||
import htmlTemplate from 'rollup-plugin-generate-html-template';
|
import htmlTemplate from 'rollup-plugin-generate-html-template';
|
||||||
import { readFile, writeFile } from 'fs';
|
import { readFile, writeFile } from 'fs';
|
||||||
import { basename } from 'path';
|
import { basename } from 'path';
|
||||||
|
@ -41,10 +42,7 @@ function cppGzipped(html, fileName, contentType) {
|
||||||
const array = hexdump(result);
|
const array = hexdump(result);
|
||||||
const src = `/*
|
const src = `/*
|
||||||
* Binary array for the Web UI.
|
* Binary array for the Web UI.
|
||||||
* gzip is used for smaller size and improved speeds.
|
* Gzip is used for smaller size and improved speeds.
|
||||||
*
|
|
||||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
|
||||||
* to find out how to easily modify the web UI source!
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Autogenerated do not edit!!
|
// Autogenerated do not edit!!
|
||||||
|
@ -59,7 +57,6 @@ function cppGzipped(html, fileName, contentType) {
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
resolve(src);
|
resolve(src);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -176,15 +173,15 @@ export default {
|
||||||
svelte({}),
|
svelte({}),
|
||||||
resolve(),
|
resolve(),
|
||||||
image(),
|
image(),
|
||||||
css({ output: 'bundle.css' }),
|
postcss({ extract: 'bundle.css' }),
|
||||||
htmlTemplate({
|
htmlTemplate({
|
||||||
template: 'src/template.html',
|
template: 'src/template.html',
|
||||||
target: 'index.html',
|
target: 'index.html',
|
||||||
}),
|
}),
|
||||||
|
terser(),
|
||||||
cpp({
|
cpp({
|
||||||
additionalFiles: ['dist/index.html'],
|
additionalFiles: ['dist/index.html'],
|
||||||
fileName: function(a) { return "../src/ui_" + basename(a).replace(".", "_") + ".h"; }
|
fileName: function(a) { return "../src/ui_" + basename(a).replace(".", "_") + ".h"; }
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,125 +1,16 @@
|
||||||
<script>
|
<script>
|
||||||
import { devices } from './stores';
|
import Router from "svelte-spa-router";
|
||||||
import SvelteTable from "svelte-table";
|
import Sidebar from "./components/Sidebar.svelte";
|
||||||
|
import routes from "./routes";
|
||||||
var filterSelections = { };
|
|
||||||
var sortBy = "distance"
|
|
||||||
var sortOrder = 1;
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
key: "distance",
|
|
||||||
title: "Distance",
|
|
||||||
value: v => v.distance,
|
|
||||||
renderValue: v => `${v.distance.toLocaleString(undefined, { minimumFractionDigits: 2 })}m`,
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "id",
|
|
||||||
title: "ID",
|
|
||||||
value: v => v.id,
|
|
||||||
sortable: true,
|
|
||||||
filterOptions: rows => {
|
|
||||||
const prefixes = new Set()
|
|
||||||
rows.forEach(row => {
|
|
||||||
var prefix = row.id.substring(0, row.id.indexOf(":")+1);
|
|
||||||
if (prefix.length > 0) {
|
|
||||||
prefixes.add(prefix);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return Array.from(prefixes).sort().map(a=>({"name": a, "value": a}));
|
|
||||||
},
|
|
||||||
filterValue: v => v.id.substring(0, v.id.indexOf(":")+1),
|
|
||||||
headerClass: "text-left",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "name",
|
|
||||||
title: "Name",
|
|
||||||
value: v => v.name ?? "",
|
|
||||||
sortable: true,
|
|
||||||
filterOptions: rows => {
|
|
||||||
let letrs = {};
|
|
||||||
rows.forEach(row => {
|
|
||||||
let letr = row.name?.charAt(0);
|
|
||||||
if (letr && letrs[letr] === undefined)
|
|
||||||
letrs[letr] = {
|
|
||||||
name: `${letr.toUpperCase()}`,
|
|
||||||
value: letr.toLowerCase(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
// fix order
|
|
||||||
letrs = Object.entries(letrs)
|
|
||||||
.sort()
|
|
||||||
.reduce((o, [k, v]) => ((o[k] = v), o), {});
|
|
||||||
return Object.values(letrs);
|
|
||||||
},
|
|
||||||
filterValue: v => v.name?.charAt(0).toLowerCase(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "mac",
|
|
||||||
title: "MAC",
|
|
||||||
value: v => v.mac,
|
|
||||||
sortable: true,
|
|
||||||
filterOptions: rows => {
|
|
||||||
// use first letter of last_name to generate filter
|
|
||||||
let letrs = {};
|
|
||||||
rows.forEach(row => {
|
|
||||||
let letr = row.mac.charAt(0);
|
|
||||||
if (letrs[letr] === undefined)
|
|
||||||
letrs[letr] = {
|
|
||||||
name: `${letr.toUpperCase()}`,
|
|
||||||
value: letr.toLowerCase(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
// fix order
|
|
||||||
letrs = Object.entries(letrs)
|
|
||||||
.sort()
|
|
||||||
.reduce((o, [k, v]) => ((o[k] = v), o), {});
|
|
||||||
return Object.values(letrs);
|
|
||||||
},
|
|
||||||
filterValue: v => v.mac.charAt(0).toLowerCase(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "rssi",
|
|
||||||
title: "Rssi",
|
|
||||||
value: v => v.rssi,
|
|
||||||
renderValue: v => v.rssi + "dBm",
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "rssi@1m",
|
|
||||||
title: "Rssi@1m",
|
|
||||||
value: v => v["rssi@1m"],
|
|
||||||
renderValue: v => v["rssi@1m"] + "dBm",
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "interval",
|
|
||||||
title: "Interval",
|
|
||||||
value: v => v.interval,
|
|
||||||
renderValue: v => v.interval + "ms",
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<div>
|
||||||
|
<div class="flex">
|
||||||
{#if $devices?.devices != null }
|
<nav>
|
||||||
<SvelteTable columns="{columns}" rows="{$devices.devices}"
|
<Sidebar />
|
||||||
bind:filterSelections="{filterSelections}"
|
</nav>
|
||||||
bind:sortBy = "{sortBy}"
|
<main class="max-w-7xl mx-auto py-8 sm:py-10 md:py-12 sm:px-10 lg:px-12">
|
||||||
bind:sortOrder = "{sortOrder}"
|
<Router {routes} />
|
||||||
></SvelteTable>
|
</main>
|
||||||
{:else}
|
</div>
|
||||||
<h1>Error while loading devices</h1>
|
</div>
|
||||||
{/if}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
main {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,15 +1,7 @@
|
||||||
:root {
|
/* Only apply purgecss on utilities, per the Tailwind documentation. */
|
||||||
color-scheme: light dark;
|
/* purgecss start ignore */
|
||||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
@tailwind base;
|
||||||
font-size: 16px;
|
@tailwind components;
|
||||||
line-height: 24px;
|
/* purgecss end ignore */
|
||||||
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
@tailwind utilities;
|
||||||
background-color: #242424;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script>
|
||||||
|
export let name;
|
||||||
|
export let width = "1rem";
|
||||||
|
export let height = "1rem";
|
||||||
|
export let focusable = false;
|
||||||
|
let icons = [
|
||||||
|
{
|
||||||
|
w: 2048,
|
||||||
|
h: 1792,
|
||||||
|
name: "contact",
|
||||||
|
svg: `<path d="M1024 1131q0-64-9-117.5t-29.5-103-60.5-78-97-28.5q-6 4-30 18t-37.5 21.5-35.5 17.5-43 14.5-42 4.5-42-4.5-43-14.5-35.5-17.5-37.5-21.5-30-18q-57 0-97 28.5t-60.5 78-29.5 103-9 117.5 37 106.5 91 42.5h512q54 0 91-42.5t37-106.5zm-157-520q0-94-66.5-160.5t-160.5-66.5-160.5 66.5-66.5 160.5 66.5 160.5 160.5 66.5 160.5-66.5 66.5-160.5zm925 509v-64q0-14-9-23t-23-9h-576q-14 0-23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23-9t9-23zm0-260v-56q0-15-10.5-25.5t-25.5-10.5h-568q-15 0-25.5 10.5t-10.5 25.5v56q0 15 10.5 25.5t25.5 10.5h568q15 0 25.5-10.5t10.5-25.5zm0-252v-64q0-14-9-23t-23-9h-576q-14 0-23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23-9t9-23zm256-320v1216q0 66-47 113t-113 47h-352v-96q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v96h-768v-96q0-14-9-23t-23-9h-64q-14 0-23 9t-9 23v96h-352q-66 0-113-47t-47-113v-1216q0-66 47-113t113-47h1728q66 0 113 47t47 113z"/>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
w: 32,
|
||||||
|
h: 32,
|
||||||
|
name: "trash",
|
||||||
|
svg: `<path d="M12 12h2v12h-2z" /><path d="M18 12h2v12h-2z" /><path d="M4 6v2h2v20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8h2V6zm4 22V8h16v20z" /><path d="M12 2h8v2h-8z" />`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "device",
|
||||||
|
w: 2048,
|
||||||
|
h: 1792,
|
||||||
|
svg: `<path d="M685 483q16 0 27.5-11.5t11.5-27.5-11.5-27.5-27.5-11.5-27 11.5-11 27.5 11 27.5 27 11.5zm422 0q16 0 27-11.5t11-27.5-11-27.5-27-11.5-27.5 11.5-11.5 27.5 11.5 27.5 27.5 11.5zm-812 184q42 0 72 30t30 72v430q0 43-29.5 73t-72.5 30-73-30-30-73v-430q0-42 30-72t73-30zm1060 19v666q0 46-32 78t-77 32h-75v227q0 43-30 73t-73 30-73-30-30-73v-227h-138v227q0 43-30 73t-73 30q-42 0-72-30t-30-73l-1-227h-74q-46 0-78-32t-32-78v-666h918zm-232-405q107 55 171 153.5t64 215.5h-925q0-117 64-215.5t172-153.5l-71-131q-7-13 5-20 13-6 20 6l72 132q95-42 201-42t201 42l72-132q7-12 20-6 12 7 5 20zm477 488v430q0 43-30 73t-73 30q-42 0-72-30t-30-73v-430q0-43 30-72.5t72-29.5q43 0 73 29.5t30 72.5z"/>`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let displayIcon = icons.find((e) => e.name === name);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if displayIcon != null}
|
||||||
|
<svg fill="currentColor" class={$$props.class || "m-auto"} {focusable} {width} {height} viewBox="0 0 {displayIcon.w} {displayIcon.h}">{@html displayIcon.svg}</svg>
|
||||||
|
{/if}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import SidebarItem from "./SidebarItem.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative bg-white dark:bg-gray-800">
|
||||||
|
<div class="flex flex-col sm:flex-row sm:justify-around">
|
||||||
|
<div class="w-72 h-screen">
|
||||||
|
<div class="flex items-center justify-start mx-6 mt-10">
|
||||||
|
<svg class="h-10" preserveAspectRatio="xMidYMid" version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 204 204">
|
||||||
|
<circle cx="102" cy="102" r="102" fill="#fff" />
|
||||||
|
<path d="M3.42 102.13c-.04 57.18 47.35 98.12 99.2 98.09 57.46-.32 98.2-49.29 97.75-98.23-.03-28.4-10.65-47.14-17.55-56.35-7.38-9.71-6.07-12.38-17.74-.72l-9.3 9.3c24.2 32.31 20.42 61.25 7.6 83.7-25.5 35.3-49.6 32.8-49.6 32.8-8.63 10.92-14.66 10.4-24.32 2.04-30.59-1.88-58-34.54-59.12-69.25-.13-11.12 2.82-20.08 8.15-32.36-7.42-13.89-.35-25.14 15.7-23.15 0 0 19.51-17.51 44.8-18.32 28.29-1.26 50.29 18.14 50.29 18.14l9.3-9.4c10.8-10.9 10.68-8.91-1.72-18.62-.1.7-18.27-16.62-61.54-16.34-43.27.28-91.87 41.5-91.9 98.67z" />
|
||||||
|
<path d="M96.26 38.92c-12.62.72-25.74 8.82-35.04 14.46 2.48 12.33-5 21.34-14.58 21.1-9.55 18-6.64 31.97-6.64 31.97-.3 31.5 39.77 64.78 52.38 53.17 6.9-6.3 13.06-5.17 19.96 1.23 10.53 4.94 26.33-8.08 33.54-15.23 22-21.9 24.8-55.7 6.7-80.6l-3.2-4.4-6.3 6.3-6.3 6.2c14.65 19.66 7.32 43.8 7.32 43.8 2.88 14.8-3.88 24.35-18.38 21.78-11.95 8.42-34.24 9.22-48.54-.28-6.6-4.3-10.4-8.6-14.4-16.4-15.18-33.19 6.6-64.4 38.8-64.6 13.82.47 18.53 2.97 28.8 9.2l12.5-12.5-4.4-3.2c-13.4-9.7-27.61-12.84-42.22-12z" />
|
||||||
|
<path d="M91.08 67.62c-7.8 2.8-14.3 7.9-18.5 14.4-11.6 17.9-5.6 40.8 13.2 51 6 3.2 6.8 3.4 16.3 3.4 8-.1 10.9-.5 14.7-2.2 4.5-2.1 4.6-2.3 3.9-5.5-1.6-7.5 3.4-13.8 11.1-13.8 3.4 0 3.8-.3 4.7-3.8 2.5-8.8.6-21.2-4.4-28.9l-1.7-2.6-9.8 9.8c-7.9 7.9-9.7 10.2-9.8 12.7-.2 6.8-8.4 10.3-14 6.1-6.21-5.49-1.4-15.11 5.1-15.3 3.1 0 4.5-1.1 12.6-9.6 6.9-7.2 8.9-10 8.1-10.9-.6-.7-3.4-2.3-6.3-3.5-7.1-3.2-18.4-3.7-25.2-1.3z" />
|
||||||
|
<circle r="6" cy="126.75" cx="132.5" />
|
||||||
|
<circle cx="101.5" cy="167" r="6" />
|
||||||
|
<circle cx="48.75" cy="60.75" r="6" />
|
||||||
|
</svg>
|
||||||
|
<span class="text-gray-600 dark:text-gray-300 ml-4 text-2xl font-bold"> ESPresense </span>
|
||||||
|
</div>
|
||||||
|
<nav class="mt-10 px-6 ">
|
||||||
|
<SidebarItem icon="device" title="Devices" href="/" />
|
||||||
|
<SidebarItem icon="contact" title="Fingerprints" href="/fingerprints" />
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script>
|
||||||
|
import Icon from "./Icon.svelte";
|
||||||
|
import active from "svelte-spa-router/active";
|
||||||
|
import { link } from "svelte-spa-router";
|
||||||
|
|
||||||
|
export let title = "Title";
|
||||||
|
export let icon = "";
|
||||||
|
export let href = "/";
|
||||||
|
export let count = 0;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a use:link use:active={{ className: "text-gray-800 dark:text-gray-100 rounded-lg bg-gray-100 dark:bg-gray-600", inactiveClassName: "text-gray-600 dark:text-gray-400 rounded-lg" }} class="hover:text-gray-800 hover:bg-gray-100 flex items-center p-2 my-6 transition-colors dark:hover:text-white dark:hover:bg-gray-600 duration-200 text-gray-600 dark:text-gray-400 rounded-lg" {href}>
|
||||||
|
<Icon name={icon} />
|
||||||
|
<span class="mx-4 text-lg font-normal">
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{#if count > 0}
|
||||||
|
<span class="flex-grow text-right">
|
||||||
|
<button type="button" class="w-6 h-6 text-xs rounded-full text-white bg-red-500">
|
||||||
|
<span class="p-1">
|
||||||
|
{count}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
{:else}
|
||||||
|
<span class="flex-grow text-right" />
|
||||||
|
{/if}
|
||||||
|
</a>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<section class="text-gray-600 body-font">
|
||||||
|
<div class="container px-5 py-24 mx-auto">
|
||||||
|
<div class="flex flex-wrap -m-4 text-center">
|
||||||
|
<div class="p-4 sm:w-1/4 w-1/2">
|
||||||
|
<h2 class="title-font font-medium sm:text-4xl text-3xl text-gray-900">2.7K</h2>
|
||||||
|
<p class="leading-relaxed">Users</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-4 sm:w-1/4 w-1/2">
|
||||||
|
<h2 class="title-font font-medium sm:text-4xl text-3xl text-gray-900">1.8K</h2>
|
||||||
|
<p class="leading-relaxed">Subscribes</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-4 sm:w-1/4 w-1/2">
|
||||||
|
<h2 class="title-font font-medium sm:text-4xl text-3xl text-gray-900">35</h2>
|
||||||
|
<p class="leading-relaxed">Downloads</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-4 sm:w-1/4 w-1/2">
|
||||||
|
<h2 class="title-font font-medium sm:text-4xl text-3xl text-gray-900">4</h2>
|
||||||
|
<p class="leading-relaxed">Products</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Home from './routes/Home.svelte';
|
||||||
|
import Devices from './routes/Devices.svelte';
|
||||||
|
import NotFound from './routes/NotFound.svelte';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'/': Home,
|
||||||
|
'/fingerprints': Devices,
|
||||||
|
// The catch-all route must always be last
|
||||||
|
'*': NotFound
|
||||||
|
};
|
|
@ -0,0 +1,137 @@
|
||||||
|
<script>
|
||||||
|
import { devices, events } from '../stores';
|
||||||
|
import SvelteTable from "svelte-table";
|
||||||
|
|
||||||
|
var filterSelections = { };
|
||||||
|
var sortBy = "distance"
|
||||||
|
var sortOrder = 1;
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key: "distance",
|
||||||
|
title: "Dist",
|
||||||
|
value: v => v.distance,
|
||||||
|
renderValue: v => `${v.distance.toLocaleString(undefined, { minimumFractionDigits: 2 })}m`,
|
||||||
|
sortable: true,
|
||||||
|
class:"px-0 py-0 whitespace-nowrap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "id",
|
||||||
|
title: "ID",
|
||||||
|
value: v => v.id,
|
||||||
|
sortable: true,
|
||||||
|
filterOptions: rows => {
|
||||||
|
const prefixes = new Set()
|
||||||
|
rows.forEach(row => {
|
||||||
|
var prefix = row.id.substring(0, row.id.indexOf(":")+1);
|
||||||
|
if (prefix.length > 0) {
|
||||||
|
prefixes.add(prefix);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Array.from(prefixes).sort().map(a=>({"name": a, "value": a}));
|
||||||
|
},
|
||||||
|
filterValue: v => v.id.substring(0, v.id.indexOf(":")+1),
|
||||||
|
headerClass: "text-left px-6 py-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
title: "Name",
|
||||||
|
value: v => v.name ?? "",
|
||||||
|
sortable: true,
|
||||||
|
filterOptions: rows => {
|
||||||
|
let letrs = {};
|
||||||
|
rows.forEach(row => {
|
||||||
|
let letr = row.name?.charAt(0);
|
||||||
|
if (letr && letrs[letr] === undefined)
|
||||||
|
letrs[letr] = {
|
||||||
|
name: `${letr.toUpperCase()}`,
|
||||||
|
value: letr.toLowerCase(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// fix order
|
||||||
|
letrs = Object.entries(letrs)
|
||||||
|
.sort()
|
||||||
|
.reduce((o, [k, v]) => ((o[k] = v), o), {});
|
||||||
|
return Object.values(letrs);
|
||||||
|
},
|
||||||
|
filterValue: v => v.name?.charAt(0).toLowerCase(),
|
||||||
|
headerClass: "text-left px-6 py-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "mac",
|
||||||
|
title: "MAC",
|
||||||
|
value: v => v.mac,
|
||||||
|
sortable: true,
|
||||||
|
filterOptions: rows => {
|
||||||
|
// use first letter of last_name to generate filter
|
||||||
|
let letrs = {};
|
||||||
|
rows.forEach(row => {
|
||||||
|
let letr = row.mac.charAt(0);
|
||||||
|
if (letrs[letr] === undefined)
|
||||||
|
letrs[letr] = {
|
||||||
|
name: `${letr.toUpperCase()}`,
|
||||||
|
value: letr.toLowerCase(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// fix order
|
||||||
|
letrs = Object.entries(letrs)
|
||||||
|
.sort()
|
||||||
|
.reduce((o, [k, v]) => ((o[k] = v), o), {});
|
||||||
|
return Object.values(letrs);
|
||||||
|
},
|
||||||
|
filterValue: v => v.mac.charAt(0).toLowerCase(),
|
||||||
|
headerClass: "text-left px-6 py-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "rssi",
|
||||||
|
title: "Rssi",
|
||||||
|
value: v => v.rssi,
|
||||||
|
renderValue: v => v.rssi + "dBm",
|
||||||
|
sortable: true,
|
||||||
|
headerClass: "text-left px-6 py-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "rssi@1m",
|
||||||
|
title: "Rssi@1m",
|
||||||
|
value: v => v["rssi@1m"],
|
||||||
|
renderValue: v => v["rssi@1m"] + "dBm",
|
||||||
|
sortable: true,
|
||||||
|
headerClass: "text-left px-6 py-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "interval",
|
||||||
|
title: "Interval",
|
||||||
|
value: v => v.interval,
|
||||||
|
renderValue: v => v.interval + "ms",
|
||||||
|
sortable: true,
|
||||||
|
headerClass: "text-left px-6 py-3",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
|
||||||
|
{#if $devices?.devices != null }
|
||||||
|
<SvelteTable columns="{columns}" rows="{$devices.devices}"
|
||||||
|
bind:filterSelections="{filterSelections}"
|
||||||
|
bind:sortBy = "{sortBy}"
|
||||||
|
bind:sortOrder = "{sortOrder}"
|
||||||
|
classNameTable="min-w-full divide-y divide-gray-200 table-auto"
|
||||||
|
classNameThead="whitespace-nowrap text-left text-xs font-medium text-gray-500 uppercase"
|
||||||
|
classNameTbody="bg-white divide-y divide-gray-200"
|
||||||
|
classNameCell="px-3 py-1 whitespace-no-wrap text-sm leading-5 font-light text-gray-900"
|
||||||
|
classNameInput="px-1 py-1 border rounded-md text-sm leading-5 font-medium text-gray-900 placeholder-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5"
|
||||||
|
classNameSelect="px-1 py-1 border rounded-md text-sm leading-5 font-medium text-gray-900 placeholder-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5"
|
||||||
|
></SvelteTable>
|
||||||
|
{:else}
|
||||||
|
<h1>Error while loading devices</h1>
|
||||||
|
{/if}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
main {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script>
|
||||||
|
import Stats from "../components/Stats.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Stats />
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input id="name" type="text" class=" bg-gray-200 relative" placeholder="Name" />
|
||||||
|
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||||
|
Enroll
|
||||||
|
</button>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<h1>Not Found</h1>
|
||||||
|
<p>This route doesn't exist.</p>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1 {
|
||||||
|
color: #ff0040;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 4em;
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,15 +1,21 @@
|
||||||
import { readable } from 'svelte/store';
|
import { readable } from 'svelte/store';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
export const devices = readable([], function start(set) {
|
export const devices = readable([], function start(set) {
|
||||||
var errors = 0;
|
var errors = 0;
|
||||||
|
var outstanding = false;
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
|
if (outstanding) return;
|
||||||
|
outstanding = true;
|
||||||
fetch(`/json/devices`)
|
fetch(`/json/devices`)
|
||||||
.then(d => d.json())
|
.then(d => d.json())
|
||||||
.then(r => {
|
.then(r => {
|
||||||
|
outstanding = false;
|
||||||
errors = 0;
|
errors = 0;
|
||||||
set(r);
|
set(r);
|
||||||
})
|
})
|
||||||
.catch((ex) => {
|
.catch((ex) => {
|
||||||
|
outstanding = false;
|
||||||
if (errors > 5) set(null);
|
if (errors > 5) set(null);
|
||||||
console.log(ex);
|
console.log(ex);
|
||||||
});
|
});
|
||||||
|
@ -19,3 +25,22 @@ export const devices = readable([], function start(set) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var initialValue = {};
|
||||||
|
export const events = readable(initialValue, function start(set) {
|
||||||
|
const socket = new WebSocket(`ws://${location.hostname}/ws`);
|
||||||
|
|
||||||
|
socket.addEventListener('open', function (event) {
|
||||||
|
console.log("It's open");
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.addEventListener('message', function (event) {
|
||||||
|
initialValue = JSON.parse(event.data);
|
||||||
|
set(initialValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
return function stop() {
|
||||||
|
socket.close();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
import preprocess from "svelte-preprocess";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
preprocess: [
|
||||||
|
preprocess({
|
||||||
|
postcss: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config;
|
|
@ -0,0 +1,8 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||||
|
theme: {
|
||||||
|
extend: {}
|
||||||
|
},
|
||||||
|
plugins: []
|
||||||
|
};
|
Loading…
Reference in New Issue