@riddix/hamh 2.1.0-alpha.819 → 2.1.0-alpha.821
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/backend/cli.js
CHANGED
|
@@ -132611,6 +132611,8 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
|
|
|
132611
132611
|
disableLockPin: config11.disableLockPin,
|
|
132612
132612
|
lockUsercodeService: config11.lockUsercodeService,
|
|
132613
132613
|
lockUsercodeSlot: config11.lockUsercodeSlot,
|
|
132614
|
+
lockPinMinLength: config11.lockPinMinLength,
|
|
132615
|
+
lockPinMaxLength: config11.lockPinMaxLength,
|
|
132614
132616
|
powerEntity: config11.powerEntity,
|
|
132615
132617
|
energyEntity: config11.energyEntity,
|
|
132616
132618
|
suctionLevelEntity: config11.suctionLevelEntity,
|
|
@@ -132764,6 +132766,8 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
|
|
|
132764
132766
|
disableLockPin: config11.disableLockPin,
|
|
132765
132767
|
lockUsercodeService: config11.lockUsercodeService,
|
|
132766
132768
|
lockUsercodeSlot: config11.lockUsercodeSlot,
|
|
132769
|
+
lockPinMinLength: config11.lockPinMinLength,
|
|
132770
|
+
lockPinMaxLength: config11.lockPinMaxLength,
|
|
132767
132771
|
powerEntity: config11.powerEntity,
|
|
132768
132772
|
energyEntity: config11.energyEntity,
|
|
132769
132773
|
suctionLevelEntity: config11.suctionLevelEntity,
|
|
@@ -133608,6 +133612,8 @@ function entityMappingApi(mappingStorage) {
|
|
|
133608
133612
|
disableLockPin: body.disableLockPin,
|
|
133609
133613
|
lockUsercodeService: body.lockUsercodeService,
|
|
133610
133614
|
lockUsercodeSlot: body.lockUsercodeSlot,
|
|
133615
|
+
lockPinMinLength: body.lockPinMinLength,
|
|
133616
|
+
lockPinMaxLength: body.lockPinMaxLength,
|
|
133611
133617
|
powerEntity: body.powerEntity,
|
|
133612
133618
|
energyEntity: body.energyEntity,
|
|
133613
133619
|
suctionLevelEntity: body.suctionLevelEntity,
|
|
@@ -134100,6 +134106,8 @@ function configToProfileEntry(config11) {
|
|
|
134100
134106
|
disableLockPin: config11.disableLockPin,
|
|
134101
134107
|
lockUsercodeService: config11.lockUsercodeService,
|
|
134102
134108
|
lockUsercodeSlot: config11.lockUsercodeSlot,
|
|
134109
|
+
lockPinMinLength: config11.lockPinMinLength,
|
|
134110
|
+
lockPinMaxLength: config11.lockPinMaxLength,
|
|
134103
134111
|
powerEntity: config11.powerEntity,
|
|
134104
134112
|
energyEntity: config11.energyEntity,
|
|
134105
134113
|
suctionLevelEntity: config11.suctionLevelEntity,
|
|
@@ -134231,6 +134239,8 @@ function mappingProfileApi(mappingStorage) {
|
|
|
134231
134239
|
disableLockPin: entry.disableLockPin,
|
|
134232
134240
|
lockUsercodeService: entry.lockUsercodeService,
|
|
134233
134241
|
lockUsercodeSlot: entry.lockUsercodeSlot,
|
|
134242
|
+
lockPinMinLength: entry.lockPinMinLength,
|
|
134243
|
+
lockPinMaxLength: entry.lockPinMaxLength,
|
|
134234
134244
|
powerEntity: entry.powerEntity,
|
|
134235
134245
|
energyEntity: entry.energyEntity,
|
|
134236
134246
|
suctionLevelEntity: entry.suctionLevelEntity,
|
|
@@ -138403,6 +138413,10 @@ var EntityMappingStorage = class extends Service {
|
|
|
138403
138413
|
this.mappings.set(request.bridgeId, bridgeMap);
|
|
138404
138414
|
}
|
|
138405
138415
|
const roomEntities = request.roomEntities?.filter((e) => e?.trim()) || [];
|
|
138416
|
+
const pinLengths = sanitizePinLengths(
|
|
138417
|
+
request.lockPinMinLength,
|
|
138418
|
+
request.lockPinMaxLength
|
|
138419
|
+
);
|
|
138406
138420
|
const config11 = {
|
|
138407
138421
|
entityId: request.entityId,
|
|
138408
138422
|
matterDeviceType: request.matterDeviceType,
|
|
@@ -138422,6 +138436,8 @@ var EntityMappingStorage = class extends Service {
|
|
|
138422
138436
|
disableLockPin: request.disableLockPin || void 0,
|
|
138423
138437
|
lockUsercodeService: request.lockUsercodeService?.trim() || void 0,
|
|
138424
138438
|
lockUsercodeSlot: sanitizeUsercodeSlot(request.lockUsercodeSlot),
|
|
138439
|
+
lockPinMinLength: pinLengths.min,
|
|
138440
|
+
lockPinMaxLength: pinLengths.max,
|
|
138425
138441
|
powerEntity: request.powerEntity?.trim() || void 0,
|
|
138426
138442
|
energyEntity: request.energyEntity?.trim() || void 0,
|
|
138427
138443
|
pressureEntity: request.pressureEntity?.trim() || void 0,
|
|
@@ -138451,7 +138467,7 @@ var EntityMappingStorage = class extends Service {
|
|
|
138451
138467
|
climateAutoMode: request.climateAutoMode || void 0,
|
|
138452
138468
|
composedEntities: request.composedEntities?.filter((e) => e.entityId?.trim()) ?? void 0
|
|
138453
138469
|
};
|
|
138454
|
-
if (!config11.matterDeviceType && !config11.customName && !config11.customProductName && !config11.customVendorName && !config11.customSerialNumber && config11.customVendorId === void 0 && config11.disabled !== true && !config11.filterLifeEntity && !config11.cleaningModeEntity && !config11.temperatureEntity && !config11.humidityEntity && !config11.batteryEntity && !config11.chargingStateEntity && !config11.roomEntities && !config11.disableLockPin && !config11.lockUsercodeService && config11.lockUsercodeSlot === void 0 && !config11.powerEntity && !config11.energyEntity && !config11.pressureEntity && !config11.suctionLevelEntity && !config11.mopIntensityEntity && (!config11.customServiceAreas || config11.customServiceAreas.length === 0) && (!config11.customFanSpeedTags || Object.keys(config11.customFanSpeedTags).length === 0) && (!config11.fanWindPresets || (config11.fanWindPresets.natural?.length ?? 0) === 0 && (config11.fanWindPresets.sleep?.length ?? 0) === 0) && !config11.fanRestoreSpeedOnPowerOn && !config11.currentRoomEntity && !config11.cleanedAreaEntity && !config11.disableCustomAreaRoomModes && !config11.valetudoIdentifier && !config11.coverSwapOpenClose && !config11.coverExposeAsDimmableLight && !config11.selectExposeAsSwitch && !config11.selectSwitchOnOption && !config11.selectSwitchOffOption && !config11.coverSliderDebounceMs && !config11.updateThrottleMs && !config11.disableClimateOnOff && !config11.disableClimateFanControl && !config11.climateKeepModeOnIdle && !config11.climateExposeFan && !config11.climateAutoMode && (!config11.composedEntities || config11.composedEntities.length === 0)) {
|
|
138470
|
+
if (!config11.matterDeviceType && !config11.customName && !config11.customProductName && !config11.customVendorName && !config11.customSerialNumber && config11.customVendorId === void 0 && config11.disabled !== true && !config11.filterLifeEntity && !config11.cleaningModeEntity && !config11.temperatureEntity && !config11.humidityEntity && !config11.batteryEntity && !config11.chargingStateEntity && !config11.roomEntities && !config11.disableLockPin && !config11.lockUsercodeService && config11.lockUsercodeSlot === void 0 && config11.lockPinMinLength === void 0 && config11.lockPinMaxLength === void 0 && !config11.powerEntity && !config11.energyEntity && !config11.pressureEntity && !config11.suctionLevelEntity && !config11.mopIntensityEntity && (!config11.customServiceAreas || config11.customServiceAreas.length === 0) && (!config11.customFanSpeedTags || Object.keys(config11.customFanSpeedTags).length === 0) && (!config11.fanWindPresets || (config11.fanWindPresets.natural?.length ?? 0) === 0 && (config11.fanWindPresets.sleep?.length ?? 0) === 0) && !config11.fanRestoreSpeedOnPowerOn && !config11.currentRoomEntity && !config11.cleanedAreaEntity && !config11.disableCustomAreaRoomModes && !config11.valetudoIdentifier && !config11.coverSwapOpenClose && !config11.coverExposeAsDimmableLight && !config11.selectExposeAsSwitch && !config11.selectSwitchOnOption && !config11.selectSwitchOffOption && !config11.coverSliderDebounceMs && !config11.updateThrottleMs && !config11.disableClimateOnOff && !config11.disableClimateFanControl && !config11.climateKeepModeOnIdle && !config11.climateExposeFan && !config11.climateAutoMode && (!config11.composedEntities || config11.composedEntities.length === 0)) {
|
|
138455
138471
|
bridgeMap.delete(request.entityId);
|
|
138456
138472
|
} else {
|
|
138457
138473
|
bridgeMap.set(request.entityId, config11);
|
|
@@ -138491,6 +138507,24 @@ function sanitizeUsercodeSlot(value) {
|
|
|
138491
138507
|
}
|
|
138492
138508
|
return n;
|
|
138493
138509
|
}
|
|
138510
|
+
function sanitizePinLength(value) {
|
|
138511
|
+
if (value === void 0 || value === null || value === "") {
|
|
138512
|
+
return void 0;
|
|
138513
|
+
}
|
|
138514
|
+
const n = typeof value === "string" ? Number(value) : value;
|
|
138515
|
+
if (typeof n !== "number" || !Number.isInteger(n) || n < 1 || n > 20) {
|
|
138516
|
+
return void 0;
|
|
138517
|
+
}
|
|
138518
|
+
return n;
|
|
138519
|
+
}
|
|
138520
|
+
function sanitizePinLengths(min, max) {
|
|
138521
|
+
const lo = sanitizePinLength(min);
|
|
138522
|
+
const hi = sanitizePinLength(max);
|
|
138523
|
+
if ((lo ?? 4) > (hi ?? 8)) {
|
|
138524
|
+
return { min: void 0, max: void 0 };
|
|
138525
|
+
}
|
|
138526
|
+
return { min: lo, max: hi };
|
|
138527
|
+
}
|
|
138494
138528
|
function sanitizeDebounceMs(value) {
|
|
138495
138529
|
if (value === void 0 || value === null || value === "") {
|
|
138496
138530
|
return void 0;
|
|
@@ -153370,6 +153404,104 @@ function dropUndefined(obj) {
|
|
|
153370
153404
|
return result;
|
|
153371
153405
|
}
|
|
153372
153406
|
|
|
153407
|
+
// src/plugins/builtin/camera/camera-tcp-requirement.ts
|
|
153408
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
153409
|
+
|
|
153410
|
+
// src/plugins/plugin-storage.ts
|
|
153411
|
+
init_esm();
|
|
153412
|
+
import * as fs9 from "node:fs";
|
|
153413
|
+
import * as path11 from "node:path";
|
|
153414
|
+
var logger194 = Logger.get("PluginStorage");
|
|
153415
|
+
var SAVE_DEBOUNCE_MS = 500;
|
|
153416
|
+
function pluginStorageFilePath(storageDir, bridgeId, pluginName) {
|
|
153417
|
+
const safe = (s) => s.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
153418
|
+
return path11.join(
|
|
153419
|
+
storageDir,
|
|
153420
|
+
`plugin-${safe(bridgeId)}-${safe(pluginName)}.json`
|
|
153421
|
+
);
|
|
153422
|
+
}
|
|
153423
|
+
var FilePluginStorage = class {
|
|
153424
|
+
data = {};
|
|
153425
|
+
dirty = false;
|
|
153426
|
+
filePath;
|
|
153427
|
+
saveTimer;
|
|
153428
|
+
constructor(storageDir, bridgeId, pluginName) {
|
|
153429
|
+
this.filePath = pluginStorageFilePath(storageDir, bridgeId, pluginName);
|
|
153430
|
+
this.load();
|
|
153431
|
+
}
|
|
153432
|
+
async get(key, defaultValue) {
|
|
153433
|
+
const value = this.data[key];
|
|
153434
|
+
return value ?? defaultValue;
|
|
153435
|
+
}
|
|
153436
|
+
async set(key, value) {
|
|
153437
|
+
this.data[key] = value;
|
|
153438
|
+
this.dirty = true;
|
|
153439
|
+
this.scheduleSave();
|
|
153440
|
+
}
|
|
153441
|
+
async delete(key) {
|
|
153442
|
+
delete this.data[key];
|
|
153443
|
+
this.dirty = true;
|
|
153444
|
+
this.scheduleSave();
|
|
153445
|
+
}
|
|
153446
|
+
async keys() {
|
|
153447
|
+
return Object.keys(this.data);
|
|
153448
|
+
}
|
|
153449
|
+
load() {
|
|
153450
|
+
try {
|
|
153451
|
+
if (fs9.existsSync(this.filePath)) {
|
|
153452
|
+
const raw = fs9.readFileSync(this.filePath, "utf-8");
|
|
153453
|
+
this.data = JSON.parse(raw);
|
|
153454
|
+
}
|
|
153455
|
+
} catch (e) {
|
|
153456
|
+
logger194.warn(`Failed to load plugin storage from ${this.filePath}:`, e);
|
|
153457
|
+
this.data = {};
|
|
153458
|
+
}
|
|
153459
|
+
}
|
|
153460
|
+
scheduleSave() {
|
|
153461
|
+
if (this.saveTimer) clearTimeout(this.saveTimer);
|
|
153462
|
+
this.saveTimer = setTimeout(() => this.save(), SAVE_DEBOUNCE_MS);
|
|
153463
|
+
}
|
|
153464
|
+
save() {
|
|
153465
|
+
if (!this.dirty) return;
|
|
153466
|
+
if (this.saveTimer) {
|
|
153467
|
+
clearTimeout(this.saveTimer);
|
|
153468
|
+
this.saveTimer = void 0;
|
|
153469
|
+
}
|
|
153470
|
+
try {
|
|
153471
|
+
const dir = path11.dirname(this.filePath);
|
|
153472
|
+
if (!fs9.existsSync(dir)) {
|
|
153473
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
153474
|
+
}
|
|
153475
|
+
fs9.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2));
|
|
153476
|
+
this.dirty = false;
|
|
153477
|
+
} catch (e) {
|
|
153478
|
+
logger194.warn(`Failed to save plugin storage to ${this.filePath}:`, e);
|
|
153479
|
+
}
|
|
153480
|
+
}
|
|
153481
|
+
flush() {
|
|
153482
|
+
this.save();
|
|
153483
|
+
}
|
|
153484
|
+
};
|
|
153485
|
+
|
|
153486
|
+
// src/plugins/builtin/camera/camera-tcp-requirement.ts
|
|
153487
|
+
var CAMERA_TCP_CONFIG = { incoming: true, outgoing: false };
|
|
153488
|
+
function parseCameraList(cameras) {
|
|
153489
|
+
if (typeof cameras !== "string") return [];
|
|
153490
|
+
return cameras.split(",").map((s) => s.trim()).filter(Boolean);
|
|
153491
|
+
}
|
|
153492
|
+
function bridgeNeedsTcpForCameras(storageDir, bridgeId) {
|
|
153493
|
+
try {
|
|
153494
|
+
const raw = readFileSync6(
|
|
153495
|
+
pluginStorageFilePath(storageDir, bridgeId, "camera"),
|
|
153496
|
+
"utf-8"
|
|
153497
|
+
);
|
|
153498
|
+
const json = JSON.parse(raw);
|
|
153499
|
+
return parseCameraList(json.config?.cameras).length > 0;
|
|
153500
|
+
} catch {
|
|
153501
|
+
return false;
|
|
153502
|
+
}
|
|
153503
|
+
}
|
|
153504
|
+
|
|
153373
153505
|
// src/plugins/plugin-manager.ts
|
|
153374
153506
|
init_esm();
|
|
153375
153507
|
import * as fs10 from "node:fs";
|
|
@@ -153671,7 +153803,7 @@ init_esm4();
|
|
|
153671
153803
|
init_esm3();
|
|
153672
153804
|
init_access_control();
|
|
153673
153805
|
init_groupcast();
|
|
153674
|
-
var
|
|
153806
|
+
var logger195 = Logger.get("GroupcastServer");
|
|
153675
153807
|
var UNMAPPED_KEYSET_ID = 65535;
|
|
153676
153808
|
var GROUPCAST_IS_PROVISIONAL = true;
|
|
153677
153809
|
var GroupcastServer = class extends GroupcastBehavior {
|
|
@@ -154088,7 +154220,7 @@ var GroupcastServer = class extends GroupcastBehavior {
|
|
|
154088
154220
|
if (hasMembership) continue;
|
|
154089
154221
|
const fabricGroups = gkmState.groupTable.filter((g) => g.fabricIndex === fi);
|
|
154090
154222
|
if (fabricGroups.length === 0) continue;
|
|
154091
|
-
|
|
154223
|
+
logger195.info(`Migrating ${fabricGroups.length} legacy group(s) for fabric ${fi} to Groupcast`);
|
|
154092
154224
|
const newEntries = fabricGroups.map((group) => {
|
|
154093
154225
|
const keyMapping = gkmState.groupKeyMap.find((m) => m.fabricIndex === fi && m.groupId === group.groupId);
|
|
154094
154226
|
const mcastAddrPolicy = this.features.perGroup ? Groupcast3.MulticastAddrPolicy.PerGroup : Groupcast3.MulticastAddrPolicy.IanaAddr;
|
|
@@ -154110,7 +154242,7 @@ var GroupcastServer = class extends GroupcastBehavior {
|
|
|
154110
154242
|
}
|
|
154111
154243
|
if (migrated) {
|
|
154112
154244
|
this.#updateUsedMcastAddrCount();
|
|
154113
|
-
|
|
154245
|
+
logger195.info("Groupcast migration complete");
|
|
154114
154246
|
}
|
|
154115
154247
|
}
|
|
154116
154248
|
async [Symbol.asyncDispose]() {
|
|
@@ -154586,7 +154718,7 @@ var IdentifyServer2 = class extends IdentifyServer {
|
|
|
154586
154718
|
// src/matter/endpoints/validate-endpoint-type.ts
|
|
154587
154719
|
init_esm();
|
|
154588
154720
|
init_esm7();
|
|
154589
|
-
var
|
|
154721
|
+
var logger196 = Logger.get("EndpointValidation");
|
|
154590
154722
|
function toCamelCase(name) {
|
|
154591
154723
|
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
154592
154724
|
}
|
|
@@ -154616,12 +154748,12 @@ function validateEndpointType(endpointType, entityId) {
|
|
|
154616
154748
|
}
|
|
154617
154749
|
const prefix = entityId ? `[${entityId}] ` : "";
|
|
154618
154750
|
if (missingMandatory.length > 0) {
|
|
154619
|
-
|
|
154751
|
+
logger196.warn(
|
|
154620
154752
|
`${prefix}${deviceTypeModel.name} (0x${endpointType.deviceType.toString(16)}): missing mandatory clusters: ${missingMandatory.join(", ")}`
|
|
154621
154753
|
);
|
|
154622
154754
|
}
|
|
154623
154755
|
if (availableOptional.length > 0) {
|
|
154624
|
-
|
|
154756
|
+
logger196.debug(
|
|
154625
154757
|
`${prefix}${deviceTypeModel.name} (0x${endpointType.deviceType.toString(16)}): optional clusters not used: ${availableOptional.join(", ")}`
|
|
154626
154758
|
);
|
|
154627
154759
|
}
|
|
@@ -154745,7 +154877,7 @@ var BridgeDataProvider = class extends Service {
|
|
|
154745
154877
|
|
|
154746
154878
|
// src/utils/apply-patch-state.ts
|
|
154747
154879
|
init_esm();
|
|
154748
|
-
var
|
|
154880
|
+
var logger197 = Logger.get("ApplyPatchState");
|
|
154749
154881
|
function applyPatchState(state, patch, options) {
|
|
154750
154882
|
return applyPatch(state, patch, options?.force);
|
|
154751
154883
|
}
|
|
@@ -154772,23 +154904,23 @@ function applyPatch(state, patch, force = false) {
|
|
|
154772
154904
|
if (errorMessage.includes(
|
|
154773
154905
|
"Endpoint storage inaccessible because endpoint is not a node and is not owned by another endpoint"
|
|
154774
154906
|
)) {
|
|
154775
|
-
|
|
154907
|
+
logger197.debug(
|
|
154776
154908
|
`Suppressed endpoint storage error, patch not applied: ${JSON.stringify(actualPatch)}`
|
|
154777
154909
|
);
|
|
154778
154910
|
return actualPatch;
|
|
154779
154911
|
}
|
|
154780
154912
|
if (errorMessage.includes("synchronous-transaction-conflict")) {
|
|
154781
|
-
|
|
154913
|
+
logger197.warn(
|
|
154782
154914
|
`Transaction conflict, state update DROPPED: ${JSON.stringify(actualPatch)}`
|
|
154783
154915
|
);
|
|
154784
154916
|
return actualPatch;
|
|
154785
154917
|
}
|
|
154786
154918
|
failedKeys.push(key);
|
|
154787
|
-
|
|
154919
|
+
logger197.warn(`Failed to set property '${key}': ${errorMessage}`);
|
|
154788
154920
|
}
|
|
154789
154921
|
}
|
|
154790
154922
|
if (failedKeys.length > 0) {
|
|
154791
|
-
|
|
154923
|
+
logger197.warn(
|
|
154792
154924
|
`${failedKeys.length} properties failed to update: [${failedKeys.join(", ")}]`
|
|
154793
154925
|
);
|
|
154794
154926
|
}
|
|
@@ -154858,7 +154990,7 @@ function truncate(maxLength, value) {
|
|
|
154858
154990
|
}
|
|
154859
154991
|
|
|
154860
154992
|
// src/plugins/plugin-device-factory.ts
|
|
154861
|
-
var
|
|
154993
|
+
var logger198 = Logger.get("PluginDeviceFactory");
|
|
154862
154994
|
var deviceTypeMap = {
|
|
154863
154995
|
on_off_light: () => OnOffLightDevice.with(
|
|
154864
154996
|
IdentifyServer2,
|
|
@@ -154964,7 +155096,7 @@ var deviceTypeMap = {
|
|
|
154964
155096
|
function createPluginEndpointType(deviceType) {
|
|
154965
155097
|
const factory = deviceTypeMap[deviceType];
|
|
154966
155098
|
if (!factory) {
|
|
154967
|
-
|
|
155099
|
+
logger198.warn(`Unsupported plugin device type: "${deviceType}"`);
|
|
154968
155100
|
return void 0;
|
|
154969
155101
|
}
|
|
154970
155102
|
const endpoint = factory();
|
|
@@ -154975,79 +155107,6 @@ function getSupportedPluginDeviceTypes() {
|
|
|
154975
155107
|
return Object.keys(deviceTypeMap);
|
|
154976
155108
|
}
|
|
154977
155109
|
|
|
154978
|
-
// src/plugins/plugin-storage.ts
|
|
154979
|
-
init_esm();
|
|
154980
|
-
import * as fs9 from "node:fs";
|
|
154981
|
-
import * as path11 from "node:path";
|
|
154982
|
-
var logger198 = Logger.get("PluginStorage");
|
|
154983
|
-
var SAVE_DEBOUNCE_MS = 500;
|
|
154984
|
-
var FilePluginStorage = class {
|
|
154985
|
-
data = {};
|
|
154986
|
-
dirty = false;
|
|
154987
|
-
filePath;
|
|
154988
|
-
saveTimer;
|
|
154989
|
-
constructor(storageDir, bridgeId, pluginName) {
|
|
154990
|
-
const safe = (s) => s.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
154991
|
-
this.filePath = path11.join(
|
|
154992
|
-
storageDir,
|
|
154993
|
-
`plugin-${safe(bridgeId)}-${safe(pluginName)}.json`
|
|
154994
|
-
);
|
|
154995
|
-
this.load();
|
|
154996
|
-
}
|
|
154997
|
-
async get(key, defaultValue) {
|
|
154998
|
-
const value = this.data[key];
|
|
154999
|
-
return value ?? defaultValue;
|
|
155000
|
-
}
|
|
155001
|
-
async set(key, value) {
|
|
155002
|
-
this.data[key] = value;
|
|
155003
|
-
this.dirty = true;
|
|
155004
|
-
this.scheduleSave();
|
|
155005
|
-
}
|
|
155006
|
-
async delete(key) {
|
|
155007
|
-
delete this.data[key];
|
|
155008
|
-
this.dirty = true;
|
|
155009
|
-
this.scheduleSave();
|
|
155010
|
-
}
|
|
155011
|
-
async keys() {
|
|
155012
|
-
return Object.keys(this.data);
|
|
155013
|
-
}
|
|
155014
|
-
load() {
|
|
155015
|
-
try {
|
|
155016
|
-
if (fs9.existsSync(this.filePath)) {
|
|
155017
|
-
const raw = fs9.readFileSync(this.filePath, "utf-8");
|
|
155018
|
-
this.data = JSON.parse(raw);
|
|
155019
|
-
}
|
|
155020
|
-
} catch (e) {
|
|
155021
|
-
logger198.warn(`Failed to load plugin storage from ${this.filePath}:`, e);
|
|
155022
|
-
this.data = {};
|
|
155023
|
-
}
|
|
155024
|
-
}
|
|
155025
|
-
scheduleSave() {
|
|
155026
|
-
if (this.saveTimer) clearTimeout(this.saveTimer);
|
|
155027
|
-
this.saveTimer = setTimeout(() => this.save(), SAVE_DEBOUNCE_MS);
|
|
155028
|
-
}
|
|
155029
|
-
save() {
|
|
155030
|
-
if (!this.dirty) return;
|
|
155031
|
-
if (this.saveTimer) {
|
|
155032
|
-
clearTimeout(this.saveTimer);
|
|
155033
|
-
this.saveTimer = void 0;
|
|
155034
|
-
}
|
|
155035
|
-
try {
|
|
155036
|
-
const dir = path11.dirname(this.filePath);
|
|
155037
|
-
if (!fs9.existsSync(dir)) {
|
|
155038
|
-
fs9.mkdirSync(dir, { recursive: true });
|
|
155039
|
-
}
|
|
155040
|
-
fs9.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2));
|
|
155041
|
-
this.dirty = false;
|
|
155042
|
-
} catch (e) {
|
|
155043
|
-
logger198.warn(`Failed to save plugin storage to ${this.filePath}:`, e);
|
|
155044
|
-
}
|
|
155045
|
-
}
|
|
155046
|
-
flush() {
|
|
155047
|
-
this.save();
|
|
155048
|
-
}
|
|
155049
|
-
};
|
|
155050
|
-
|
|
155051
155110
|
// src/plugins/safe-plugin-runner.ts
|
|
155052
155111
|
init_esm();
|
|
155053
155112
|
var logger199 = Logger.get("SafePluginRunner");
|
|
@@ -155763,13 +155822,14 @@ var SecondaryNetworkInterfaceEndpointDefinition = MutableEndpoint({
|
|
|
155763
155822
|
Object.freeze(SecondaryNetworkInterfaceEndpointDefinition);
|
|
155764
155823
|
|
|
155765
155824
|
// src/utils/json/create-bridge-server-config.ts
|
|
155766
|
-
function createBridgeServerConfig(data) {
|
|
155825
|
+
function createBridgeServerConfig(data, options) {
|
|
155767
155826
|
return {
|
|
155768
155827
|
type: ServerNode.RootEndpoint,
|
|
155769
155828
|
id: data.id,
|
|
155770
155829
|
network: {
|
|
155771
155830
|
port: data.port,
|
|
155772
|
-
subscriptionOptions: matterSubscriptionOptions()
|
|
155831
|
+
subscriptionOptions: matterSubscriptionOptions(),
|
|
155832
|
+
...options?.tcp ? { tcp: options.tcp } : {}
|
|
155773
155833
|
},
|
|
155774
155834
|
productDescription: {
|
|
155775
155835
|
name: data.name,
|
|
@@ -155800,8 +155860,8 @@ function createBridgeServerConfig(data) {
|
|
|
155800
155860
|
|
|
155801
155861
|
// src/matter/endpoints/bridge-server-node.ts
|
|
155802
155862
|
var BridgeServerNode = class extends ServerNode {
|
|
155803
|
-
constructor(env, bridgeData, aggregator) {
|
|
155804
|
-
const config11 = createBridgeServerConfig(bridgeData);
|
|
155863
|
+
constructor(env, bridgeData, aggregator, options) {
|
|
155864
|
+
const config11 = createBridgeServerConfig(bridgeData, options);
|
|
155805
155865
|
super({
|
|
155806
155866
|
...config11,
|
|
155807
155867
|
environment: env,
|
|
@@ -155941,14 +156001,16 @@ function deadSessionTimeoutMs(flags2) {
|
|
|
155941
156001
|
var SHUTDOWN_SESSION_CLOSE_TIMEOUT_MS = 2500;
|
|
155942
156002
|
var MDNS_ADDRESS_CHECK_INTERVAL_MS = 6e4;
|
|
155943
156003
|
var Bridge = class {
|
|
155944
|
-
constructor(env, logger245, dataProvider, endpointManager) {
|
|
156004
|
+
constructor(env, logger245, dataProvider, endpointManager, serverOptions) {
|
|
155945
156005
|
this.dataProvider = dataProvider;
|
|
155946
156006
|
this.endpointManager = endpointManager;
|
|
156007
|
+
this.serverOptions = serverOptions;
|
|
155947
156008
|
this.log = logger245.get(`Bridge / ${dataProvider.id}`);
|
|
155948
156009
|
this.server = new BridgeServerNode(
|
|
155949
156010
|
env,
|
|
155950
156011
|
this.dataProvider,
|
|
155951
|
-
this.endpointManager.root
|
|
156012
|
+
this.endpointManager.root,
|
|
156013
|
+
this.serverOptions
|
|
155952
156014
|
);
|
|
155953
156015
|
const { basicInformation } = this.dataProvider;
|
|
155954
156016
|
this.log.debugCtx("Root bridge BasicInformation configured", {
|
|
@@ -155963,6 +156025,7 @@ var Bridge = class {
|
|
|
155963
156025
|
}
|
|
155964
156026
|
dataProvider;
|
|
155965
156027
|
endpointManager;
|
|
156028
|
+
serverOptions;
|
|
155966
156029
|
log;
|
|
155967
156030
|
server;
|
|
155968
156031
|
status = {
|
|
@@ -156113,7 +156176,44 @@ var Bridge = class {
|
|
|
156113
156176
|
return this.endpointManager.getPluginConfigSchema(pluginName);
|
|
156114
156177
|
}
|
|
156115
156178
|
async updatePluginConfig(pluginName, config11) {
|
|
156116
|
-
|
|
156179
|
+
const result = await this.endpointManager.updatePluginConfig(
|
|
156180
|
+
pluginName,
|
|
156181
|
+
config11
|
|
156182
|
+
);
|
|
156183
|
+
if (result && pluginName === "camera") {
|
|
156184
|
+
await this.applyCameraTcp(config11);
|
|
156185
|
+
}
|
|
156186
|
+
return result;
|
|
156187
|
+
}
|
|
156188
|
+
// #419: cameras need Matter over TCP because the WebRTC offer exceeds the UDP
|
|
156189
|
+
// message size. Match the bridge listener to the saved camera list. Changing
|
|
156190
|
+
// network config takes effect on (re)start, so bounce the bridge if running.
|
|
156191
|
+
async applyCameraTcp(config11) {
|
|
156192
|
+
const want = parseCameraList(config11.cameras).length > 0;
|
|
156193
|
+
const have = !!this.server.state.network.tcp;
|
|
156194
|
+
if (want === have) {
|
|
156195
|
+
return;
|
|
156196
|
+
}
|
|
156197
|
+
this.log.info(
|
|
156198
|
+
want ? "cameras configured, enabling matter over tcp (#419)" : "no cameras left, disabling matter over tcp (#419)"
|
|
156199
|
+
);
|
|
156200
|
+
const running = this.status.code === BridgeStatus.Running;
|
|
156201
|
+
if (running) {
|
|
156202
|
+
await this.stop();
|
|
156203
|
+
}
|
|
156204
|
+
try {
|
|
156205
|
+
await this.server.set({
|
|
156206
|
+
network: { tcp: want ? CAMERA_TCP_CONFIG : void 0 }
|
|
156207
|
+
});
|
|
156208
|
+
} catch (e) {
|
|
156209
|
+
this.log.warn(
|
|
156210
|
+
"Could not apply tcp live, restart matterhub to apply it:",
|
|
156211
|
+
e
|
|
156212
|
+
);
|
|
156213
|
+
}
|
|
156214
|
+
if (running) {
|
|
156215
|
+
await this.start();
|
|
156216
|
+
}
|
|
156117
156217
|
}
|
|
156118
156218
|
async initialize() {
|
|
156119
156219
|
await this.server.construction.ready.then();
|
|
@@ -164339,6 +164439,24 @@ function usercodePassthroughFrom(homeAssistant) {
|
|
|
164339
164439
|
callAction: (action) => homeAssistant.callAction(action)
|
|
164340
164440
|
};
|
|
164341
164441
|
}
|
|
164442
|
+
var DEFAULT_MIN_PIN_LENGTH = 4;
|
|
164443
|
+
var DEFAULT_MAX_PIN_LENGTH = 8;
|
|
164444
|
+
var PIN_LENGTH_SPEC_MIN = 1;
|
|
164445
|
+
var PIN_LENGTH_SPEC_MAX = 20;
|
|
164446
|
+
function clampPinLength(value) {
|
|
164447
|
+
if (value === void 0 || !Number.isInteger(value) || value < PIN_LENGTH_SPEC_MIN || value > PIN_LENGTH_SPEC_MAX) {
|
|
164448
|
+
return void 0;
|
|
164449
|
+
}
|
|
164450
|
+
return value;
|
|
164451
|
+
}
|
|
164452
|
+
function effectivePinCodeLengths(homeAssistant) {
|
|
164453
|
+
const min = clampPinLength(homeAssistant.state.mapping?.lockPinMinLength) ?? DEFAULT_MIN_PIN_LENGTH;
|
|
164454
|
+
const max = clampPinLength(homeAssistant.state.mapping?.lockPinMaxLength) ?? DEFAULT_MAX_PIN_LENGTH;
|
|
164455
|
+
if (min > max) {
|
|
164456
|
+
return { min: DEFAULT_MIN_PIN_LENGTH, max: DEFAULT_MAX_PIN_LENGTH };
|
|
164457
|
+
}
|
|
164458
|
+
return { min, max };
|
|
164459
|
+
}
|
|
164342
164460
|
function asFabricIndex(value) {
|
|
164343
164461
|
return value === void 0 || value === 0 ? null : FabricIndex3(value);
|
|
164344
164462
|
}
|
|
@@ -164597,6 +164715,7 @@ var LockServerWithPinBase = class extends PinCredentialBase {
|
|
|
164597
164715
|
const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
|
|
164598
164716
|
const isPinDisabledByMapping = homeAssistant.state.mapping?.disableLockPin === true;
|
|
164599
164717
|
const hasPinConfigured = !isPinDisabledByMapping && hasStoredCredentialHelper(this.env, homeAssistant.entityId);
|
|
164718
|
+
const pinLengths = effectivePinCodeLengths(homeAssistant);
|
|
164600
164719
|
applyPatchState(this.state, {
|
|
164601
164720
|
lockState: this.state.config.getLockState(entity.state, this.agent),
|
|
164602
164721
|
lockType: DoorLock3.LockType.DeadBolt,
|
|
@@ -164616,8 +164735,8 @@ var LockServerWithPinBase = class extends PinCredentialBase {
|
|
|
164616
164735
|
numberOfPinUsersSupported: 1,
|
|
164617
164736
|
numberOfTotalUsersSupported: 1,
|
|
164618
164737
|
numberOfCredentialsSupportedPerUser: 1,
|
|
164619
|
-
maxPinCodeLength:
|
|
164620
|
-
minPinCodeLength:
|
|
164738
|
+
maxPinCodeLength: pinLengths.max,
|
|
164739
|
+
minPinCodeLength: pinLengths.min,
|
|
164621
164740
|
requirePinForRemoteOperation: hasPinConfigured
|
|
164622
164741
|
});
|
|
164623
164742
|
}
|
|
@@ -164770,6 +164889,7 @@ var LockServerWithPinAndUnboltBase = class extends PinCredentialUnboltBase {
|
|
|
164770
164889
|
const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
|
|
164771
164890
|
const isPinDisabledByMapping = homeAssistant.state.mapping?.disableLockPin === true;
|
|
164772
164891
|
const hasPinConfigured = !isPinDisabledByMapping && hasStoredCredentialHelper(this.env, homeAssistant.entityId);
|
|
164892
|
+
const pinLengths = effectivePinCodeLengths(homeAssistant);
|
|
164773
164893
|
applyPatchState(this.state, {
|
|
164774
164894
|
lockState: this.state.config.getLockState(entity.state, this.agent),
|
|
164775
164895
|
lockType: DoorLock3.LockType.DeadBolt,
|
|
@@ -164789,8 +164909,8 @@ var LockServerWithPinAndUnboltBase = class extends PinCredentialUnboltBase {
|
|
|
164789
164909
|
numberOfPinUsersSupported: 1,
|
|
164790
164910
|
numberOfTotalUsersSupported: 1,
|
|
164791
164911
|
numberOfCredentialsSupportedPerUser: 1,
|
|
164792
|
-
maxPinCodeLength:
|
|
164793
|
-
minPinCodeLength:
|
|
164912
|
+
maxPinCodeLength: pinLengths.max,
|
|
164913
|
+
minPinCodeLength: pinLengths.min,
|
|
164794
164914
|
requirePinForRemoteOperation: hasPinConfigured
|
|
164795
164915
|
});
|
|
164796
164916
|
}
|
|
@@ -171111,7 +171231,7 @@ var CameraPlugin = class {
|
|
|
171111
171231
|
this.log.info("no Home Assistant connection, no cameras exposed");
|
|
171112
171232
|
return;
|
|
171113
171233
|
}
|
|
171114
|
-
const entityIds = (cameras
|
|
171234
|
+
const entityIds = parseCameraList(cameras);
|
|
171115
171235
|
if (entityIds.length === 0) {
|
|
171116
171236
|
this.log.info("no camera entity ids configured");
|
|
171117
171237
|
return;
|
|
@@ -174364,6 +174484,11 @@ var BridgeEnvironmentFactory = class extends BridgeFactory {
|
|
|
174364
174484
|
initialData,
|
|
174365
174485
|
this.storageLocation
|
|
174366
174486
|
);
|
|
174487
|
+
const loggerService = env.get(LoggerService);
|
|
174488
|
+
const tcp = this.storageLocation && bridgeNeedsTcpForCameras(this.storageLocation, initialData.id) ? CAMERA_TCP_CONFIG : void 0;
|
|
174489
|
+
if (tcp) {
|
|
174490
|
+
loggerService.get("BridgeEnvironmentFactory").info("matter over tcp enabled, cameras configured (#419)");
|
|
174491
|
+
}
|
|
174367
174492
|
class BridgeWithEnvironment extends Bridge {
|
|
174368
174493
|
async dispose() {
|
|
174369
174494
|
await super.dispose();
|
|
@@ -174372,9 +174497,10 @@ var BridgeEnvironmentFactory = class extends BridgeFactory {
|
|
|
174372
174497
|
}
|
|
174373
174498
|
const bridge = new BridgeWithEnvironment(
|
|
174374
174499
|
env,
|
|
174375
|
-
|
|
174500
|
+
loggerService,
|
|
174376
174501
|
await env.load(BridgeDataProvider),
|
|
174377
|
-
await env.load(BridgeEndpointManager)
|
|
174502
|
+
await env.load(BridgeEndpointManager),
|
|
174503
|
+
tcp ? { tcp } : void 0
|
|
174378
174504
|
);
|
|
174379
174505
|
await bridge.initialize();
|
|
174380
174506
|
return bridge;
|