@camstack/system 1.2.243 → 1.2.244
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/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +315 -7
- package/dist/builtins/device-manager/device-manager.addon.mjs +315 -7
- package/dist/builtins/device-manager/device-meta-actions.d.ts +65 -0
- package/dist/builtins/device-manager/device-meta-types.d.ts +49 -0
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-B4htkDVv.mjs → dist-4MS1GNc8.mjs} +206 -1
- package/dist/{dist-D6kqBW1G.js → dist-Cz6h6Nc3.js} +211 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{retired-settings-keys-BPIyHR4_.js → retired-settings-keys-DBrIdOQp.js} +1 -1
- package/dist/{retired-settings-keys-C9w4b4TV.mjs → retired-settings-keys-D_5MkKII.mjs} +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Cz6h6Nc3.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as addonPagesCapability,
|
|
1
|
+
import { A as addonPagesCapability, Cn as EventCategory, Xt as errMsg, Zt as BaseAddon } from "../../dist-4MS1GNc8.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Cz6h6Nc3.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Cn as EventCategory, M as addonWidgetsCapability, Xt as errMsg, Zt as BaseAddon } from "../../dist-4MS1GNc8.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Cz6h6Nc3.js");
|
|
7
7
|
//#region src/builtins/alerts/prune-policy.ts
|
|
8
8
|
/**
|
|
9
9
|
* Ids older than `cutoffMs`, oldest first.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Cn as EventCategory, N as alertsCapability, Xt as errMsg, Zt as BaseAddon, n as AlertSchema, un as createEvent } from "../../dist-4MS1GNc8.mjs";
|
|
2
2
|
//#region src/builtins/alerts/prune-policy.ts
|
|
3
3
|
/**
|
|
4
4
|
* Ids older than `cutoffMs`, oldest first.
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Cz6h6Nc3.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let buffer = require("buffer");
|
|
9
9
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Cn as EventCategory, F as backupCapability, Ut as storageOccupancyCapability, Zt as BaseAddon, xt as mayWriteToLocation } from "../../dist-4MS1GNc8.mjs";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import { Buffer as Buffer$1 } from "buffer";
|
|
4
4
|
import * as fsp from "node:fs/promises";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Cz6h6Nc3.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
//#region src/builtins/console-logging/console-destination.ts
|
|
9
9
|
var LEVEL_RANK = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Zt as BaseAddon, _t as logDestinationCapability } from "../../dist-4MS1GNc8.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/console-logging/console-destination.ts
|
|
4
4
|
var LEVEL_RANK = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
3
|
+
const require_dist = require("../../dist-Cz6h6Nc3.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
let node_fs_promises = require("node:fs/promises");
|
|
6
6
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as coreBlockAddonId, H as coreBlocksCapability, V as coreBlockIdFromAddonId, Xt as
|
|
1
|
+
import { B as coreBlockAddonId, H as coreBlocksCapability, V as coreBlockIdFromAddonId, Xt as errMsg, Zt as BaseAddon, c as CORE_BLOCK_ADDON_PREFIX, d as DeclaredDevices, l as CoreBlockSchema, s as CORE_BLOCKS_ADDON_ID } from "../../dist-4MS1GNc8.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
7
|
-
const require_retired_settings_keys = require("../../retired-settings-keys-
|
|
6
|
+
const require_dist = require("../../dist-Cz6h6Nc3.js");
|
|
7
|
+
const require_retired_settings_keys = require("../../retired-settings-keys-DBrIdOQp.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let _camstack_types_node = require("@camstack/types/node");
|
|
10
10
|
//#region src/builtins/device-manager/adoption-job-engine.ts
|
|
@@ -5037,6 +5037,234 @@ async function setLocationIcon(pctx, input) {
|
|
|
5037
5037
|
});
|
|
5038
5038
|
}
|
|
5039
5039
|
/**
|
|
5040
|
+
* Every location that carries a description, sorted by key.
|
|
5041
|
+
*
|
|
5042
|
+
* Only what was WRITTEN — a location with no description is absent, and the
|
|
5043
|
+
* digest then tells the model the location name alone, exactly as it did
|
|
5044
|
+
* before descriptions existed.
|
|
5045
|
+
*/
|
|
5046
|
+
async function listLocationDescriptions(pctx) {
|
|
5047
|
+
const descriptions = (await pctx.metaStore.readStore()).locationDescriptions ?? {};
|
|
5048
|
+
return Object.entries(descriptions).map(([locationKey, description]) => ({
|
|
5049
|
+
locationKey,
|
|
5050
|
+
description
|
|
5051
|
+
})).toSorted((a, b) => a.locationKey.localeCompare(b.locationKey));
|
|
5052
|
+
}
|
|
5053
|
+
/**
|
|
5054
|
+
* Set (or clear) a location's description.
|
|
5055
|
+
*
|
|
5056
|
+
* A BLANK description clears, exactly as `null` does: an empty line handed to
|
|
5057
|
+
* a vision model costs attention and says nothing, so the two must not be
|
|
5058
|
+
* different states. The label is folded with `locationIconKey` — the same key
|
|
5059
|
+
* the icon uses, which is the whole point of this living here.
|
|
5060
|
+
*
|
|
5061
|
+
* Same read-modify-write and same lock as `setLocationIcon`; `writeAddonStore`
|
|
5062
|
+
* re-inserts the whole key range, so an unordered write carries a stale copy
|
|
5063
|
+
* of every other key.
|
|
5064
|
+
*/
|
|
5065
|
+
async function setLocationDescription(pctx, input) {
|
|
5066
|
+
const trimmed = input.location.trim();
|
|
5067
|
+
if (trimmed.length === 0) throw new Error("[device-manager] setLocationDescription: location must be non-empty");
|
|
5068
|
+
const key = require_dist.locationIconKey(trimmed);
|
|
5069
|
+
const text = input.description === null ? "" : input.description.trim();
|
|
5070
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
5071
|
+
const descriptions = (await pctx.metaStore.readStore()).locationDescriptions ?? {};
|
|
5072
|
+
if (text.length === 0) {
|
|
5073
|
+
if (!(key in descriptions)) return;
|
|
5074
|
+
const { [key]: _cleared, ...kept } = descriptions;
|
|
5075
|
+
await pctx.settings.writeAddonStore({ locationDescriptions: kept });
|
|
5076
|
+
return;
|
|
5077
|
+
}
|
|
5078
|
+
if (descriptions[key] === text) return;
|
|
5079
|
+
await pctx.settings.writeAddonStore({ locationDescriptions: {
|
|
5080
|
+
...descriptions,
|
|
5081
|
+
[key]: text
|
|
5082
|
+
} });
|
|
5083
|
+
});
|
|
5084
|
+
}
|
|
5085
|
+
/**
|
|
5086
|
+
* Every site zone, with its rooms, sorted by key.
|
|
5087
|
+
*
|
|
5088
|
+
* Membership is inverted HERE rather than stored both ways: the store keeps
|
|
5089
|
+
* `location → zone`, which is what makes "one zone per room" structural, and a
|
|
5090
|
+
* second copy keyed the other way is a second thing that can disagree with the
|
|
5091
|
+
* first.
|
|
5092
|
+
*/
|
|
5093
|
+
async function listSiteZones(pctx) {
|
|
5094
|
+
const store = await pctx.metaStore.readStore();
|
|
5095
|
+
const zones = store.siteZones ?? {};
|
|
5096
|
+
const membership = store.locationZones ?? {};
|
|
5097
|
+
const rooms = /* @__PURE__ */ new Map();
|
|
5098
|
+
for (const [locationKey, zoneKey] of Object.entries(membership)) {
|
|
5099
|
+
const list = rooms.get(zoneKey);
|
|
5100
|
+
if (list === void 0) rooms.set(zoneKey, [locationKey]);
|
|
5101
|
+
else list.push(locationKey);
|
|
5102
|
+
}
|
|
5103
|
+
return Object.entries(zones).map(([zoneKey, record]) => ({
|
|
5104
|
+
zoneKey,
|
|
5105
|
+
label: record.label,
|
|
5106
|
+
icon: record.icon ?? null,
|
|
5107
|
+
locationKeys: (rooms.get(zoneKey) ?? []).toSorted((a, b) => a.localeCompare(b))
|
|
5108
|
+
})).toSorted((a, b) => a.zoneKey.localeCompare(b.zoneKey));
|
|
5109
|
+
}
|
|
5110
|
+
/**
|
|
5111
|
+
* Create a zone, or set the icon of one that already exists.
|
|
5112
|
+
*
|
|
5113
|
+
* `icon: null` clears the icon and KEEPS the zone. Conflating the two would
|
|
5114
|
+
* make a mis-click on "no icon" delete every room's membership — the icon is a
|
|
5115
|
+
* decoration and the grouping is the data.
|
|
5116
|
+
*
|
|
5117
|
+
* `icon` OMITTED leaves whatever icon is there: creating a zone from a screen
|
|
5118
|
+
* with no picker must not silently clear the glyph a previous screen set.
|
|
5119
|
+
*/
|
|
5120
|
+
async function setSiteZone(pctx, input) {
|
|
5121
|
+
const label = input.name.trim();
|
|
5122
|
+
if (label.length === 0) throw new Error("[device-manager] setSiteZone: name must be non-empty");
|
|
5123
|
+
const key = require_dist.siteZoneKey(label);
|
|
5124
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
5125
|
+
const zones = (await pctx.metaStore.readStore()).siteZones ?? {};
|
|
5126
|
+
const existing = zones[key];
|
|
5127
|
+
const icon = input.icon === void 0 ? existing?.icon : input.icon === null ? void 0 : input.icon;
|
|
5128
|
+
const next = icon === void 0 ? { label } : {
|
|
5129
|
+
label,
|
|
5130
|
+
icon
|
|
5131
|
+
};
|
|
5132
|
+
if (existing?.label === next.label && existing.icon === next.icon) return;
|
|
5133
|
+
await pctx.settings.writeAddonStore({ siteZones: {
|
|
5134
|
+
...zones,
|
|
5135
|
+
[key]: next
|
|
5136
|
+
} });
|
|
5137
|
+
});
|
|
5138
|
+
}
|
|
5139
|
+
/**
|
|
5140
|
+
* Delete a zone. Its LOCATIONS survive, unattached.
|
|
5141
|
+
*
|
|
5142
|
+
* Deleting a grouping must never delete what was grouped — the rooms exist
|
|
5143
|
+
* independently and the devices in them were never touched to begin with.
|
|
5144
|
+
*/
|
|
5145
|
+
async function removeSiteZone(pctx, input) {
|
|
5146
|
+
const key = require_dist.siteZoneKey(input.name);
|
|
5147
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
5148
|
+
const store = await pctx.metaStore.readStore();
|
|
5149
|
+
const zones = store.siteZones ?? {};
|
|
5150
|
+
const membership = store.locationZones ?? {};
|
|
5151
|
+
const hadZone = key in zones;
|
|
5152
|
+
const orphaned = Object.entries(membership).filter(([, zoneKey]) => zoneKey === key);
|
|
5153
|
+
if (!hadZone && orphaned.length === 0) return;
|
|
5154
|
+
const { [key]: _dropped, ...keptZones } = zones;
|
|
5155
|
+
const keptMembership = Object.fromEntries(Object.entries(membership).filter(([, zoneKey]) => zoneKey !== key));
|
|
5156
|
+
await pctx.settings.writeAddonStore({
|
|
5157
|
+
...hadZone ? { siteZones: keptZones } : {},
|
|
5158
|
+
...orphaned.length > 0 ? { locationZones: keptMembership } : {}
|
|
5159
|
+
});
|
|
5160
|
+
});
|
|
5161
|
+
}
|
|
5162
|
+
/**
|
|
5163
|
+
* Rename a zone, carrying its icon AND every room in it.
|
|
5164
|
+
*
|
|
5165
|
+
* It exists so that renaming is not delete-then-recreate, which drops both.
|
|
5166
|
+
* A rename onto an existing zone MERGES: the target keeps its own icon and the
|
|
5167
|
+
* rooms are unioned, the same rule `renameLocation` follows.
|
|
5168
|
+
*/
|
|
5169
|
+
async function renameSiteZone(pctx, input) {
|
|
5170
|
+
const toLabel = input.to.trim();
|
|
5171
|
+
if (toLabel.length === 0) throw new Error("[device-manager] renameSiteZone: target name must be non-empty");
|
|
5172
|
+
const fromKey = require_dist.siteZoneKey(input.from);
|
|
5173
|
+
const toKey = require_dist.siteZoneKey(toLabel);
|
|
5174
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
5175
|
+
const store = await pctx.metaStore.readStore();
|
|
5176
|
+
const zones = store.siteZones ?? {};
|
|
5177
|
+
const moved = zones[fromKey];
|
|
5178
|
+
if (moved === void 0) return;
|
|
5179
|
+
const { [fromKey]: _dropped, ...rest } = zones;
|
|
5180
|
+
const target = rest[toKey];
|
|
5181
|
+
const nextZones = {
|
|
5182
|
+
...rest,
|
|
5183
|
+
[toKey]: target ?? (moved.icon === void 0 ? { label: toLabel } : {
|
|
5184
|
+
label: toLabel,
|
|
5185
|
+
icon: moved.icon
|
|
5186
|
+
})
|
|
5187
|
+
};
|
|
5188
|
+
const membership = store.locationZones ?? {};
|
|
5189
|
+
const nextMembership = Object.fromEntries(Object.entries(membership).map(([locationKey, zoneKey]) => [locationKey, zoneKey === fromKey ? toKey : zoneKey]));
|
|
5190
|
+
await pctx.settings.writeAddonStore({
|
|
5191
|
+
siteZones: nextZones,
|
|
5192
|
+
locationZones: nextMembership
|
|
5193
|
+
});
|
|
5194
|
+
});
|
|
5195
|
+
}
|
|
5196
|
+
/**
|
|
5197
|
+
* Put a location in a zone, or take it out with `zone: null`.
|
|
5198
|
+
*
|
|
5199
|
+
* ONE slot per location, so a second call moves the room rather than listing it
|
|
5200
|
+
* twice. The zone is created if it does not exist — a screen that assigns
|
|
5201
|
+
* before creating is a normal order, and refusing it would be the only place in
|
|
5202
|
+
* this registry that demands registration first.
|
|
5203
|
+
*/
|
|
5204
|
+
async function setLocationZone(pctx, input) {
|
|
5205
|
+
const location = input.location.trim();
|
|
5206
|
+
if (location.length === 0) throw new Error("[device-manager] setLocationZone: location must be non-empty");
|
|
5207
|
+
const locationKey = require_dist.locationIconKey(location);
|
|
5208
|
+
const label = input.zone === null ? null : input.zone.trim();
|
|
5209
|
+
if (label !== null && label.length === 0) throw new Error("[device-manager] setLocationZone: zone must be non-empty or null");
|
|
5210
|
+
await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
|
|
5211
|
+
const store = await pctx.metaStore.readStore();
|
|
5212
|
+
const membership = store.locationZones ?? {};
|
|
5213
|
+
if (label === null) {
|
|
5214
|
+
if (!(locationKey in membership)) return;
|
|
5215
|
+
const { [locationKey]: _cleared, ...kept } = membership;
|
|
5216
|
+
await pctx.settings.writeAddonStore({ locationZones: kept });
|
|
5217
|
+
return;
|
|
5218
|
+
}
|
|
5219
|
+
const zoneKey = require_dist.siteZoneKey(label);
|
|
5220
|
+
const zones = store.siteZones ?? {};
|
|
5221
|
+
const needsZone = !(zoneKey in zones);
|
|
5222
|
+
if (membership[locationKey] === zoneKey && !needsZone) return;
|
|
5223
|
+
await pctx.settings.writeAddonStore({
|
|
5224
|
+
...needsZone ? { siteZones: {
|
|
5225
|
+
...zones,
|
|
5226
|
+
[zoneKey]: { label }
|
|
5227
|
+
} } : {},
|
|
5228
|
+
locationZones: {
|
|
5229
|
+
...membership,
|
|
5230
|
+
[locationKey]: zoneKey
|
|
5231
|
+
}
|
|
5232
|
+
});
|
|
5233
|
+
});
|
|
5234
|
+
}
|
|
5235
|
+
/**
|
|
5236
|
+
* Move a location's zone membership from one key to another, for
|
|
5237
|
+
* `renameLocation`. Same shape as the icon and description movers.
|
|
5238
|
+
*/
|
|
5239
|
+
function renameLocationZone(membership, fromKey, toKey) {
|
|
5240
|
+
const moved = membership[fromKey];
|
|
5241
|
+
if (moved === void 0) return null;
|
|
5242
|
+
if (fromKey === toKey) return null;
|
|
5243
|
+
const { [fromKey]: _dropped, ...rest } = membership;
|
|
5244
|
+
return toKey in rest ? rest : {
|
|
5245
|
+
...rest,
|
|
5246
|
+
[toKey]: moved
|
|
5247
|
+
};
|
|
5248
|
+
}
|
|
5249
|
+
/**
|
|
5250
|
+
* Move a location's description from one key to another, for `renameLocation`.
|
|
5251
|
+
*
|
|
5252
|
+
* Identical rule to {@link renameLocationIcon}, deliberately: a merge keeps the
|
|
5253
|
+
* TARGET's description, because renaming "Sala" into "Salone" moves devices
|
|
5254
|
+
* into Salone and does not re-describe it. Split from the icon mover only
|
|
5255
|
+
* because the value types differ; if one ever changes, the other must.
|
|
5256
|
+
*/
|
|
5257
|
+
function renameLocationDescription(descriptions, fromKey, toKey) {
|
|
5258
|
+
const moved = descriptions[fromKey];
|
|
5259
|
+
if (moved === void 0) return null;
|
|
5260
|
+
if (fromKey === toKey) return null;
|
|
5261
|
+
const { [fromKey]: _dropped, ...rest } = descriptions;
|
|
5262
|
+
return toKey in rest ? rest : {
|
|
5263
|
+
...rest,
|
|
5264
|
+
[toKey]: moved
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5267
|
+
/**
|
|
5040
5268
|
* Move a location's icon from one key to another, for `renameLocation`.
|
|
5041
5269
|
*
|
|
5042
5270
|
* Returns `null` when NOTHING changes, so the caller can leave the key out of
|
|
@@ -5092,14 +5320,18 @@ async function renameLocation(pctx, input) {
|
|
|
5092
5320
|
const current = store.locations ?? [];
|
|
5093
5321
|
const registered = current.some((l) => l.trim().toLowerCase() === fromKey);
|
|
5094
5322
|
const movedIcons = renameLocationIcon(store.locationIcons ?? {}, fromKey, toKey);
|
|
5095
|
-
|
|
5323
|
+
const movedDescriptions = renameLocationDescription(store.locationDescriptions ?? {}, fromKey, toKey);
|
|
5324
|
+
const movedZones = renameLocationZone(store.locationZones ?? {}, fromKey, toKey);
|
|
5325
|
+
if (!registered && movedIcons === null && movedDescriptions === null && movedZones === null) return;
|
|
5096
5326
|
const kept = current.filter((l) => {
|
|
5097
5327
|
const key = l.trim().toLowerCase();
|
|
5098
5328
|
return key !== fromKey && key !== toKey;
|
|
5099
5329
|
});
|
|
5100
5330
|
await pctx.settings.writeAddonStore({
|
|
5101
5331
|
...registered ? { locations: [...kept, to] } : {},
|
|
5102
|
-
...movedIcons !== null ? { locationIcons: movedIcons } : {}
|
|
5332
|
+
...movedIcons !== null ? { locationIcons: movedIcons } : {},
|
|
5333
|
+
...movedDescriptions !== null ? { locationDescriptions: movedDescriptions } : {},
|
|
5334
|
+
...movedZones !== null ? { locationZones: movedZones } : {}
|
|
5103
5335
|
});
|
|
5104
5336
|
});
|
|
5105
5337
|
const moved = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
@@ -5145,11 +5377,19 @@ async function removeLocation(pctx, input) {
|
|
|
5145
5377
|
const droppedLabel = remaining.length !== current.length;
|
|
5146
5378
|
const icons = store.locationIcons ?? {};
|
|
5147
5379
|
const droppedIcon = key in icons;
|
|
5148
|
-
|
|
5380
|
+
const descriptions = store.locationDescriptions ?? {};
|
|
5381
|
+
const droppedDescription = key in descriptions;
|
|
5382
|
+
const membership = store.locationZones ?? {};
|
|
5383
|
+
const droppedZone = key in membership;
|
|
5384
|
+
if (!droppedLabel && !droppedIcon && !droppedDescription && !droppedZone) return;
|
|
5149
5385
|
const { [key]: _removedIcon, ...keptIcons } = icons;
|
|
5386
|
+
const { [key]: _removedDescription, ...keptDescriptions } = descriptions;
|
|
5387
|
+
const { [key]: _removedZone, ...keptMembership } = membership;
|
|
5150
5388
|
await pctx.settings.writeAddonStore({
|
|
5151
5389
|
...droppedLabel ? { locations: remaining } : {},
|
|
5152
|
-
...droppedIcon ? { locationIcons: keptIcons } : {}
|
|
5390
|
+
...droppedIcon ? { locationIcons: keptIcons } : {},
|
|
5391
|
+
...droppedDescription ? { locationDescriptions: keptDescriptions } : {},
|
|
5392
|
+
...droppedZone ? { locationZones: keptMembership } : {}
|
|
5153
5393
|
});
|
|
5154
5394
|
});
|
|
5155
5395
|
if (input.cascade !== true) return;
|
|
@@ -5253,11 +5493,17 @@ function decodeAddonStore(raw) {
|
|
|
5253
5493
|
const locations = raw["locations"];
|
|
5254
5494
|
const roleDisplayDefaults = raw["roleDisplayDefaults"];
|
|
5255
5495
|
const locationIcons = decodeLocationIcons(raw["locationIcons"]);
|
|
5496
|
+
const locationDescriptions = decodeLocationDescriptions(raw["locationDescriptions"]);
|
|
5497
|
+
const siteZones = decodeSiteZones(raw["siteZones"]);
|
|
5498
|
+
const locationZones = decodeStringMap(raw["locationZones"]);
|
|
5256
5499
|
return {
|
|
5257
5500
|
...typeof nextDeviceId === "number" && Number.isFinite(nextDeviceId) ? { nextDeviceId } : {},
|
|
5258
5501
|
...Array.isArray(locations) ? { locations: locations.filter((l) => typeof l === "string") } : {},
|
|
5259
5502
|
...isRoleDisplayDefaults(roleDisplayDefaults) ? { roleDisplayDefaults } : {},
|
|
5260
|
-
...locationIcons !== null ? { locationIcons } : {}
|
|
5503
|
+
...locationIcons !== null ? { locationIcons } : {},
|
|
5504
|
+
...locationDescriptions !== null ? { locationDescriptions } : {},
|
|
5505
|
+
...siteZones !== null ? { siteZones } : {},
|
|
5506
|
+
...locationZones !== null ? { locationZones } : {}
|
|
5261
5507
|
};
|
|
5262
5508
|
}
|
|
5263
5509
|
/**
|
|
@@ -5281,6 +5527,61 @@ function decodeLocationIcons(raw) {
|
|
|
5281
5527
|
}
|
|
5282
5528
|
return out;
|
|
5283
5529
|
}
|
|
5530
|
+
/**
|
|
5531
|
+
* Decode the `locationDescriptions` map, dropping every entry that is not a
|
|
5532
|
+
* non-blank string.
|
|
5533
|
+
*
|
|
5534
|
+
* Same posture as the icons: a blank description and a missing one are the
|
|
5535
|
+
* same instruction to every reader, and storing the first would send an empty
|
|
5536
|
+
* line to a vision model. Over-long text is TRUNCATED rather than dropped —
|
|
5537
|
+
* losing the whole description because a hand-edit ran long is a worse answer
|
|
5538
|
+
* than losing its tail.
|
|
5539
|
+
*/
|
|
5540
|
+
function decodeLocationDescriptions(raw) {
|
|
5541
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
5542
|
+
const out = {};
|
|
5543
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
5544
|
+
if (typeof value !== "string") continue;
|
|
5545
|
+
const text = value.trim();
|
|
5546
|
+
if (text.length === 0) continue;
|
|
5547
|
+
out[key] = text.slice(0, 400);
|
|
5548
|
+
}
|
|
5549
|
+
return out;
|
|
5550
|
+
}
|
|
5551
|
+
/**
|
|
5552
|
+
* Decode the zone registry, dropping any entry without a usable label.
|
|
5553
|
+
*
|
|
5554
|
+
* A zone whose label did not survive a hand-edit cannot be shown or renamed —
|
|
5555
|
+
* it would be a row the operator can see the effects of and never name. An
|
|
5556
|
+
* icon outside the vocabulary is dropped on its own, leaving the zone: losing
|
|
5557
|
+
* a glyph is a smaller loss than losing the grouping.
|
|
5558
|
+
*/
|
|
5559
|
+
function decodeSiteZones(raw) {
|
|
5560
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
5561
|
+
const out = {};
|
|
5562
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
5563
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) continue;
|
|
5564
|
+
const rec = value;
|
|
5565
|
+
const label = typeof rec["label"] === "string" ? rec["label"].trim() : "";
|
|
5566
|
+
if (label.length === 0) continue;
|
|
5567
|
+
const icon = require_dist.LocationIconIdSchema.safeParse(rec["icon"]);
|
|
5568
|
+
out[key] = icon.success ? {
|
|
5569
|
+
label,
|
|
5570
|
+
icon: icon.data
|
|
5571
|
+
} : { label };
|
|
5572
|
+
}
|
|
5573
|
+
return out;
|
|
5574
|
+
}
|
|
5575
|
+
/** Decode a plain `string → string` map, dropping anything else. */
|
|
5576
|
+
function decodeStringMap(raw) {
|
|
5577
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
5578
|
+
const out = {};
|
|
5579
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
5580
|
+
if (typeof value !== "string" || value.trim().length === 0) continue;
|
|
5581
|
+
out[key] = value;
|
|
5582
|
+
}
|
|
5583
|
+
return out;
|
|
5584
|
+
}
|
|
5284
5585
|
function isRoleDisplayDefaults(value) {
|
|
5285
5586
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5286
5587
|
}
|
|
@@ -7115,6 +7416,13 @@ var DeviceManagerAddon = class extends require_dist.BaseAddon {
|
|
|
7115
7416
|
renameLocation: (input) => renameLocation(pctx, input),
|
|
7116
7417
|
listLocationIcons: () => listLocationIcons(pctx),
|
|
7117
7418
|
setLocationIcon: (input) => setLocationIcon(pctx, input),
|
|
7419
|
+
listLocationDescriptions: () => listLocationDescriptions(pctx),
|
|
7420
|
+
setLocationDescription: (input) => setLocationDescription(pctx, input),
|
|
7421
|
+
listSiteZones: () => listSiteZones(pctx),
|
|
7422
|
+
setSiteZone: (input) => setSiteZone(pctx, input),
|
|
7423
|
+
removeSiteZone: (input) => removeSiteZone(pctx, input),
|
|
7424
|
+
renameSiteZone: (input) => renameSiteZone(pctx, input),
|
|
7425
|
+
setLocationZone: (input) => setLocationZone(pctx, input),
|
|
7118
7426
|
listPersistedByAddon: (input) => listPersistedByAddon(pctx, input),
|
|
7119
7427
|
listAll: (input) => listAll(pctx, input),
|
|
7120
7428
|
getDevice: (input) => getDevice(pctx, input),
|