@camstack/ui-library 1.2.304 → 1.2.306

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.
Files changed (41) hide show
  1. package/dist/{MotionZonesSettings-C3Tj12yc.js → MotionZonesSettings-DR1g9ja3.js} +1 -1
  2. package/dist/{PrivacyMaskSettings-C9wfHkiB.js → PrivacyMaskSettings-CtetT6Jl.js} +1 -1
  3. package/dist/{SceneMonitorEditor-30H29vN4.js → SceneMonitorEditor-BQhVO2F2.js} +1 -1
  4. package/dist/composites/cap-settings/EventHeatmap.d.ts +8 -2
  5. package/dist/composites/charts/BarSeries.d.ts +49 -0
  6. package/dist/composites/charts/Sparkline.d.ts +26 -0
  7. package/dist/composites/charts/StateTimeline.d.ts +47 -0
  8. package/dist/composites/charts/TimeSeriesChart.d.ts +48 -0
  9. package/dist/composites/charts/bar-series-model.d.ts +25 -0
  10. package/dist/composites/charts/chart-data.d.ts +87 -0
  11. package/dist/composites/charts/chart-frame.d.ts +53 -0
  12. package/dist/composites/charts/chart-palette.d.ts +46 -0
  13. package/dist/composites/charts/chart-scale.d.ts +29 -0
  14. package/dist/composites/charts/chart-uplot.d.ts +44 -0
  15. package/dist/composites/charts/chart-window.d.ts +19 -0
  16. package/dist/composites/charts/index.d.ts +16 -0
  17. package/dist/composites/charts/time-series-model.d.ts +47 -0
  18. package/dist/composites/charts/uplot-host.d.ts +34 -0
  19. package/dist/composites/device-history/DeviceHistoryView.d.ts +26 -0
  20. package/dist/composites/device-history/HistoryChart.d.ts +31 -0
  21. package/dist/composites/device-history/HistoryWindowBar.d.ts +16 -0
  22. package/dist/composites/device-history/history-model.d.ts +121 -0
  23. package/dist/composites/device-history/history-reads.d.ts +43 -0
  24. package/dist/composites/device-history/history-selection.d.ts +55 -0
  25. package/dist/composites/device-history/history-window.d.ts +31 -0
  26. package/dist/composites/device-history/index.d.ts +14 -0
  27. package/dist/composites/device-history/use-device-history.d.ts +37 -0
  28. package/dist/composites/device-history/use-history-panels.d.ts +10 -0
  29. package/dist/composites/index.d.ts +2 -0
  30. package/dist/composites/log-history-paging.d.ts +62 -0
  31. package/dist/composites/log-stream.d.ts +6 -10
  32. package/dist/generated/system-hooks.d.ts +2 -0
  33. package/dist/hooks/use-log-history.d.ts +26 -0
  34. package/dist/{icons-B1AFXUaZ.cjs → icons-BTux33dH.cjs} +2 -35
  35. package/dist/{icons-BT8c2hxp.js → icons-D178phML.js} +1 -34
  36. package/dist/index.cjs +3509 -122
  37. package/dist/index.js +3430 -129
  38. package/dist/uPlot.esm-BbxBUnav.cjs +3767 -0
  39. package/dist/uPlot.esm-C_8LiAPm.js +3767 -0
  40. package/package.json +2 -1
  41. package/src/tailwind/camstack-theme.css +104 -2
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import * as __mfHostReact from "react";
5
5
  import React, { Fragment, Suspense, cloneElement, createContext, createElement, forwardRef, lazy, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
6
6
  import * as __mfHostReactJsxRuntime from "react/jsx-runtime";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
- import { ALL_CAPABILITY_DEFINITIONS, CAM_PROFILE_ORDER, CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, CONTAINER_CHILD_PRIORITY, DEFAULT_EVENTS_BAND_BUFFER_SEC, DEFAULT_EVENT_COLOR, DEVICE_TYPE_CONTROL_KIND, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EVENT_TAXONOMY, EXTENSION_SLOTS, EXTENSION_SLOT_IDS, EventCategory, ExposedDeviceSchema, FIRST_LEVEL_MACRO_CLASSES, ICON_CATEGORIES, KNOWN_CAP_NAMES, MAX_SENSOR_TRIGGER_DEVICES, MODEL_PROVIDER_IDS, NAVIGATION_LIGHT_LEVEL_MAX, NAVIGATION_LIGHT_LEVEL_MIN, NODE_COLOR_IDS, NodeColorIdSchema, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RECORDING_AUDIO_TRIGGER_OFFERED_DBFS, RECORDING_SOURCE_CAMSTACK_ADDON, REDACTED_SECRET, RecordingConfigSchema, RecordingStatusSchema, SECRET_CLEAR, SOURCE_CAP_ACTIVE_FIELD, STATIONARY_OVERLAY_STYLE, TIMEZONES, USE_ROOT_MODEL_SETTING_KEY, assignNodeColorId, assignNodeColorId as assignNodeColorId$1, buildModelVariantGroups, canConvertUnit, canonicalIconKey, clearModelBoundOverrides, clearModelBoundSetting, clusterStepSettingFieldsFor, collectModelBoundKeys, createDeviceProxy, createLazyTrpcSource, createSliceHandle, dehydrateSchema, deriveRecordingMode, describeModelVariant, effectiveStepSettings, findTimezone, formatParkedFor, hasModelBoundOverrides, iconCategoryLabel, iconsInCategory, isKeychainStoredField, isRootSuppliableStepId, isSecretClear, isSecretFieldView, modelBindingKey, normalizeUnit, publicEventCategories, resolveContainerPrimaryChild, resolveDeviceControlKind, resolveIconKey, resolveModelBoundSetting, resolveRecordingProfiles, resolveRootSupply, resolveVariantModelId, resolveViewableDeviceIds, searchIcons, selectAssignedProfileSlots, stepSettingSource, tryConvertUnit, unitsForDimension, withFieldFrom, withoutField, writeModelBoundSetting } from "@camstack/types";
8
+ import { ALL_CAPABILITY_DEFINITIONS, CAM_PROFILE_ORDER, CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, CONTAINER_CHILD_PRIORITY, DEFAULT_EVENTS_BAND_BUFFER_SEC, DEFAULT_EVENT_COLOR, DEVICE_HISTORY_ENERGY_KEY, DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS, DEVICE_TYPE_CONTROL_KIND, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EVENT_TAXONOMY, EXTENSION_SLOTS, EXTENSION_SLOT_IDS, EventCategory, ExposedDeviceSchema, FIRST_LEVEL_MACRO_CLASSES, HISTORY_CATALOG, ICON_CATEGORIES, KNOWN_CAP_NAMES, MAX_SENSOR_TRIGGER_DEVICES, MODEL_PROVIDER_IDS, NAVIGATION_LIGHT_LEVEL_MAX, NAVIGATION_LIGHT_LEVEL_MIN, NODE_COLOR_IDS, NodeColorIdSchema, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RECORDING_AUDIO_TRIGGER_OFFERED_DBFS, RECORDING_SOURCE_CAMSTACK_ADDON, REDACTED_SECRET, RecordingConfigSchema, RecordingStatusSchema, SECRET_CLEAR, SOURCE_CAP_ACTIVE_FIELD, STATIONARY_OVERLAY_STYLE, TIMEZONES, USE_ROOT_MODEL_SETTING_KEY, assignNodeColorId, assignNodeColorId as assignNodeColorId$1, buildModelVariantGroups, canConvertUnit, canonicalIconKey, clearModelBoundOverrides, clearModelBoundSetting, clusterStepSettingFieldsFor, collectModelBoundKeys, createDeviceProxy, createLazyTrpcSource, createSliceHandle, dehydrateSchema, deriveRecordingMode, describeModelVariant, effectiveStepSettings, findTimezone, formatParkedFor, hasModelBoundOverrides, iconCategoryLabel, iconsInCategory, isKeychainStoredField, isRootSuppliableStepId, isSecretClear, isSecretFieldView, modelBindingKey, normalizeUnit, parseOccupancyScope, publicEventCategories, resolveContainerPrimaryChild, resolveDeviceControlKind, resolveIconKey, resolveModelBoundSetting, resolveRecordingProfiles, resolveRootSupply, resolveVariantModelId, resolveViewableDeviceIds, searchIcons, selectAssignedProfileSlots, stepSettingSource, tryConvertUnit, unitsForDimension, withFieldFrom, withoutField, writeModelBoundSetting } from "@camstack/types";
9
9
  import * as __mfHostTrpcReactQuery from "@trpc/react-query";
10
10
  import { createTRPCReact } from "@trpc/react-query";
11
11
  import * as __mfHostReactDOM from "react-dom";
@@ -864,6 +864,19 @@ var Cat = createLucideIcon("cat", [
864
864
  * This source code is licensed under the ISC license.
865
865
  * See the LICENSE file in the root directory of this source tree.
866
866
  */
867
+ var ChartLine = createLucideIcon("chart-line", [["path", {
868
+ d: "M3 3v16a2 2 0 0 0 2 2h16",
869
+ key: "c24i48"
870
+ }], ["path", {
871
+ d: "m19 9-5 5-4-4-3 3",
872
+ key: "2osh9i"
873
+ }]]);
874
+ /**
875
+ * @license lucide-react v0.576.0 - ISC
876
+ *
877
+ * This source code is licensed under the ISC license.
878
+ * See the LICENSE file in the root directory of this source tree.
879
+ */
867
880
  var Check = createLucideIcon("check", [["path", {
868
881
  d: "M20 6 9 17l-5-5",
869
882
  key: "1gmf2c"
@@ -2209,6 +2222,26 @@ var Heater = createLucideIcon("heater", [
2209
2222
  * This source code is licensed under the ISC license.
2210
2223
  * See the LICENSE file in the root directory of this source tree.
2211
2224
  */
2225
+ var History = createLucideIcon("history", [
2226
+ ["path", {
2227
+ d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",
2228
+ key: "1357e3"
2229
+ }],
2230
+ ["path", {
2231
+ d: "M3 3v5h5",
2232
+ key: "1xhq8a"
2233
+ }],
2234
+ ["path", {
2235
+ d: "M12 7v5l4 2",
2236
+ key: "1fdv2h"
2237
+ }]
2238
+ ]);
2239
+ /**
2240
+ * @license lucide-react v0.576.0 - ISC
2241
+ *
2242
+ * This source code is licensed under the ISC license.
2243
+ * See the LICENSE file in the root directory of this source tree.
2244
+ */
2212
2245
  var House = createLucideIcon("house", [["path", {
2213
2246
  d: "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",
2214
2247
  key: "5wwlr5"
@@ -5795,7 +5828,7 @@ function allDeviceTypeFilterOptions(t = defaultUiText) {
5795
5828
  */
5796
5829
  var fullTable;
5797
5830
  var pending;
5798
- var failure;
5831
+ var failure$1;
5799
5832
  var version = 0;
5800
5833
  var listeners$1 = /* @__PURE__ */ new Set();
5801
5834
  function publish() {
@@ -5812,12 +5845,12 @@ function lucideExportName(key) {
5812
5845
  */
5813
5846
  function loadIconGlyphs() {
5814
5847
  if (fullTable !== void 0) return Promise.resolve();
5815
- if (failure !== void 0) return Promise.reject(failure);
5816
- pending ??= import("./icons-BT8c2hxp.js").then((module) => {
5848
+ if (failure$1 !== void 0) return Promise.reject(failure$1);
5849
+ pending ??= import("./icons-D178phML.js").then((module) => {
5817
5850
  fullTable = module;
5818
5851
  publish();
5819
5852
  }, (error) => {
5820
- failure = error;
5853
+ failure$1 = error;
5821
5854
  publish();
5822
5855
  throw error;
5823
5856
  });
@@ -5825,7 +5858,7 @@ function loadIconGlyphs() {
5825
5858
  }
5826
5859
  /** Has the lazy glyph chunk failed to load? (Then only the fast table answers.) */
5827
5860
  function iconGlyphsFailed() {
5828
- return failure !== void 0;
5861
+ return failure$1 !== void 0;
5829
5862
  }
5830
5863
  /** Is the full table loaded? */
5831
5864
  function iconGlyphsLoaded() {
@@ -5842,7 +5875,7 @@ function resolveDisplayIcon(name) {
5842
5875
  const fast = FAST_ICON_GLYPHS[key];
5843
5876
  if (fast !== void 0) return fast;
5844
5877
  if (fullTable !== void 0) return fullTable[lucideExportName(key)];
5845
- if (failure === void 0) loadIconGlyphs().catch(() => void 0);
5878
+ if (failure$1 === void 0) loadIconGlyphs().catch(() => void 0);
5846
5879
  }
5847
5880
  function subscribe(listener) {
5848
5881
  listeners$1.add(listener);
@@ -9564,7 +9597,7 @@ var TILE_ROW_PX = 32;
9564
9597
  /** Matches the `h-72` of the scroll area. */
9565
9598
  var VIEWPORT_PX = 288;
9566
9599
  var OVERSCAN_PX = 96;
9567
- function chunk(keys) {
9600
+ function chunk$1(keys) {
9568
9601
  const rows = [];
9569
9602
  for (let i = 0; i < keys.length; i += TILES_PER_ROW) rows.push(keys.slice(i, i + TILES_PER_ROW));
9570
9603
  return rows;
@@ -9574,7 +9607,7 @@ function heightOf(row) {
9574
9607
  }
9575
9608
  /** An empty query shows every category as a section; a query shows one ranked list. */
9576
9609
  function buildRows(query, extraWords) {
9577
- if (query.trim().length > 0) return chunk(searchIcons(query, extraWords).map((h) => h.entry.key)).map((row, i) => ({
9610
+ if (query.trim().length > 0) return chunk$1(searchIcons(query, extraWords).map((h) => h.entry.key)).map((row, i) => ({
9578
9611
  kind: "tiles",
9579
9612
  id: `r${i}`,
9580
9613
  keys: row
@@ -9585,7 +9618,7 @@ function buildRows(query, extraWords) {
9585
9618
  kind: "header",
9586
9619
  id: `h-${category}`,
9587
9620
  category
9588
- }, ...chunk(keys).map((row, i) => ({
9621
+ }, ...chunk$1(keys).map((row, i) => ({
9589
9622
  kind: "tiles",
9590
9623
  id: `${category}-${i}`,
9591
9624
  keys: row
@@ -10730,6 +10763,8 @@ var useDeviceHistoryListNodeSeries = trpc.deviceHistory.listNodeSeries.useQuery;
10730
10763
  var useDeviceHistoryQuery = trpc.deviceHistory.query.useQuery;
10731
10764
  /** Generated alias around `trpc.deviceHistory.queryNode.useQuery`. */
10732
10765
  var useDeviceHistoryQueryNode = trpc.deviceHistory.queryNode.useQuery;
10766
+ /** Generated alias around `trpc.deviceHistory.queryMany.useQuery`. */
10767
+ var useDeviceHistoryQueryMany = trpc.deviceHistory.queryMany.useQuery;
10733
10768
  /** Generated alias around `trpc.deviceHistory.declareSeries.useMutation`. */
10734
10769
  var useDeviceHistoryDeclareSeries = trpc.deviceHistory.declareSeries.useMutation;
10735
10770
  /** Generated alias around `trpc.deviceHistory.record.useMutation`. */
@@ -20859,7 +20894,7 @@ function bucketMsForSpan(spanMs) {
20859
20894
  * - `pct` is via pctForMs (first=0, last=100).
20860
20895
  * - `label` is a localised HH:MM clock string.
20861
20896
  */
20862
- function axisTicks(fromMs, toMs, count) {
20897
+ function axisTicks$1(fromMs, toMs, count) {
20863
20898
  if (count <= 1) return [{
20864
20899
  pct: 0,
20865
20900
  label: new Date(fromMs).toLocaleTimeString(void 0, {
@@ -21065,43 +21100,1193 @@ function ErrorBox({ message, title, onRetry, action, variant = "danger", classNa
21065
21100
  });
21066
21101
  }
21067
21102
  //#endregion
21103
+ //#region src/composites/charts/chart-palette.ts
21104
+ /**
21105
+ * The chart family's colours — theme tokens only.
21106
+ *
21107
+ * Categorical identity comes from the eight `--color-chart-series-*` tokens in
21108
+ * `tailwind/camstack-theme.css`, in FIXED order (a blue, b magenta, c yellow,
21109
+ * d aqua, e orange, f violet, g green, h red). A series keeps its slot for as
21110
+ * long as it is on screen: the caller assigns the index, never the rank, so a
21111
+ * filter that hides one series does not repaint the others.
21112
+ *
21113
+ * Past eight, a slot is reused with a dash pattern (composite encoding) — a
21114
+ * generated ninth hue would be indistinguishable from one of the eight.
21115
+ *
21116
+ * Canvas cannot read `var(--…)`, so a token is resolved through
21117
+ * `getComputedStyle` at DRAW time: a theme switch (`.dark` / `.light` on
21118
+ * `<html>`) followed by a redraw picks the new value up with no re-creation.
21119
+ */
21120
+ /** The categorical slots, in their fixed order. */
21121
+ var CHART_SERIES_TOKENS = [
21122
+ "chart-series-a",
21123
+ "chart-series-b",
21124
+ "chart-series-c",
21125
+ "chart-series-d",
21126
+ "chart-series-e",
21127
+ "chart-series-f",
21128
+ "chart-series-g",
21129
+ "chart-series-h"
21130
+ ];
21131
+ /** Dash patterns (canvas `setLineDash`, CSS px) for the 1st, 2nd and 3rd lap of the palette. */
21132
+ var DASH_LAPS = [
21133
+ [],
21134
+ [6, 4],
21135
+ [2, 3]
21136
+ ];
21137
+ /** The style of the series at `index` (0-based, its stable identity slot). */
21138
+ function chartSeriesStyle(index) {
21139
+ const i = Math.max(0, Math.floor(index));
21140
+ return {
21141
+ token: CHART_SERIES_TOKENS[i % CHART_SERIES_TOKENS.length] ?? "chart-series-a",
21142
+ dash: DASH_LAPS[Math.floor(i / CHART_SERIES_TOKENS.length) % DASH_LAPS.length] ?? []
21143
+ };
21144
+ }
21145
+ /** Spelled out so Tailwind finds every class (a built class name emits no rule). */
21146
+ var SWATCH_FILL = {
21147
+ "chart-series-a": "bg-chart-series-a",
21148
+ "chart-series-b": "bg-chart-series-b",
21149
+ "chart-series-c": "bg-chart-series-c",
21150
+ "chart-series-d": "bg-chart-series-d",
21151
+ "chart-series-e": "bg-chart-series-e",
21152
+ "chart-series-f": "bg-chart-series-f",
21153
+ "chart-series-g": "bg-chart-series-g",
21154
+ "chart-series-h": "bg-chart-series-h",
21155
+ border: "bg-border"
21156
+ };
21157
+ var SWATCH_LINE = {
21158
+ "chart-series-a": "border-chart-series-a",
21159
+ "chart-series-b": "border-chart-series-b",
21160
+ "chart-series-c": "border-chart-series-c",
21161
+ "chart-series-d": "border-chart-series-d",
21162
+ "chart-series-e": "border-chart-series-e",
21163
+ "chart-series-f": "border-chart-series-f",
21164
+ "chart-series-g": "border-chart-series-g",
21165
+ "chart-series-h": "border-chart-series-h",
21166
+ border: "border-border"
21167
+ };
21168
+ /** The utility that fills a swatch with `token`. */
21169
+ function swatchFillClass(token) {
21170
+ return SWATCH_FILL[token];
21171
+ }
21172
+ /** The utility that colours a dashed swatch's line with `token`. */
21173
+ function swatchLineClass(token) {
21174
+ return SWATCH_LINE[token];
21175
+ }
21176
+ /**
21177
+ * Resolve a theme token to the colour the canvas paints. Reads the computed
21178
+ * value of `--color-<token>` on the chart's own root (so a scoped theme block
21179
+ * wins); answers `transparent` when the document has no value (a test DOM).
21180
+ */
21181
+ function resolveChartColor(token, el) {
21182
+ if (typeof window === "undefined" || typeof getComputedStyle !== "function") return "transparent";
21183
+ const root = el ?? document.documentElement;
21184
+ const value = getComputedStyle(root).getPropertyValue(`--color-${token}`).trim();
21185
+ return value.length > 0 ? value : "transparent";
21186
+ }
21187
+ var SIX_DIGIT_HEX = /^#[0-9a-f]{6}$/i;
21188
+ /**
21189
+ * `color` with `alpha` (0..1). A resolved token is a six-digit hex, which
21190
+ * becomes the eight-digit form every canvas accepts; anything else falls back
21191
+ * to `color-mix`.
21192
+ */
21193
+ function withAlpha(color, alpha) {
21194
+ const a = Math.min(1, Math.max(0, alpha));
21195
+ if (SIX_DIGIT_HEX.test(color)) return `${color}${Math.round(a * 255).toString(16).padStart(2, "0")}`;
21196
+ return `color-mix(in srgb, ${color} ${String(Math.round(a * 100))}%, transparent)`;
21197
+ }
21198
+ //#endregion
21199
+ //#region src/composites/charts/chart-frame.tsx
21200
+ function ChartFrame({ title, actions, readoutTime, legend, summary, footer, placeholder, controls, className, children }) {
21201
+ const single = legend.length === 1 ? legend[0] : void 0;
21202
+ return /* @__PURE__ */ jsxs("figure", {
21203
+ className: cn("min-w-0 m-0", className),
21204
+ "data-chart-frame": "",
21205
+ children: [
21206
+ /* @__PURE__ */ jsxs("div", {
21207
+ className: "flex items-baseline justify-between gap-2 mb-1 min-w-0",
21208
+ children: [/* @__PURE__ */ jsx("figcaption", {
21209
+ className: "text-[10px] uppercase tracking-wider text-foreground-subtle truncate min-w-0",
21210
+ children: title
21211
+ }), /* @__PURE__ */ jsxs("div", {
21212
+ className: "flex items-center gap-2 shrink-0",
21213
+ children: [
21214
+ readoutTime && /* @__PURE__ */ jsx("span", {
21215
+ className: "text-[10px] tabular-nums text-foreground-subtle",
21216
+ children: readoutTime
21217
+ }),
21218
+ single?.value !== void 0 && placeholder === void 0 && /* @__PURE__ */ jsx(LegendValue, { entry: single }),
21219
+ actions
21220
+ ]
21221
+ })]
21222
+ }),
21223
+ placeholder ?? /* @__PURE__ */ jsx("div", {
21224
+ role: "img",
21225
+ "aria-label": summary,
21226
+ className: "min-w-0",
21227
+ children
21228
+ }),
21229
+ placeholder === void 0 && controls,
21230
+ /* @__PURE__ */ jsx("p", {
21231
+ className: "sr-only",
21232
+ children: summary
21233
+ }),
21234
+ legend.length > 1 && placeholder === void 0 && /* @__PURE__ */ jsx("ul", {
21235
+ className: "flex flex-wrap items-center gap-x-3 gap-y-0.5 mt-1 list-none p-0 m-0",
21236
+ children: legend.map((entry) => /* @__PURE__ */ jsx("li", {
21237
+ className: "min-w-0",
21238
+ children: /* @__PURE__ */ jsx(LegendItem, { entry })
21239
+ }, entry.id))
21240
+ }),
21241
+ footer && /* @__PURE__ */ jsx("div", {
21242
+ className: "mt-1 text-[10px] text-foreground-subtle",
21243
+ children: footer
21244
+ })
21245
+ ]
21246
+ });
21247
+ }
21248
+ /** The swatch: a filled square, or a dashed bar for a dashed series. */
21249
+ function ChartSwatch({ token, dashed }) {
21250
+ return dashed ? /* @__PURE__ */ jsx("span", {
21251
+ "aria-hidden": "true",
21252
+ className: cn("inline-block h-0 w-3 border-t-2 border-dashed shrink-0", swatchLineClass(token))
21253
+ }) : /* @__PURE__ */ jsx("span", {
21254
+ "aria-hidden": "true",
21255
+ className: cn("inline-block h-2 w-2 rounded-[2px] shrink-0", swatchFillClass(token))
21256
+ });
21257
+ }
21258
+ function LegendItem({ entry }) {
21259
+ return /* @__PURE__ */ jsxs("span", {
21260
+ className: cn("inline-flex items-center gap-1 text-[10px] min-w-0", entry.muted ? "text-foreground-subtle" : "text-foreground-muted"),
21261
+ children: [
21262
+ /* @__PURE__ */ jsx(ChartSwatch, {
21263
+ token: entry.token,
21264
+ dashed: entry.dashed
21265
+ }),
21266
+ /* @__PURE__ */ jsx("span", {
21267
+ className: "truncate",
21268
+ children: entry.label
21269
+ }),
21270
+ entry.value !== void 0 && /* @__PURE__ */ jsx("span", {
21271
+ className: "tabular-nums text-foreground",
21272
+ children: entry.value
21273
+ })
21274
+ ]
21275
+ });
21276
+ }
21277
+ function LegendValue({ entry }) {
21278
+ return /* @__PURE__ */ jsxs("span", {
21279
+ className: "inline-flex items-center gap-1 text-[11px] tabular-nums text-foreground",
21280
+ children: [/* @__PURE__ */ jsx(ChartSwatch, {
21281
+ token: entry.token,
21282
+ dashed: entry.dashed
21283
+ }), entry.value]
21284
+ });
21285
+ }
21286
+ /** The plot-sized box for an empty or pending chart: a dashed outline and one line of text. */
21287
+ function ChartPlaceholder({ message, height }) {
21288
+ return /* @__PURE__ */ jsx("div", {
21289
+ className: cn(height === "sm" ? "h-8" : height === "md" ? "h-24" : "h-40", "flex items-center justify-center rounded border border-dashed border-border text-[10px] text-foreground-subtle text-center px-2"),
21290
+ children: message
21291
+ });
21292
+ }
21293
+ //#endregion
21294
+ //#region src/composites/charts/chart-data.ts
21295
+ /** Join several series onto one x array, inserting a `null` inside every hole wider than its `gapMs`. */
21296
+ function alignSeries(inputs) {
21297
+ const sorted = inputs.map((s) => [...s.points].sort((a, b) => a.t - b.t));
21298
+ const breaks = sorted.map((pts, i) => gapBreaks(pts, inputs[i]?.gapMs));
21299
+ const xset = /* @__PURE__ */ new Set();
21300
+ for (const pts of sorted) for (const p of pts) xset.add(p.t);
21301
+ for (const b of breaks) for (const t of b) xset.add(t);
21302
+ const xs = [...xset].sort((a, b) => a - b);
21303
+ const index = /* @__PURE__ */ new Map();
21304
+ xs.forEach((x, i) => index.set(x, i));
21305
+ const blank = () => xs.map(() => void 0);
21306
+ const ys = [];
21307
+ const mins = [];
21308
+ const maxs = [];
21309
+ sorted.forEach((pts, si) => {
21310
+ const y = blank();
21311
+ const lo = blank();
21312
+ const hi = blank();
21313
+ for (const t of breaks[si] ?? []) {
21314
+ const i = index.get(t);
21315
+ if (i !== void 0) {
21316
+ y[i] = null;
21317
+ lo[i] = null;
21318
+ hi[i] = null;
21319
+ }
21320
+ }
21321
+ for (const p of pts) {
21322
+ const i = index.get(p.t);
21323
+ if (i === void 0) continue;
21324
+ y[i] = p.v;
21325
+ if (p.min !== void 0) lo[i] = p.min;
21326
+ if (p.max !== void 0) hi[i] = p.max;
21327
+ }
21328
+ ys.push(y);
21329
+ mins.push(lo);
21330
+ maxs.push(hi);
21331
+ });
21332
+ return {
21333
+ xs,
21334
+ ys,
21335
+ mins,
21336
+ maxs
21337
+ };
21338
+ }
21339
+ /** The x of a `null` placed inside every hole: one millisecond after the sample before it. */
21340
+ function gapBreaks(points, gapMs) {
21341
+ if (gapMs === void 0 || !(gapMs > 0)) return [];
21342
+ const out = [];
21343
+ for (let i = 1; i < points.length; i++) {
21344
+ const prev = points[i - 1];
21345
+ const cur = points[i];
21346
+ if (prev && cur && cur.t - prev.t > gapMs) out.push(prev.t + 1);
21347
+ }
21348
+ return out;
21349
+ }
21350
+ /**
21351
+ * The holes every series shares (the intersection), so the chart shades only
21352
+ * what NOBODY measured. Series without `gapMs` never contribute a hole and so
21353
+ * make the intersection empty: a held value is not unmeasured.
21354
+ */
21355
+ function sharedGapSpans(inputs) {
21356
+ if (inputs.length === 0) return [];
21357
+ let acc = null;
21358
+ for (const s of inputs) {
21359
+ const own = ownGaps(s);
21360
+ acc = acc === null ? own : intersectSpans(acc, own);
21361
+ if (acc.length === 0) return [];
21362
+ }
21363
+ return acc ?? [];
21364
+ }
21365
+ function ownGaps(s) {
21366
+ const gapMs = s.gapMs;
21367
+ if (gapMs === void 0 || !(gapMs > 0)) return [];
21368
+ const pts = [...s.points].sort((a, b) => a.t - b.t);
21369
+ const out = [];
21370
+ for (let i = 1; i < pts.length; i++) {
21371
+ const prev = pts[i - 1];
21372
+ const cur = pts[i];
21373
+ if (prev && cur && cur.t - prev.t > gapMs) out.push({
21374
+ from: prev.t,
21375
+ to: cur.t
21376
+ });
21377
+ }
21378
+ return out;
21379
+ }
21380
+ function intersectSpans(a, b) {
21381
+ const out = [];
21382
+ for (const x of a) for (const y of b) {
21383
+ const from = Math.max(x.from, y.from);
21384
+ const to = Math.min(x.to, y.to);
21385
+ if (to > from) out.push({
21386
+ from,
21387
+ to
21388
+ });
21389
+ }
21390
+ return out;
21391
+ }
21392
+ /**
21393
+ * Cumulative sums for a stacked area: `out[i] = ys[0] + … + ys[i]`. A stack
21394
+ * level is unknown (`null`) as soon as any layer under it is unknown, and an
21395
+ * alignment filler stays a filler.
21396
+ */
21397
+ function stackSeries(ys) {
21398
+ const out = [];
21399
+ ys.forEach((row, si) => {
21400
+ const below = si === 0 ? null : out[si - 1];
21401
+ out.push(row.map((v, i) => {
21402
+ if (v === void 0) return void 0;
21403
+ if (v === null) return null;
21404
+ if (below === null || below === void 0) return v;
21405
+ const b = below[i];
21406
+ if (b === null) return null;
21407
+ if (b === void 0) return v;
21408
+ return b + v;
21409
+ }));
21410
+ });
21411
+ return out;
21412
+ }
21413
+ /**
21414
+ * The value a reader sees at index `idx`: the nearest sample at or before it
21415
+ * that is not an alignment filler. A `null` there is answered as unknown.
21416
+ */
21417
+ function valueAtOrBefore(xs, ys, idx) {
21418
+ for (let i = Math.min(idx, xs.length - 1); i >= 0; i--) {
21419
+ const v = ys[i];
21420
+ if (v !== void 0) return v;
21421
+ }
21422
+ return null;
21423
+ }
21424
+ /**
21425
+ * Turn change points into segments clipped to [from, to]. `before` is the
21426
+ * state in force when the window opens (the last change before it); without
21427
+ * it the window starts unknown until the first change.
21428
+ */
21429
+ function segmentsFromChanges(changes, from, to, before = null) {
21430
+ const sorted = [...changes].filter((c) => c.t < to).sort((a, b) => a.t - b.t);
21431
+ const out = [];
21432
+ let cursor = from;
21433
+ let state = before;
21434
+ for (const c of sorted) {
21435
+ if (c.t <= from) {
21436
+ state = c.state;
21437
+ continue;
21438
+ }
21439
+ if (c.state === state) continue;
21440
+ out.push({
21441
+ from: cursor,
21442
+ to: c.t,
21443
+ state
21444
+ });
21445
+ cursor = c.t;
21446
+ state = c.state;
21447
+ }
21448
+ if (to > cursor) out.push({
21449
+ from: cursor,
21450
+ to,
21451
+ state
21452
+ });
21453
+ return out.filter((s) => s.to > s.from && s.state !== null);
21454
+ }
21455
+ /** Total milliseconds per state over the segments (unknown spans are not counted). */
21456
+ function timeInState(segments) {
21457
+ const out = /* @__PURE__ */ new Map();
21458
+ for (const s of segments) {
21459
+ if (s.state === null) continue;
21460
+ out.set(s.state, (out.get(s.state) ?? 0) + Math.max(0, s.to - s.from));
21461
+ }
21462
+ return out;
21463
+ }
21464
+ /** The state in force at `t`, or `null` when nothing was recorded there. */
21465
+ function stateAt(segments, t) {
21466
+ for (const s of segments) if (t >= s.from && t < s.to) return s.state;
21467
+ return null;
21468
+ }
21469
+ var MINUTE$1 = 6e4;
21470
+ var HOUR = 60 * MINUTE$1;
21471
+ var DAY = 24 * HOUR;
21472
+ /** `2h 14m`, `3d 4h`, `45s` — two units at most, for a legend. */
21473
+ function formatSpan(ms) {
21474
+ if (!(ms > 0)) return "0s";
21475
+ if (ms < MINUTE$1) return `${String(Math.round(ms / 1e3))}s`;
21476
+ if (ms < HOUR) return `${String(Math.floor(ms / MINUTE$1))}m`;
21477
+ if (ms < DAY) {
21478
+ const h = Math.floor(ms / HOUR);
21479
+ const m = Math.floor(ms % HOUR / MINUTE$1);
21480
+ return m > 0 ? `${String(h)}h ${String(m)}m` : `${String(h)}h`;
21481
+ }
21482
+ const d = Math.floor(ms / DAY);
21483
+ const h = Math.floor(ms % DAY / HOUR);
21484
+ return h > 0 ? `${String(d)}d ${String(h)}h` : `${String(d)}d`;
21485
+ }
21486
+ var TIME_HM = {
21487
+ hour: "2-digit",
21488
+ minute: "2-digit"
21489
+ };
21490
+ var TIME_HMS = {
21491
+ hour: "2-digit",
21492
+ minute: "2-digit",
21493
+ second: "2-digit"
21494
+ };
21495
+ var DATE_DM = {
21496
+ day: "2-digit",
21497
+ month: "short"
21498
+ };
21499
+ var DATE_MY = {
21500
+ month: "short",
21501
+ year: "2-digit"
21502
+ };
21503
+ /**
21504
+ * A wall-clock label at the precision the window's length justifies:
21505
+ * seconds under ten minutes, `HH:MM` within two days, the date past that,
21506
+ * month + year past ~4 months.
21507
+ */
21508
+ function formatTimeLabel(t, spanMs) {
21509
+ const d = new Date(t);
21510
+ if (spanMs < 10 * MINUTE$1) return d.toLocaleTimeString(void 0, TIME_HMS);
21511
+ if (spanMs <= 2 * DAY) return d.toLocaleTimeString(void 0, TIME_HM);
21512
+ if (spanMs <= 120 * DAY) return d.toLocaleDateString(void 0, DATE_DM);
21513
+ return d.toLocaleDateString(void 0, DATE_MY);
21514
+ }
21515
+ /** A cursor readout time: always the full instant at the window's precision. */
21516
+ function formatCursorTime(t, spanMs) {
21517
+ const d = new Date(t);
21518
+ if (spanMs <= 2 * DAY) return d.toLocaleTimeString(void 0, spanMs < 10 * MINUTE$1 ? TIME_HMS : TIME_HM);
21519
+ return `${d.toLocaleDateString(void 0, DATE_DM)} ${d.toLocaleTimeString(void 0, TIME_HM)}`;
21520
+ }
21521
+ //#endregion
21522
+ //#region src/composites/charts/chart-scale.ts
21523
+ var LINEAR_MAX_MANTISSAS = [
21524
+ 1,
21525
+ 2,
21526
+ 5,
21527
+ 10
21528
+ ];
21529
+ var BYTE_MAX_MANTISSAS = [
21530
+ 1,
21531
+ 2,
21532
+ 4,
21533
+ 8,
21534
+ 16,
21535
+ 32,
21536
+ 64,
21537
+ 128,
21538
+ 256,
21539
+ 512,
21540
+ 1024
21541
+ ];
21542
+ /** Steps a reader adds up without arithmetic; 1.25 / 1.66 are what an equal split of anything gives. */
21543
+ var READABLE_STEP_MANTISSAS = [
21544
+ 1,
21545
+ 1.5,
21546
+ 2,
21547
+ 2.5,
21548
+ 3,
21549
+ 4,
21550
+ 5
21551
+ ];
21552
+ /** Interval counts to try, in preference order: four is as many gridlines as a short plot keeps legible. */
21553
+ var AXIS_INTERVAL_CANDIDATES = [
21554
+ 4,
21555
+ 3,
21556
+ 5,
21557
+ 2
21558
+ ];
21559
+ var MANTISSA_EPSILON = 1e-9;
21560
+ function isReadableStep(step, scale) {
21561
+ if (!Number.isFinite(step) || step <= 0) return false;
21562
+ if (scale === "bytes") {
21563
+ if (!Number.isInteger(step)) return false;
21564
+ const mantissa = step / 1024 ** Math.floor(Math.log(step) / Math.log(1024));
21565
+ return READABLE_STEP_MANTISSAS.some((m) => Math.abs(mantissa - m) < MANTISSA_EPSILON);
21566
+ }
21567
+ const mantissa = step / 10 ** Math.floor(Math.log10(step));
21568
+ return READABLE_STEP_MANTISSAS.some((m) => Math.abs(mantissa - m) < MANTISSA_EPSILON);
21569
+ }
21570
+ /**
21571
+ * Nice upper bound of a value axis that starts at zero. `floor` is returned
21572
+ * VERBATIM when nothing exceeds it, so an idle series keeps the scale the
21573
+ * caller chose instead of a 0–0.3 axis on which noise looks like load.
21574
+ */
21575
+ function niceMax(values, floor, scale = "linear") {
21576
+ let max = 0;
21577
+ for (const v of values) if (v !== null && Number.isFinite(v) && v > max) max = v;
21578
+ if (max <= floor) return floor;
21579
+ if (scale === "bytes") {
21580
+ const magnitude = 1024 ** Math.floor(Math.log(max) / Math.log(1024));
21581
+ for (const mantissa of BYTE_MAX_MANTISSAS) {
21582
+ const candidate = mantissa * magnitude;
21583
+ if (candidate >= max) return candidate;
21584
+ }
21585
+ return 1024 * magnitude;
21586
+ }
21587
+ const magnitude = 10 ** Math.floor(Math.log10(max));
21588
+ for (const mantissa of LINEAR_MAX_MANTISSAS) {
21589
+ const candidate = mantissa * magnitude;
21590
+ if (candidate >= max) return candidate;
21591
+ }
21592
+ return 10 * magnitude;
21593
+ }
21594
+ /** Gridline values for an axis topping out at `max`, ascending, `max` last; zero is the baseline, not a tick. */
21595
+ function axisTicks(max, scale = "linear") {
21596
+ if (!Number.isFinite(max) || max <= 0) return [];
21597
+ const intervals = AXIS_INTERVAL_CANDIDATES.find((d) => isReadableStep(max / d, scale)) ?? AXIS_INTERVAL_CANDIDATES[AXIS_INTERVAL_CANDIDATES.length - 1] ?? 2;
21598
+ const step = max / intervals;
21599
+ const ticks = [];
21600
+ for (let i = 1; i <= intervals; i++) ticks.push(step * i);
21601
+ return ticks;
21602
+ }
21603
+ /**
21604
+ * Nice [min, max] for a series that need NOT start at zero (a temperature, a
21605
+ * dBFS level): the data range padded by a tenth and rounded outward to a
21606
+ * readable step. A flat series gets a ±1 window so it is not a degenerate axis.
21607
+ */
21608
+ function niceRange(values) {
21609
+ let lo = Number.POSITIVE_INFINITY;
21610
+ let hi = Number.NEGATIVE_INFINITY;
21611
+ for (const v of values) {
21612
+ if (v === null || !Number.isFinite(v)) continue;
21613
+ if (v < lo) lo = v;
21614
+ if (v > hi) hi = v;
21615
+ }
21616
+ if (!Number.isFinite(lo) || !Number.isFinite(hi)) return null;
21617
+ if (hi - lo < 1e-9) return [lo - 1, hi + 1];
21618
+ const pad = (hi - lo) * .1;
21619
+ const span = hi - lo + 2 * pad;
21620
+ const magnitude = 10 ** Math.floor(Math.log10(span / 4));
21621
+ const step = [
21622
+ 1,
21623
+ 2,
21624
+ 2.5,
21625
+ 5,
21626
+ 10
21627
+ ].map((m) => m * magnitude).find((st) => span / st <= 6) ?? magnitude * 10;
21628
+ return [Math.floor((lo - pad) / step) * step, Math.ceil((hi + pad) / step) * step];
21629
+ }
21630
+ /**
21631
+ * How many sampling intervals of silence make a HOLE rather than a late tick:
21632
+ * past any plausible delivery jitter, well inside the shortest outage worth seeing.
21633
+ */
21634
+ var GAP_INTERVAL_FACTOR = 2.5;
21635
+ /** The gap threshold for a series sampled every `intervalMs` — derived, never a literal. */
21636
+ function gapThresholdMs(intervalMs) {
21637
+ return intervalMs * GAP_INTERVAL_FACTOR;
21638
+ }
21639
+ //#endregion
21640
+ //#region src/composites/charts/chart-uplot.ts
21641
+ var AXIS_FONT_PX = 10;
21642
+ function axisFont() {
21643
+ const family = typeof document !== "undefined" && document.body ? getComputedStyle(document.body).fontFamily : "sans-serif";
21644
+ return `${String(AXIS_FONT_PX)}px ${family}`;
21645
+ }
21646
+ /** The x axis: wall-clock time under the plot, at the window's precision. */
21647
+ function timeAxis(spanMs, show = true) {
21648
+ return {
21649
+ show,
21650
+ scale: "x",
21651
+ side: 2,
21652
+ size: show ? 22 : 0,
21653
+ gap: 4,
21654
+ font: axisFont(),
21655
+ stroke: () => resolveChartColor("foreground-subtle"),
21656
+ grid: { show: false },
21657
+ ticks: {
21658
+ show,
21659
+ stroke: () => resolveChartColor("border"),
21660
+ width: 1,
21661
+ size: 3
21662
+ },
21663
+ space: 70,
21664
+ values: (_u, splits) => splits.map((t) => formatTimeLabel(t, spanMs()))
21665
+ };
21666
+ }
21667
+ /** A y scale's range from the values it carries, per the axis spec. */
21668
+ function valueRange(values, spec) {
21669
+ if (spec.fixed) return spec.fixed;
21670
+ const defined = values.filter((v) => typeof v === "number" && Number.isFinite(v));
21671
+ if ((spec.zeroBased ?? true) && defined.every((v) => v >= 0)) return [0, niceMax(defined, spec.floor ?? 0, spec.scale ?? "linear") || 1];
21672
+ return niceRange(defined) ?? [0, 1];
21673
+ }
21674
+ /** Steps a reader adds up without arithmetic, as mantissas over a power of ten. */
21675
+ var READABLE_STEPS = [
21676
+ 1,
21677
+ 2,
21678
+ 2.5,
21679
+ 5
21680
+ ];
21681
+ /** Tick positions for a value axis over `range`: readable steps, 3–6 intervals. */
21682
+ function valueSplits(range, spec) {
21683
+ const [lo, hi] = range;
21684
+ if (lo === 0 && hi > 0) return [...axisTicks(hi, spec.scale ?? "linear")];
21685
+ const span = hi - lo;
21686
+ if (!(span > 0)) return [lo];
21687
+ const magnitude = 10 ** Math.floor(Math.log10(span / 4));
21688
+ const step = [...READABLE_STEPS, 10].map((m) => m * magnitude).find((st) => span / st <= 6) ?? span / 4;
21689
+ const out = [];
21690
+ for (let v = Math.ceil(lo / step - 1e-9) * step; v <= hi + 1e-9; v += step) out.push(Math.round(v / step) * step);
21691
+ return out;
21692
+ }
21693
+ /** One value axis. `side` 3 = left, 1 = right. */
21694
+ function valueAxis(scaleKey, side, format, showGrid) {
21695
+ return {
21696
+ scale: scaleKey,
21697
+ side,
21698
+ font: axisFont(),
21699
+ size: (_u, values) => {
21700
+ const longest = (values ?? []).reduce((m, v) => Math.max(m, String(v).length), 0);
21701
+ return Math.max(28, Math.min(72, longest * 6 + 10));
21702
+ },
21703
+ gap: 4,
21704
+ stroke: () => resolveChartColor("foreground-subtle"),
21705
+ grid: {
21706
+ show: showGrid,
21707
+ stroke: () => withAlpha(resolveChartColor("foreground-subtle"), .18),
21708
+ width: 1
21709
+ },
21710
+ ticks: { show: false },
21711
+ values: (_u, splits) => splits.map((v) => format(v))
21712
+ };
21713
+ }
21714
+ /** The cursor: a vertical crosshair, synced across every chart with the same key. */
21715
+ function chartCursor(syncKey) {
21716
+ return {
21717
+ y: false,
21718
+ drag: {
21719
+ x: false,
21720
+ y: false,
21721
+ setScale: false
21722
+ },
21723
+ points: {
21724
+ size: 6,
21725
+ width: 1.5
21726
+ },
21727
+ focus: { prox: 16 },
21728
+ ...syncKey !== void 0 ? { sync: {
21729
+ key: syncKey,
21730
+ setSeries: false
21731
+ } } : {}
21732
+ };
21733
+ }
21734
+ /**
21735
+ * Shade every hole nobody measured, UNDER the series, so an outage reads as
21736
+ * missing ground and never as a quiet period.
21737
+ */
21738
+ function gapShadeHook(spans) {
21739
+ return (u) => {
21740
+ const list = spans();
21741
+ if (list.length === 0) return;
21742
+ const ctx = u.ctx;
21743
+ ctx.save();
21744
+ ctx.fillStyle = withAlpha(resolveChartColor("foreground-subtle"), .12);
21745
+ for (const s of list) {
21746
+ const x0 = u.valToPos(s.from, "x", true);
21747
+ const x1 = u.valToPos(s.to, "x", true);
21748
+ ctx.fillRect(x0, u.bbox.top, Math.max(1, x1 - x0), u.bbox.height);
21749
+ }
21750
+ ctx.restore();
21751
+ };
21752
+ }
21753
+ var BYTE_UNITS = new Set([
21754
+ "B",
21755
+ "bytes",
21756
+ "byte"
21757
+ ]);
21758
+ /** Is this unit a byte count (binary axis and formatter)? */
21759
+ function isByteUnit(unit) {
21760
+ return unit !== null && unit !== void 0 && BYTE_UNITS.has(unit);
21761
+ }
21762
+ /** Units that live far from zero: their axis is padded around the data instead. */
21763
+ var FLOATING_UNITS = new Set([
21764
+ "°C",
21765
+ "°F",
21766
+ "K",
21767
+ "dBFS",
21768
+ "dB",
21769
+ "dBm",
21770
+ "hPa",
21771
+ "mbar",
21772
+ "inHg"
21773
+ ]);
21774
+ /** The default axis spec for a unit. */
21775
+ function defaultAxisSpec(unit) {
21776
+ if (isByteUnit(unit)) return { scale: "bytes" };
21777
+ if (unit === "%") return { floor: 10 };
21778
+ if (unit !== null && unit !== void 0 && FLOATING_UNITS.has(unit)) return { zeroBased: false };
21779
+ return {};
21780
+ }
21781
+ /** Binary byte count: `512 B`, `3.4 MB`, `1.2 GB`. */
21782
+ function formatChartBytes(bytes) {
21783
+ if (!Number.isFinite(bytes)) return "—";
21784
+ if (Math.abs(bytes) < 1024) return `${String(Math.round(bytes))} B`;
21785
+ const units = [
21786
+ "KB",
21787
+ "MB",
21788
+ "GB",
21789
+ "TB",
21790
+ "PB"
21791
+ ];
21792
+ let value = bytes / 1024;
21793
+ let unit = "KB";
21794
+ for (const u of units) {
21795
+ unit = u;
21796
+ if (Math.abs(value) < 1024) break;
21797
+ value /= 1024;
21798
+ }
21799
+ return `${Math.abs(value) < 100 ? value.toFixed(1) : value.toFixed(0)} ${unit}`;
21800
+ }
21801
+ /** A number at a precision its magnitude justifies. */
21802
+ function formatChartNumber(v) {
21803
+ if (!Number.isFinite(v)) return "—";
21804
+ const abs = Math.abs(v);
21805
+ if (abs >= 1e3) return Math.round(v).toLocaleString();
21806
+ if (abs >= 100) return v.toFixed(0);
21807
+ if (abs >= 10) return trimZeros(v.toFixed(1));
21808
+ if (abs >= 1) return trimZeros(v.toFixed(2));
21809
+ if (abs === 0) return "0";
21810
+ return trimZeros(v.toFixed(3));
21811
+ }
21812
+ function trimZeros(s) {
21813
+ return s.includes(".") ? s.replace(/\.?0+$/, "") : s;
21814
+ }
21815
+ /** The default value formatter: bytes binary, `%` glued, any other unit after a space. */
21816
+ function formatChartValue(v, unit) {
21817
+ if (isByteUnit(unit)) return formatChartBytes(v);
21818
+ const n = formatChartNumber(v);
21819
+ if (unit === null || unit === void 0 || unit === "" || unit === "count") return n;
21820
+ if (unit === "%") return `${n}%`;
21821
+ return `${n} ${unit}`;
21822
+ }
21823
+ //#endregion
21824
+ //#region src/composites/charts/chart-window.tsx
21825
+ function ChartWindowToggle({ options, value, onChange, ariaLabel, className }) {
21826
+ if (options.length < 2) return null;
21827
+ return /* @__PURE__ */ jsx("div", {
21828
+ role: "radiogroup",
21829
+ "aria-label": ariaLabel,
21830
+ className: cn("inline-flex items-center gap-0.5 border border-border rounded-md p-0.5 bg-background", className),
21831
+ children: options.map(({ id, label }) => {
21832
+ const active = value === id;
21833
+ return /* @__PURE__ */ jsx("button", {
21834
+ type: "button",
21835
+ role: "radio",
21836
+ "aria-checked": active,
21837
+ onClick: () => onChange(id),
21838
+ className: cn("px-2 py-0.5 rounded text-[10px] font-medium transition-colors", active ? "bg-primary/10 text-primary" : "text-foreground-subtle hover:text-foreground"),
21839
+ children: label
21840
+ }, id);
21841
+ })
21842
+ });
21843
+ }
21844
+ //#endregion
21845
+ //#region src/composites/charts/bar-series-model.ts
21846
+ /** Cumulative tops per layer, bottom layer first: `tops[l][i] = values[0..l]` summed. */
21847
+ function stackBuckets(buckets, layers) {
21848
+ const tops = [];
21849
+ for (let l = 0; l < layers; l++) tops.push(buckets.map((b) => {
21850
+ let sum = null;
21851
+ for (let k = 0; k <= l; k++) {
21852
+ const v = b.values[k];
21853
+ if (v === null || v === void 0) continue;
21854
+ sum = (sum ?? 0) + v;
21855
+ }
21856
+ return sum;
21857
+ }));
21858
+ return tops;
21859
+ }
21860
+ /** Total per layer and overall over the window (unknown buckets add nothing). */
21861
+ function bucketTotals(buckets, layers) {
21862
+ const perLayer = Array.from({ length: layers }, () => 0);
21863
+ let known = false;
21864
+ for (const b of buckets) for (let l = 0; l < layers; l++) {
21865
+ const v = b.values[l];
21866
+ if (v === null || v === void 0) continue;
21867
+ known = true;
21868
+ perLayer[l] = (perLayer[l] ?? 0) + v;
21869
+ }
21870
+ return {
21871
+ perLayer,
21872
+ total: perLayer.reduce((a, b) => a + b, 0),
21873
+ known
21874
+ };
21875
+ }
21876
+ /** Index of the bucket that holds `t`, or `null`. */
21877
+ function bucketIndexAt(buckets, bucketMs, t) {
21878
+ for (let i = 0; i < buckets.length; i++) {
21879
+ const b = buckets[i];
21880
+ if (b && t >= b.t && t < b.t + bucketMs) return i;
21881
+ }
21882
+ return null;
21883
+ }
21884
+ //#endregion
21885
+ //#region src/composites/charts/uplot-host.tsx
21886
+ /**
21887
+ * The one place the chart family touches uPlot.
21888
+ *
21889
+ * - uPlot is imported LAZILY: its module reads `matchMedia` at load time, and
21890
+ * a document without a 2D canvas (a test DOM, a print preview) has nothing
21891
+ * for it to draw on. There the chart's DOM half — title, legend, readout and
21892
+ * the accessible summary — still renders; only the plot is absent.
21893
+ * - Width follows the container (`ResizeObserver`); the height is the caller's.
21894
+ * - A theme switch (`.dark` / `.light` on `<html>`) only REDRAWS: every colour
21895
+ * is a function that resolves its token at draw time (`chart-palette.ts`).
21896
+ * - `optionsKey` decides when the plot is rebuilt (series set, axes); new
21897
+ * `data` under the same key is a cheap `setData`.
21898
+ */
21899
+ var ctorPromise = null;
21900
+ /** A 2D canvas this document can draw on — false in a test DOM. */
21901
+ function canDrawCanvas() {
21902
+ if (typeof document === "undefined" || typeof window === "undefined") return false;
21903
+ if (typeof window.matchMedia !== "function") return false;
21904
+ try {
21905
+ return document.createElement("canvas").getContext("2d") !== null;
21906
+ } catch {
21907
+ return false;
21908
+ }
21909
+ }
21910
+ function loadUPlot() {
21911
+ if (ctorPromise === null) ctorPromise = canDrawCanvas() ? import("./uPlot.esm-C_8LiAPm.js").then((m) => m.default).catch(() => null) : Promise.resolve(null);
21912
+ return ctorPromise;
21913
+ }
21914
+ function UPlotHost({ optionsKey, buildOptions, data, height, onInstance, className, inline = false }) {
21915
+ const hostRef = useRef(null);
21916
+ const plotRef = useRef(null);
21917
+ const [ctor, setCtor] = useState(null);
21918
+ const latest = useRef({
21919
+ buildOptions,
21920
+ data,
21921
+ onInstance
21922
+ });
21923
+ latest.current = {
21924
+ buildOptions,
21925
+ data,
21926
+ onInstance
21927
+ };
21928
+ useEffect(() => {
21929
+ let alive = true;
21930
+ loadUPlot().then((c) => {
21931
+ if (alive && c !== null) setCtor(() => c);
21932
+ });
21933
+ return () => {
21934
+ alive = false;
21935
+ };
21936
+ }, []);
21937
+ useEffect(() => {
21938
+ const el = hostRef.current;
21939
+ if (ctor === null || el === null) return;
21940
+ const width = Math.max(1, Math.floor(el.clientWidth));
21941
+ const u = new ctor({
21942
+ ...latest.current.buildOptions(width, ctor),
21943
+ width,
21944
+ height
21945
+ }, latest.current.data, el);
21946
+ plotRef.current = u;
21947
+ latest.current.onInstance?.(u);
21948
+ return () => {
21949
+ latest.current.onInstance?.(null);
21950
+ u.destroy();
21951
+ plotRef.current = null;
21952
+ };
21953
+ }, [
21954
+ ctor,
21955
+ optionsKey,
21956
+ height
21957
+ ]);
21958
+ useEffect(() => {
21959
+ plotRef.current?.setData(data);
21960
+ }, [data]);
21961
+ useEffect(() => {
21962
+ const el = hostRef.current;
21963
+ if (el === null || typeof ResizeObserver === "undefined") return;
21964
+ const ro = new ResizeObserver((entries) => {
21965
+ const w = Math.floor(entries[0]?.contentRect.width ?? 0);
21966
+ const u = plotRef.current;
21967
+ if (u !== null && w > 0 && w !== u.width) u.setSize({
21968
+ width: w,
21969
+ height
21970
+ });
21971
+ });
21972
+ ro.observe(el);
21973
+ return () => ro.disconnect();
21974
+ }, [height]);
21975
+ useEffect(() => {
21976
+ if (typeof MutationObserver === "undefined") return;
21977
+ const mo = new MutationObserver(() => plotRef.current?.redraw(false, true));
21978
+ mo.observe(document.documentElement, {
21979
+ attributes: true,
21980
+ attributeFilter: ["class"]
21981
+ });
21982
+ return () => mo.disconnect();
21983
+ }, []);
21984
+ const setHost = (el) => {
21985
+ hostRef.current = el;
21986
+ };
21987
+ return inline ? /* @__PURE__ */ jsx("span", {
21988
+ ref: setHost,
21989
+ className: className ?? "block w-full min-w-0",
21990
+ "data-chart-plot": ""
21991
+ }) : /* @__PURE__ */ jsx("div", {
21992
+ ref: setHost,
21993
+ className: className ?? "w-full min-w-0",
21994
+ "data-chart-plot": ""
21995
+ });
21996
+ }
21997
+ //#endregion
21998
+ //#region src/composites/charts/BarSeries.tsx
21999
+ /**
22000
+ * BarSeries — bucketed counts or deltas on wall time (uPlot), optionally
22001
+ * stacked, with a window total (kWh per hour with the day's sum, detections
22002
+ * per 5 minutes by class, event density under a recording timeline).
22003
+ *
22004
+ * Bars sit on their bucket's real span, so a missing bucket is a missing bar
22005
+ * and never a zero bar. `compact` drops the frame and axes for a strip that
22006
+ * lives inside another control; there a bucket can be selected (`onSelect`)
22007
+ * and every bucket is also a real button for keyboard and screen readers.
22008
+ */
22009
+ function BarSeries({ title, layers, buckets, bucketMs, range, unit = null, formatValue, height, stacked, showTotal = false, syncKey, pending = false, emptyMessage, actions, footer, windowToggle, compact = false, minBarFraction = 0, selectedIndex = null, onSelect, bucketLabel, bucketTestId, className }) {
22010
+ const t = useUiText();
22011
+ const plotHeight = height ?? (compact ? 24 : 100);
22012
+ const isStacked = stacked ?? layers.length > 1;
22013
+ const fmt = useCallback((v) => (formatValue ?? formatChartValue)(v, unit), [formatValue, unit]);
22014
+ const tops = useMemo(() => isStacked ? stackBuckets(buckets, layers.length) : layers.map((_l, li) => buckets.map((b) => b.values[li] ?? null)), [
22015
+ buckets,
22016
+ layers,
22017
+ isStacked
22018
+ ]);
22019
+ const totals = useMemo(() => bucketTotals(buckets, layers.length), [buckets, layers.length]);
22020
+ const [cursorIdx, setCursorIdx] = useState(null);
22021
+ const live = useRef({
22022
+ buckets,
22023
+ tops,
22024
+ range,
22025
+ selectedIndex,
22026
+ onSelect,
22027
+ bucketLabel,
22028
+ fmt
22029
+ });
22030
+ live.current = {
22031
+ buckets,
22032
+ tops,
22033
+ range,
22034
+ selectedIndex,
22035
+ onSelect,
22036
+ bucketLabel,
22037
+ fmt
22038
+ };
22039
+ const optionsKey = `${layers.map((l) => `${l.id}:${l.colorIndex}`).join(",")}#${compact ? "C" : ""}#${isStacked ? "S" : ""}#${syncKey ?? ""}#${String(bucketMs)}`;
22040
+ const data = useMemo(() => {
22041
+ const top = tops.at(-1) ?? [];
22042
+ const peak = isStacked ? top : buckets.map((_b, i) => Math.max(...tops.map((row) => row[i] ?? 0)));
22043
+ return [buckets.map((b) => b.t), peak.map((v) => v ?? null)];
22044
+ }, [
22045
+ buckets,
22046
+ tops,
22047
+ isStacked,
22048
+ selectedIndex
22049
+ ]);
22050
+ const buildOptions = useCallback(() => {
22051
+ const spec = { floor: 0 };
22052
+ return {
22053
+ ms: 1,
22054
+ legend: { show: false },
22055
+ padding: compact ? [
22056
+ 0,
22057
+ 0,
22058
+ 0,
22059
+ 0
22060
+ ] : [
22061
+ 8,
22062
+ 30,
22063
+ 0,
22064
+ 0
22065
+ ],
22066
+ scales: {
22067
+ x: {
22068
+ time: true,
22069
+ range: () => [live.current.range.from, live.current.range.to]
22070
+ },
22071
+ y: { range: (self) => {
22072
+ const r = valueRange([...self.data[1] ?? []], spec);
22073
+ return [r[0], r[1]];
22074
+ } }
22075
+ },
22076
+ axes: compact ? [{
22077
+ show: false,
22078
+ scale: "x"
22079
+ }, {
22080
+ show: false,
22081
+ scale: "y"
22082
+ }] : [timeAxis(() => live.current.range.to - live.current.range.from), {
22083
+ ...valueAxis("y", 3, (v) => live.current.fmt(v), true),
22084
+ splits: (_s, _i, min, max) => valueSplits([min, max], spec)
22085
+ }],
22086
+ series: [{}, {
22087
+ scale: "y",
22088
+ stroke: () => "transparent",
22089
+ points: { show: false },
22090
+ paths: () => null
22091
+ }],
22092
+ cursor: {
22093
+ ...chartCursor(syncKey),
22094
+ points: { show: false }
22095
+ },
22096
+ hooks: {
22097
+ draw: [(u) => {
22098
+ const { buckets: bs, tops: ts, selectedIndex: sel } = live.current;
22099
+ const ctx = u.ctx;
22100
+ const baseY = u.valToPos(0, "y", true);
22101
+ const minPx = minBarFraction * u.bbox.height;
22102
+ const gap = Math.max(1, Math.round(devicePixelRatio));
22103
+ ctx.save();
22104
+ bs.forEach((b, i) => {
22105
+ const x0 = u.valToPos(b.t, "x", true);
22106
+ const x1 = u.valToPos(b.t + bucketMs, "x", true);
22107
+ const w = Math.max(1, x1 - x0 - gap);
22108
+ let prevY = baseY;
22109
+ layers.forEach((layer, li) => {
22110
+ const top = ts[li]?.[i];
22111
+ if (top === null || top === void 0) return;
22112
+ if (!(top > (isStacked && li > 0 ? ts[li - 1]?.[i] ?? 0 : 0))) return;
22113
+ let yTop = u.valToPos(top, "y", true);
22114
+ const yBottom = isStacked ? prevY : baseY;
22115
+ if (li === layers.length - 1 && baseY - yTop < minPx) yTop = baseY - minPx;
22116
+ ctx.fillStyle = resolveChartColor(chartSeriesStyle(layer.colorIndex).token);
22117
+ const h = Math.max(1, yBottom - yTop - (isStacked && li > 0 ? gap : 0));
22118
+ ctx.fillRect(x0, yTop, w, h);
22119
+ prevY = yTop;
22120
+ });
22121
+ if (sel === i) {
22122
+ ctx.strokeStyle = resolveChartColor("primary");
22123
+ ctx.lineWidth = gap * 1.5;
22124
+ ctx.strokeRect(x0, u.bbox.top + gap, w, u.bbox.height - 2 * gap);
22125
+ }
22126
+ });
22127
+ ctx.restore();
22128
+ }],
22129
+ setCursor: [(u) => {
22130
+ const left = u.cursor.left ?? -1;
22131
+ const idx = left < 0 ? null : bucketIndexAt(live.current.buckets, bucketMs, u.posToVal(left, "x"));
22132
+ setCursorIdx(idx);
22133
+ const b = idx === null ? void 0 : live.current.buckets[idx];
22134
+ const label = b === void 0 || idx === null ? "" : live.current.bucketLabel?.(b, idx) ?? "";
22135
+ u.over.title = label;
22136
+ }],
22137
+ ready: [(u) => {
22138
+ u.over.addEventListener("click", () => {
22139
+ const left = u.cursor.left ?? -1;
22140
+ if (left < 0) return;
22141
+ const idx = bucketIndexAt(live.current.buckets, bucketMs, u.posToVal(left, "x"));
22142
+ if (idx !== null) live.current.onSelect?.(idx);
22143
+ });
22144
+ }]
22145
+ }
22146
+ };
22147
+ }, [optionsKey, minBarFraction]);
22148
+ const span = range.to - range.from;
22149
+ const hovered = cursorIdx === null ? void 0 : buckets[cursorIdx];
22150
+ const legend = layers.map((l, li) => {
22151
+ const v = hovered ? hovered.values[li] : totals.known ? totals.perLayer[li] : void 0;
22152
+ return {
22153
+ id: l.id,
22154
+ label: l.label,
22155
+ token: chartSeriesStyle(l.colorIndex).token,
22156
+ value: v === null || v === void 0 ? hovered ? "—" : void 0 : fmt(v)
22157
+ };
22158
+ });
22159
+ const summary = t("uiLibrary.charts.barSummary", "{{title}}, {{from}} to {{to}}: total {{total}}", {
22160
+ title,
22161
+ from: formatTimeLabel(range.from, span),
22162
+ to: formatTimeLabel(range.to, span),
22163
+ total: totals.known ? fmt(totals.total) : t("uiLibrary.charts.unknown", "unknown")
22164
+ });
22165
+ const buttons = onSelect !== void 0 ? /* @__PURE__ */ jsx("div", {
22166
+ className: "sr-only",
22167
+ children: buckets.map((b, i) => {
22168
+ const sum = b.values.reduce((a, v) => a + (v ?? 0), 0);
22169
+ if (sum <= 0) return null;
22170
+ return /* @__PURE__ */ jsx("button", {
22171
+ type: "button",
22172
+ "data-testid": bucketTestId,
22173
+ "aria-pressed": selectedIndex === i,
22174
+ onClick: () => onSelect(i),
22175
+ children: bucketLabel?.(b, i) ?? `${formatTimeLabel(b.t, span)} ${fmt(sum)}`
22176
+ }, b.t);
22177
+ })
22178
+ }) : null;
22179
+ const plot = /* @__PURE__ */ jsx(UPlotHost, {
22180
+ optionsKey,
22181
+ buildOptions,
22182
+ data,
22183
+ height: plotHeight
22184
+ });
22185
+ if (compact) return /* @__PURE__ */ jsxs("div", {
22186
+ className: cn("relative min-w-0", className),
22187
+ children: [/* @__PURE__ */ jsx("div", {
22188
+ role: "img",
22189
+ "aria-label": summary,
22190
+ children: plot
22191
+ }), buttons]
22192
+ });
22193
+ const totalLine = showTotal && totals.known ? t("uiLibrary.charts.total", "Total {{value}}", { value: fmt(totals.total) }) : null;
22194
+ const empty = !totals.known;
22195
+ const sizeClass = plotHeight <= 64 ? "sm" : plotHeight <= 120 ? "md" : "lg";
22196
+ return /* @__PURE__ */ jsx(ChartFrame, {
22197
+ title,
22198
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [
22199
+ totalLine && /* @__PURE__ */ jsx("span", {
22200
+ className: "text-[11px] tabular-nums text-foreground",
22201
+ children: totalLine
22202
+ }),
22203
+ windowToggle && /* @__PURE__ */ jsx(ChartWindowToggle, { ...windowToggle }),
22204
+ actions
22205
+ ] }),
22206
+ readoutTime: hovered ? formatCursorTime(hovered.t, span) : null,
22207
+ legend: layers.length > 1 || hovered ? legend : [],
22208
+ summary,
22209
+ footer,
22210
+ placeholder: pending ? /* @__PURE__ */ jsx(ChartPlaceholder, {
22211
+ message: t("uiLibrary.charts.loading", "Loading…"),
22212
+ height: sizeClass
22213
+ }) : empty ? /* @__PURE__ */ jsx(ChartPlaceholder, {
22214
+ message: emptyMessage ?? t("uiLibrary.charts.empty", "Nothing was recorded in this window."),
22215
+ height: sizeClass
22216
+ }) : void 0,
22217
+ controls: buttons,
22218
+ className,
22219
+ children: plot
22220
+ });
22221
+ }
22222
+ //#endregion
21068
22223
  //#region src/composites/cap-settings/EventHeatmap.tsx
21069
- var KIND_BG = {
21070
- motion: "bg-emerald-500",
21071
- object: "bg-violet-500",
21072
- audio: "bg-orange-500"
22224
+ /** Stack order, bottom first; the categorical slots keep motion green, object violet, audio orange. */
22225
+ var KINDS = [
22226
+ "motion",
22227
+ "object",
22228
+ "audio"
22229
+ ];
22230
+ var KIND_SLOT = {
22231
+ motion: 6,
22232
+ object: 5,
22233
+ audio: 4
21073
22234
  };
22235
+ /** A non-empty bucket is never thinner than this fraction of the strip. */
22236
+ var MIN_BAR_FRACTION = .15;
22237
+ /**
22238
+ * Event-density strip under a recording timeline: one stacked bar per bucket
22239
+ * (motion / object / audio), height normalised to the busiest bucket, on the
22240
+ * timeline's own window. A click (or the bucket's button, for keyboard and
22241
+ * screen readers) selects the bucket — it drives the drill-down strip.
22242
+ */
21074
22243
  function EventHeatmap({ windowFrom, windowTo, buckets, selectedIndex, onSelectBucket }) {
21075
22244
  const t = useUiText();
21076
- const peak = buckets.reduce((m, b) => Math.max(m, b.total), 0);
21077
- const firstBucket = buckets[0];
21078
- const bucketWidthPct = firstBucket ? pctForMs(firstBucket.endMs, windowFrom, windowTo) - pctForMs(firstBucket.startMs, windowFrom, windowTo) : 0;
22245
+ const nonEmpty = useMemo(() => buckets.filter((b) => b.total > 0 && b.dominant !== null), [buckets]);
22246
+ const bars = useMemo(() => nonEmpty.map((b) => ({
22247
+ t: b.startMs,
22248
+ values: KINDS.map((k) => b.counts[k])
22249
+ })), [nonEmpty]);
22250
+ const layers = useMemo(() => KINDS.map((k) => ({
22251
+ id: k,
22252
+ label: t(`uiLibrary.eventHeatmap.kind.${k}`, k),
22253
+ colorIndex: KIND_SLOT[k]
22254
+ })), [t]);
22255
+ const first = buckets[0];
22256
+ const bucketMs = first ? Math.max(1, first.endMs - first.startMs) : 1;
22257
+ const selectedPos = selectedIndex === null ? null : nonEmpty.findIndex((b) => b.index === selectedIndex);
21079
22258
  return /* @__PURE__ */ jsx("div", {
21080
- className: "relative h-6 rounded bg-background/40",
21081
- children: buckets.map((b) => {
21082
- if (b.total === 0 || b.dominant === null) return null;
21083
- const heightPct = peak > 0 ? Math.max(15, b.total / peak * 100) : 0;
21084
- const left = pctForMs(b.startMs, windowFrom, windowTo);
21085
- const selected = b.index === selectedIndex;
21086
- return /* @__PURE__ */ jsx("button", {
21087
- type: "button",
21088
- "data-testid": "heatmap-bar",
21089
- title: t("uiLibrary.eventHeatmap.bucketTitle", "{{time}} · motion {{motion}} · object {{object}}", {
22259
+ className: "rounded bg-background/40",
22260
+ children: /* @__PURE__ */ jsx(BarSeries, {
22261
+ compact: true,
22262
+ title: t("uiLibrary.eventHeatmap.title", "Event density"),
22263
+ layers,
22264
+ buckets: bars,
22265
+ bucketMs,
22266
+ range: {
22267
+ from: windowFrom,
22268
+ to: windowTo
22269
+ },
22270
+ stacked: true,
22271
+ minBarFraction: MIN_BAR_FRACTION,
22272
+ selectedIndex: selectedPos === null || selectedPos < 0 ? null : selectedPos,
22273
+ onSelect: (pos) => {
22274
+ const b = nonEmpty[pos];
22275
+ if (b) onSelectBucket(b.index);
22276
+ },
22277
+ bucketTestId: "heatmap-bar",
22278
+ bucketLabel: (_bar, pos) => {
22279
+ const b = nonEmpty[pos];
22280
+ if (!b) return "";
22281
+ return t("uiLibrary.eventHeatmap.bucketTitle", "{{time}} · motion {{motion}} · object {{object}}", {
21090
22282
  time: new Date(b.startMs).toLocaleTimeString(void 0, {
21091
22283
  hour: "2-digit",
21092
22284
  minute: "2-digit"
21093
22285
  }),
21094
22286
  motion: b.counts.motion,
21095
22287
  object: b.counts.object
21096
- }),
21097
- onClick: () => onSelectBucket(b.index),
21098
- className: `absolute bottom-0 rounded-t-sm transition-opacity ${KIND_BG[b.dominant]} ${selected ? "opacity-100 ring-1 ring-amber-400" : "opacity-80 hover:opacity-100"}`,
21099
- style: {
21100
- left: `${left}%`,
21101
- width: `${Math.max(.6, bucketWidthPct)}%`,
21102
- height: `${heightPct}%`
21103
- }
21104
- }, b.index);
22288
+ });
22289
+ }
21105
22290
  })
21106
22291
  });
21107
22292
  }
@@ -21294,7 +22479,7 @@ function ClipBandBar({ day, onDayChange, view, dayBounds, onViewChange, buckets,
21294
22479
  dayBounds,
21295
22480
  onViewChange
21296
22481
  ]);
21297
- const ticks = axisTicks(view.fromMs, view.toMs, 5);
22482
+ const ticks = axisTicks$1(view.fromMs, view.toMs, 5);
21298
22483
  return /* @__PURE__ */ jsxs("div", {
21299
22484
  className: "space-y-1",
21300
22485
  children: [/* @__PURE__ */ jsx(TimelineControls, {
@@ -26091,7 +27276,7 @@ function RecordingTimeline({ deviceId }) {
26091
27276
  const coverageLoading = availabilityQuery.isLoading;
26092
27277
  const densityLoading = eventDensityQuery.isLoading;
26093
27278
  const drillLoading = (objectDrill.isLoading || motionDrill.isLoading || audioDrill.isLoading) && drillEnabled;
26094
- const ticks = useMemo(() => axisTicks(view.fromMs, view.toMs, 5), [view.fromMs, view.toMs]);
27279
+ const ticks = useMemo(() => axisTicks$1(view.fromMs, view.toMs, 5), [view.fromMs, view.toMs]);
26095
27280
  const displayPlayheadMs = framePush ? scrubState.scrubbing ? scrubState.scrubEpoch : scrubState.playheadEpoch : playheadMs;
26096
27281
  const showRateToggle = framePush && (recordedMode === "playing" || recordedMode === "paused");
26097
27282
  return /* @__PURE__ */ jsxs("div", {
@@ -26361,8 +27546,8 @@ function RecordingPanel({ deviceId }) {
26361
27546
  * `type:'widget'` ConfigField. `WidgetSlot` resolves these BEFORE the
26362
27547
  * Module-Federation `WidgetRegistry`. ids are namespaced `host/<name>`. */
26363
27548
  var HOST_WIDGETS = {
26364
- "host/motion-zones-grid": lazy(() => import("./MotionZonesSettings-C3Tj12yc.js").then((m) => ({ default: m.MotionZonesSettings }))),
26365
- "host/privacy-mask-grid": lazy(() => import("./PrivacyMaskSettings-C9wfHkiB.js").then((m) => ({ default: m.PrivacyMaskSettings }))),
27549
+ "host/motion-zones-grid": lazy(() => import("./MotionZonesSettings-DR1g9ja3.js").then((m) => ({ default: m.MotionZonesSettings }))),
27550
+ "host/privacy-mask-grid": lazy(() => import("./PrivacyMaskSettings-CtetT6Jl.js").then((m) => ({ default: m.PrivacyMaskSettings }))),
26366
27551
  "host/ptz-panel": PtzPanel,
26367
27552
  "host/navigation-panel": NavigationPanel,
26368
27553
  "host/consumables-panel": ConsumablesPanel,
@@ -29890,6 +31075,25 @@ function AddonGlobalSettingsForm({ trpc, addonId, nodeId, title, disabled, onAft
29890
31075
  }
29891
31076
  //#endregion
29892
31077
  //#region src/composites/accelerator-label.ts
31078
+ var CORAL_UNIT = /^(usb|pci)(?::(\d+))?$/;
31079
+ /**
31080
+ * One Coral per node keeps the plain name; a second unit, or one on PCIe, says
31081
+ * which: `edgetpu:usb` "Coral Edge TPU", `edgetpu:usb:1` "Coral Edge TPU (USB 2)",
31082
+ * `edgetpu:pci` "Coral Edge TPU (PCIe)", `edgetpu:pci:1` "Coral Edge TPU (PCIe 2)".
31083
+ */
31084
+ function coralNames(dev) {
31085
+ const match = CORAL_UNIT.exec(dev);
31086
+ if (match === null || match[1] === "usb" && match[2] === void 0) return {
31087
+ label: "Coral Edge TPU",
31088
+ short: "Coral"
31089
+ };
31090
+ const bus = match[1] === "pci" ? "PCIe" : "USB";
31091
+ const unit = match[2] === void 0 ? bus : `${bus} ${Number(match[2]) + 1}`;
31092
+ return {
31093
+ label: `Coral Edge TPU (${unit})`,
31094
+ short: `Coral ${unit}`
31095
+ };
31096
+ }
29893
31097
  function names(device) {
29894
31098
  const backend = device.backend.toLowerCase();
29895
31099
  const dev = device.device.toLowerCase();
@@ -29905,10 +31109,7 @@ function names(device) {
29905
31109
  label: "Intel iGPU",
29906
31110
  short: "iGPU"
29907
31111
  };
29908
- if (backend === "edgetpu") return {
29909
- label: "Coral Edge TPU",
29910
- short: "Coral"
29911
- };
31112
+ if (backend === "edgetpu") return coralNames(dev);
29912
31113
  if (backend === "coreml") return {
29913
31114
  label: "Apple Neural Engine",
29914
31115
  short: "ANE"
@@ -39808,6 +41009,202 @@ function useLiveBuffer(max) {
39808
41009
  reset
39809
41010
  ]);
39810
41011
  }
41012
+ //#endregion
41013
+ //#region src/hooks/use-debounced-string.ts
41014
+ /**
41015
+ * useDebouncedString — return `value` after `delay` ms have passed
41016
+ * without further changes. Resets the timer on every change.
41017
+ *
41018
+ * Useful for search inputs that should NOT push to URL state on every
41019
+ * keystroke. The DOM input stays controlled by an external `value`
41020
+ * (caller's `useState`); this hook produces a debounced derivation.
41021
+ */
41022
+ function useDebouncedString(value, delayMs) {
41023
+ const [debounced, setDebounced] = useState(value);
41024
+ useEffect(() => {
41025
+ const t = setTimeout(() => setDebounced(value), delayMs);
41026
+ return () => clearTimeout(t);
41027
+ }, [value, delayMs]);
41028
+ return debounced;
41029
+ }
41030
+ //#endregion
41031
+ //#region src/composites/log-history-paging.ts
41032
+ /**
41033
+ * Accumulate slices until `limit` entries, `done`, or an abort. An abort keeps
41034
+ * what was already collected and the cursor reached, so "Load older" resumes
41035
+ * where the scan stopped instead of starting again.
41036
+ */
41037
+ async function collectPage(options) {
41038
+ const { fetchSlice, limit, signal, onProgress } = options;
41039
+ const entries = [];
41040
+ let cursor = options.before;
41041
+ let scannedBytes = 0;
41042
+ let source;
41043
+ for (;;) {
41044
+ if (signal.aborted) return {
41045
+ entries,
41046
+ nextBefore: cursor ?? null,
41047
+ done: false,
41048
+ cancelled: true,
41049
+ scannedBytes,
41050
+ source
41051
+ };
41052
+ let page;
41053
+ try {
41054
+ page = await fetchSlice(cursor, Math.max(1, limit - entries.length), signal);
41055
+ } catch (error) {
41056
+ if (signal.aborted) return {
41057
+ entries,
41058
+ nextBefore: cursor ?? null,
41059
+ done: false,
41060
+ cancelled: true,
41061
+ scannedBytes,
41062
+ source
41063
+ };
41064
+ throw error;
41065
+ }
41066
+ entries.push(...page.entries);
41067
+ scannedBytes += page.scannedBytes;
41068
+ source = page.source;
41069
+ onProgress?.(scannedBytes);
41070
+ const exhausted = page.done || page.nextBefore === null;
41071
+ if (page.cancelled === true) return {
41072
+ entries,
41073
+ nextBefore: page.nextBefore,
41074
+ done: exhausted,
41075
+ cancelled: true,
41076
+ scannedBytes,
41077
+ source
41078
+ };
41079
+ if (exhausted || entries.length >= limit) return {
41080
+ entries,
41081
+ nextBefore: page.nextBefore,
41082
+ done: exhausted,
41083
+ cancelled: false,
41084
+ scannedBytes,
41085
+ source
41086
+ };
41087
+ cursor = page.nextBefore ?? void 0;
41088
+ }
41089
+ }
41090
+ /** Stable identity of an entry: the server id, else the legacy composite key. */
41091
+ function logEntryKey(entry) {
41092
+ if (entry.id !== void 0 && entry.id !== "") return `id:${entry.id}`;
41093
+ return `${entry.timestamp}|${entry.level}|${entry.scope ?? ""}|${entry.message}`;
41094
+ }
41095
+ /**
41096
+ * History (newest first, as paged) merged with live entries (arrival order),
41097
+ * deduped by key — a live entry wins (richer tags/meta) — and returned
41098
+ * chronologically, newest LAST. Ties keep insertion order.
41099
+ */
41100
+ function mergeLogEntries(history, live) {
41101
+ const byKey = /* @__PURE__ */ new Map();
41102
+ for (const entry of history) byKey.set(logEntryKey(entry), entry);
41103
+ for (const entry of live) byKey.set(logEntryKey(entry), entry);
41104
+ return [...byKey.values()].toSorted((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
41105
+ }
41106
+ //#endregion
41107
+ //#region src/hooks/use-log-history.ts
41108
+ /**
41109
+ * useLogHistory — pages the server-served log history (D898).
41110
+ *
41111
+ * The first page and every older page go through `collectPage`, which keeps
41112
+ * calling `logs.query` with `before: nextBefore` while the server answers a
41113
+ * short, not-done slice. Changing the filters aborts whatever is in flight and
41114
+ * restarts from the first page; `cancel()` aborts the scan but keeps what was
41115
+ * found and the cursor reached, so "Load older" resumes from there.
41116
+ */
41117
+ var INITIAL = {
41118
+ entries: [],
41119
+ nextBefore: null,
41120
+ done: false,
41121
+ status: "loading",
41122
+ scannedBytes: 0,
41123
+ source: void 0,
41124
+ error: null
41125
+ };
41126
+ function useLogHistory(query, limit) {
41127
+ const utils = trpc.useUtils();
41128
+ const [state, setState] = useState(INITIAL);
41129
+ const stateRef = useRef(state);
41130
+ stateRef.current = state;
41131
+ const queryRef = useRef(query);
41132
+ queryRef.current = query;
41133
+ const abortRef = useRef(null);
41134
+ const queryKey = JSON.stringify(query);
41135
+ const run = useCallback(async (before, reset) => {
41136
+ abortRef.current?.abort();
41137
+ const controller = new AbortController();
41138
+ abortRef.current = controller;
41139
+ setState((prev) => ({
41140
+ ...reset ? INITIAL : prev,
41141
+ status: "loading",
41142
+ scannedBytes: 0,
41143
+ error: null
41144
+ }));
41145
+ const fetchSlice = (cursor, remaining, signal) => utils.client.logs.query.query({
41146
+ ...queryRef.current,
41147
+ limit: remaining,
41148
+ ...cursor !== void 0 ? { before: cursor } : {}
41149
+ }, { signal });
41150
+ try {
41151
+ const out = await collectPage({
41152
+ fetchSlice,
41153
+ limit,
41154
+ ...before !== void 0 ? { before } : {},
41155
+ signal: controller.signal,
41156
+ onProgress: (scannedBytes) => {
41157
+ if (abortRef.current === controller) setState((prev) => ({
41158
+ ...prev,
41159
+ scannedBytes
41160
+ }));
41161
+ }
41162
+ });
41163
+ if (abortRef.current !== controller) return;
41164
+ setState((prev) => ({
41165
+ entries: reset ? out.entries : [...prev.entries, ...out.entries],
41166
+ nextBefore: out.nextBefore,
41167
+ done: out.done,
41168
+ status: "idle",
41169
+ scannedBytes: out.scannedBytes,
41170
+ source: out.source ?? prev.source,
41171
+ error: null
41172
+ }));
41173
+ } catch (error) {
41174
+ if (abortRef.current !== controller) return;
41175
+ setState((prev) => ({
41176
+ ...prev,
41177
+ status: "idle",
41178
+ error: error instanceof Error ? error.message : String(error)
41179
+ }));
41180
+ }
41181
+ }, [utils, limit]);
41182
+ useEffect(() => {
41183
+ run(void 0, true);
41184
+ return () => {
41185
+ abortRef.current?.abort();
41186
+ abortRef.current = null;
41187
+ };
41188
+ }, [run, queryKey]);
41189
+ const loadOlder = useCallback(() => {
41190
+ const current = stateRef.current;
41191
+ if (current.status === "loading" || current.done) return;
41192
+ run(current.nextBefore ?? void 0, false);
41193
+ }, [run]);
41194
+ const cancel = useCallback(() => {
41195
+ abortRef.current?.abort();
41196
+ }, []);
41197
+ return {
41198
+ entries: state.entries,
41199
+ status: state.status,
41200
+ done: state.done,
41201
+ scannedBytes: state.scannedBytes,
41202
+ source: state.source,
41203
+ error: state.error,
41204
+ loadOlder,
41205
+ cancel
41206
+ };
41207
+ }
39811
41208
  /**
39812
41209
  * True when the viewport is scrolled to (or within `threshold` px of) the
39813
41210
  * bottom of its content.
@@ -39864,12 +41261,6 @@ var LEVEL_STYLES = {
39864
41261
  color: "text-gray-500"
39865
41262
  }
39866
41263
  };
39867
- var LEVEL_SEVERITY = {
39868
- debug: 0,
39869
- info: 1,
39870
- warn: 2,
39871
- error: 3
39872
- };
39873
41264
  var LEVEL_OPTIONS = [
39874
41265
  "debug",
39875
41266
  "info",
@@ -39878,15 +41269,18 @@ var LEVEL_OPTIONS = [
39878
41269
  ];
39879
41270
  /** Max live entries kept in the ring buffer. */
39880
41271
  var MAX_LIVE_ENTRIES$1 = 500;
39881
- function passesLevel(logLevel, filterLevel) {
39882
- if (!filterLevel) return true;
39883
- return (LEVEL_SEVERITY[logLevel] ?? 0) >= (LEVEL_SEVERITY[filterLevel] ?? 0);
39884
- }
39885
- function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: propsDeviceId, containerDeviceId: propsContainerDeviceId, integrationId: propsIntegrationId, requestId: propsRequestId, level: initialLevel, maxHeight = "max-h-96", showScope = false, showDeviceChip = false, showFilters = true, limit = 100, liveBuffer: externalBuffer, onClose, className }) {
41272
+ /** Text filter debounce before it goes to the server. */
41273
+ var TEXT_DEBOUNCE_MS = 300;
41274
+ /** Distance from the top (px) that asks for the next older page. */
41275
+ var TOP_LOAD_THRESHOLD_PX = 24;
41276
+ var BYTES_PER_MB = 1024 * 1024;
41277
+ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: propsDeviceId, containerDeviceId: propsContainerDeviceId, integrationId: propsIntegrationId, requestId: propsRequestId, level: initialLevel, maxHeight = "max-h-96", showScope = false, showDeviceChip = false, showFilters = true, showSourceSelect, limit = 100, liveBuffer: externalBuffer, onClose, className }) {
39886
41278
  const t = useUiText();
41279
+ const sourceSelectOn = showSourceSelect ?? showFilters;
39887
41280
  const [localAddonId, setLocalAddonId] = useState("");
39888
41281
  const [localDeviceId, setLocalDeviceId] = useState("");
39889
41282
  const [levelFilter, setLevelFilter] = useState(initialLevel ?? "info");
41283
+ const [sourceChoice, setSourceChoice] = useState(void 0);
39890
41284
  const agentId = propsAgentId;
39891
41285
  const addonId = propsAddonId ?? (localAddonId || void 0);
39892
41286
  const deviceId = propsDeviceId ?? (localDeviceId ? Number(localDeviceId) : void 0);
@@ -39931,48 +41325,63 @@ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: pro
39931
41325
  setExpandAll(false);
39932
41326
  }
39933
41327
  }, [scopeKey, buffer]);
39934
- const { data: initialLogs, isLoading } = trpc.logs.query.useQuery({
39935
- ...tags ? { tags } : {},
39936
- limit
39937
- }, { staleTime: 3e4 });
39938
- trpc.logs.subscribe.useSubscription(tags ? { tags } : {}, { onData: (entry) => {
41328
+ const debouncedText = useDebouncedString(searchText, TEXT_DEBOUNCE_MS).trim();
41329
+ const sourcesQuery = trpc.logs.sources.useQuery(void 0, { staleTime: 6e4 });
41330
+ const readableSources = useMemo(() => (sourcesQuery.data ?? []).filter((source) => source.readable), [sourcesQuery.data]);
41331
+ const history = useLogHistory(useMemo(() => ({
41332
+ ...sourceChoice ? { source: sourceChoice } : {},
41333
+ level: levelFilter,
41334
+ ...debouncedText ? { text: debouncedText } : {},
41335
+ ...tags ? { tags } : {}
41336
+ }), [
41337
+ sourceChoice,
41338
+ levelFilter,
41339
+ debouncedText,
41340
+ tags
41341
+ ]), limit);
41342
+ trpc.logs.subscribe.useSubscription({
41343
+ level: levelFilter,
41344
+ ...debouncedText ? { text: debouncedText } : {},
41345
+ ...tags ? { tags } : {}
41346
+ }, { onData: (entry) => {
39939
41347
  buffer.append(entry);
39940
41348
  } });
41349
+ const filterKey = `${sourceChoice ?? ""}|${levelFilter}|${debouncedText}`;
41350
+ const prevFilterRef = useRef(filterKey);
41351
+ useEffect(() => {
41352
+ if (prevFilterRef.current === filterKey) return;
41353
+ prevFilterRef.current = filterKey;
41354
+ buffer.reset();
41355
+ setFollow(true);
41356
+ }, [filterKey, buffer]);
39941
41357
  const allLogs = useMemo(() => {
39942
- const initial = (initialLogs ?? []).map((log) => ({
39943
- timestamp: log.timestamp,
39944
- level: log.level,
39945
- message: log.message,
39946
- scope: log.scope,
39947
- tags: log.tags,
39948
- meta: log.meta
39949
- }));
39950
- const byKey = /* @__PURE__ */ new Map();
39951
- for (const log of initial) byKey.set(`${log.timestamp}|${log.level}|${log.scope ?? ""}|${log.message}`, log);
39952
- for (const log of liveLogs) byKey.set(`${log.timestamp}|${log.level}|${log.scope ?? ""}|${log.message}`, log);
39953
- const merged = [...byKey.values()].toSorted((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
39954
- const levelFiltered = (clearedAt > 0 ? merged.filter((log) => new Date(log.timestamp).getTime() > clearedAt) : merged).filter((log) => passesLevel(log.level, levelFilter));
39955
- const needle = searchText.trim().toLowerCase();
39956
- if (!needle) return levelFiltered;
39957
- return levelFiltered.filter((log) => {
39958
- if (log.message.toLowerCase().includes(needle)) return true;
39959
- if (log.scope && log.scope.toLowerCase().includes(needle)) return true;
39960
- if (log.tags) {
39961
- for (const v of Object.values(log.tags)) if (v !== void 0 && String(v).toLowerCase().includes(needle)) return true;
39962
- }
39963
- if (log.meta) for (const v of Object.values(log.meta)) {
39964
- if (v === void 0 || v === null) continue;
39965
- if (formatMetaValue(v).toLowerCase().includes(needle)) return true;
39966
- }
39967
- return false;
39968
- });
41358
+ const merged = mergeLogEntries(history.entries, liveLogs);
41359
+ return clearedAt > 0 ? merged.filter((log) => new Date(log.timestamp).getTime() > clearedAt) : merged;
39969
41360
  }, [
39970
- initialLogs,
41361
+ history.entries,
39971
41362
  liveLogs,
39972
- levelFilter,
39973
- clearedAt,
39974
- searchText
41363
+ clearedAt
39975
41364
  ]);
41365
+ const prependAnchorRef = useRef(null);
41366
+ const requestOlder = useCallback(() => {
41367
+ if (history.status === "loading" || history.done) return;
41368
+ const el = scrollRef.current;
41369
+ if (el) prependAnchorRef.current = {
41370
+ height: el.scrollHeight,
41371
+ top: el.scrollTop
41372
+ };
41373
+ history.loadOlder();
41374
+ }, [history]);
41375
+ useLayoutEffect(() => {
41376
+ const anchor = prependAnchorRef.current;
41377
+ const el = scrollRef.current;
41378
+ if (!anchor || !el || history.status !== "idle") return;
41379
+ prependAnchorRef.current = null;
41380
+ const next = anchor.top + (el.scrollHeight - anchor.height);
41381
+ if (Math.abs(next - el.scrollTop) < 1) return;
41382
+ programmaticScrollRef.current = true;
41383
+ el.scrollTop = next;
41384
+ }, [history.status, history.entries.length]);
39976
41385
  const scrollToBottom = useCallback((behavior) => {
39977
41386
  const el = scrollRef.current;
39978
41387
  if (!el) return;
@@ -39983,15 +41392,16 @@ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: pro
39983
41392
  });
39984
41393
  }, []);
39985
41394
  const [hasNewWhilePaused, setHasNewWhilePaused] = useState(false);
39986
- const prevLenRef = useRef(allLogs.length);
41395
+ const lastKey = allLogs.length > 0 ? logEntryKey(allLogs[allLogs.length - 1]) : "";
41396
+ const prevLastKeyRef = useRef(lastKey);
39987
41397
  useLayoutEffect(() => {
39988
- const grew = allLogs.length > prevLenRef.current;
39989
- prevLenRef.current = allLogs.length;
39990
- if (!grew) return;
41398
+ const newestChanged = lastKey !== "" && lastKey !== prevLastKeyRef.current;
41399
+ prevLastKeyRef.current = lastKey;
41400
+ if (!newestChanged) return;
39991
41401
  if (follow) scrollToBottom();
39992
41402
  else setHasNewWhilePaused(true);
39993
41403
  }, [
39994
- allLogs.length,
41404
+ lastKey,
39995
41405
  follow,
39996
41406
  scrollToBottom
39997
41407
  ]);
@@ -40011,8 +41421,9 @@ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: pro
40011
41421
  if (!el) return;
40012
41422
  const pinned = isPinnedToBottom(el, 40);
40013
41423
  setFollow((prev) => prev === pinned ? prev : pinned);
40014
- }, []);
40015
- const expandableKeys = useMemo(() => allLogs.filter((log) => log.tags && Object.keys(log.tags).length > 0 || log.meta && Object.keys(log.meta).length > 0).map((log) => `${log.timestamp}|${log.level}|${log.scope ?? ""}|${log.message}`), [allLogs]);
41424
+ if (el.scrollTop <= TOP_LOAD_THRESHOLD_PX) requestOlder();
41425
+ }, [requestOlder]);
41426
+ const expandableKeys = useMemo(() => allLogs.filter((log) => log.tags && Object.keys(log.tags).length > 0 || log.meta && Object.keys(log.meta).length > 0).map(logEntryKey), [allLogs]);
40016
41427
  const toggleExpandAll = useCallback(() => {
40017
41428
  setExpandAll((prev) => {
40018
41429
  if (prev) setExpandedRows(/* @__PURE__ */ new Set());
@@ -40059,7 +41470,7 @@ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: pro
40059
41470
  setTimeout(() => setCopiedRowKey((prev) => prev === key ? null : prev), 1200);
40060
41471
  });
40061
41472
  }, []);
40062
- const rowKey = useCallback((log) => `${log.timestamp}|${log.level}|${log.scope ?? ""}|${log.message}`, []);
41473
+ const rowKey = logEntryKey;
40063
41474
  return /* @__PURE__ */ jsxs("div", {
40064
41475
  className: cn("h-full min-h-0 flex flex-col", className),
40065
41476
  onClick: (e) => e.stopPropagation(),
@@ -40094,6 +41505,23 @@ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: pro
40094
41505
  children: [l, "+"]
40095
41506
  }, l))
40096
41507
  }),
41508
+ sourceSelectOn && readableSources.length > 1 && /* @__PURE__ */ jsx("div", {
41509
+ className: "w-36",
41510
+ children: /* @__PURE__ */ jsx(Select, {
41511
+ "aria-label": t("uiLibrary.logStream.source", "Log source"),
41512
+ className: "h-5 text-[10px]",
41513
+ value: sourceChoice ?? readableSources.find((x) => x.isDefault)?.addonId ?? "",
41514
+ onChange: (e) => setSourceChoice(e.target.value),
41515
+ options: readableSources.map((x) => ({
41516
+ value: x.addonId,
41517
+ label: x.addonId
41518
+ }))
41519
+ })
41520
+ }),
41521
+ sourceSelectOn && readableSources.length <= 1 && history.source && /* @__PURE__ */ jsx("span", {
41522
+ className: "text-[9px] text-foreground-subtle",
41523
+ children: t("uiLibrary.logStream.sourceName", "Source: {{name}}", { name: history.source })
41524
+ }),
40097
41525
  /* @__PURE__ */ jsx(ToolbarButton, {
40098
41526
  icon: ArrowDownToLine,
40099
41527
  label: follow ? t("uiLibrary.logStream.following", "Following") : t("uiLibrary.logStream.paused", "Paused"),
@@ -40192,11 +41620,16 @@ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: pro
40192
41620
  onScroll: handleScroll,
40193
41621
  className: cn("flex-1 min-h-0 overflow-auto font-mono text-[10px]", maxHeight),
40194
41622
  children: [
40195
- isLoading && /* @__PURE__ */ jsxs("div", {
40196
- className: "flex items-center justify-center gap-2 py-6 text-foreground-subtle",
40197
- children: [/* @__PURE__ */ jsx(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }), t("uiLibrary.logStream.loading", "Loading logs...")]
41623
+ /* @__PURE__ */ jsx(HistoryEdge, {
41624
+ status: history.status,
41625
+ done: history.done,
41626
+ scannedBytes: history.scannedBytes,
41627
+ hasRows: allLogs.length > 0,
41628
+ error: history.error,
41629
+ onLoadOlder: requestOlder,
41630
+ onCancel: history.cancel
40198
41631
  }),
40199
- !isLoading && allLogs.length === 0 && /* @__PURE__ */ jsx("div", {
41632
+ history.status === "idle" && history.done && allLogs.length === 0 && /* @__PURE__ */ jsx("div", {
40200
41633
  className: "py-4 text-center text-foreground-subtle",
40201
41634
  children: t("uiLibrary.logStream.noLogs", "No logs found")
40202
41635
  }),
@@ -40320,6 +41753,49 @@ function DeviceChip({ deviceId, deviceName }) {
40320
41753
  children: label
40321
41754
  });
40322
41755
  }
41756
+ /**
41757
+ * The top of the list: the state of the older-history scan. A scan that is
41758
+ * still looking (short page, source not done) says how much it has read and
41759
+ * can be cancelled; the end of the source is stated quietly.
41760
+ */
41761
+ function HistoryEdge({ status, done, scannedBytes, hasRows, error, onLoadOlder, onCancel }) {
41762
+ const t = useUiText();
41763
+ if (status === "loading") return /* @__PURE__ */ jsxs("div", {
41764
+ className: "flex items-center justify-center gap-2 py-2 text-foreground-subtle",
41765
+ children: [
41766
+ /* @__PURE__ */ jsx(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }),
41767
+ scannedBytes > 0 ? t("uiLibrary.logStream.searching", "Searching older logs… ({{mb}} MB scanned)", { mb: (scannedBytes / BYTES_PER_MB).toFixed(1) }) : t("uiLibrary.logStream.loading", "Loading logs..."),
41768
+ /* @__PURE__ */ jsx(Button, {
41769
+ variant: "outline",
41770
+ size: "sm",
41771
+ onClick: onCancel,
41772
+ children: t("uiLibrary.logStream.cancel", "Cancel")
41773
+ })
41774
+ ]
41775
+ });
41776
+ if (error) return /* @__PURE__ */ jsxs("div", {
41777
+ className: "flex items-center justify-center gap-2 py-2 text-danger",
41778
+ children: [error, /* @__PURE__ */ jsx(Button, {
41779
+ variant: "outline",
41780
+ size: "sm",
41781
+ onClick: onLoadOlder,
41782
+ children: t("uiLibrary.logStream.retry", "Retry")
41783
+ })]
41784
+ });
41785
+ if (done) return hasRows ? /* @__PURE__ */ jsx("div", {
41786
+ className: "py-2 text-center text-foreground-subtle",
41787
+ children: t("uiLibrary.logStream.beginning", "Beginning of logs")
41788
+ }) : null;
41789
+ return /* @__PURE__ */ jsx("div", {
41790
+ className: "flex justify-center py-1.5",
41791
+ children: /* @__PURE__ */ jsx(Button, {
41792
+ variant: "outline",
41793
+ size: "sm",
41794
+ onClick: onLoadOlder,
41795
+ children: t("uiLibrary.logStream.loadOlder", "Load older")
41796
+ })
41797
+ });
41798
+ }
40323
41799
  function ScopeBadge$2({ label, value }) {
40324
41800
  return /* @__PURE__ */ jsxs("span", {
40325
41801
  className: "text-[9px] px-1.5 py-0.5 rounded-full bg-primary/10 text-primary font-medium",
@@ -51123,7 +52599,7 @@ function AlarmSegments({ state, availableModes, busy, onDisarm, onArm }) {
51123
52599
  }, mode))]
51124
52600
  });
51125
52601
  }
51126
- function messageOf(err) {
52602
+ function messageOf$1(err) {
51127
52603
  return err instanceof Error ? err.message : String(err);
51128
52604
  }
51129
52605
  /**
@@ -51165,7 +52641,7 @@ function useAlarmControl(trpc, deviceId, optimistic) {
51165
52641
  cap.disarm({}).catch((err) => {
51166
52642
  if (latestCallRef.current !== call) return;
51167
52643
  rollback();
51168
- setError(messageOf(err));
52644
+ setError(messageOf$1(err));
51169
52645
  });
51170
52646
  };
51171
52647
  const handleArm = (mode) => {
@@ -51175,7 +52651,7 @@ function useAlarmControl(trpc, deviceId, optimistic) {
51175
52651
  cap.arm({ mode }).catch((err) => {
51176
52652
  if (latestCallRef.current !== call) return;
51177
52653
  rollback();
51178
- setError(messageOf(err));
52654
+ setError(messageOf$1(err));
51179
52655
  });
51180
52656
  };
51181
52657
  return {
@@ -52363,24 +53839,6 @@ function DeviceBatchActionsBar(props) {
52363
53839
  })] });
52364
53840
  }
52365
53841
  //#endregion
52366
- //#region src/hooks/use-debounced-string.ts
52367
- /**
52368
- * useDebouncedString — return `value` after `delay` ms have passed
52369
- * without further changes. Resets the timer on every change.
52370
- *
52371
- * Useful for search inputs that should NOT push to URL state on every
52372
- * keystroke. The DOM input stays controlled by an external `value`
52373
- * (caller's `useState`); this hook produces a debounced derivation.
52374
- */
52375
- function useDebouncedString(value, delayMs) {
52376
- const [debounced, setDebounced] = useState(value);
52377
- useEffect(() => {
52378
- const t = setTimeout(() => setDebounced(value), delayMs);
52379
- return () => clearTimeout(t);
52380
- }, [value, delayMs]);
52381
- return debounced;
52382
- }
52383
- //#endregion
52384
53842
  //#region src/hooks/use-persisted-page-size.ts
52385
53843
  /**
52386
53844
  * usePersistedPageSize — a "rows per page" choice remembered per viewer.
@@ -63786,6 +65244,1849 @@ function RetrainReviewBadge({ kind, approximate = false, className, ...props })
63786
65244
  });
63787
65245
  }
63788
65246
  //#endregion
65247
+ //#region src/composites/charts/time-series-model.ts
65248
+ /**
65249
+ * The pure half of `TimeSeriesChart`: which series go on which value axis,
65250
+ * the aligned data uPlot draws, and what the readout says at an index.
65251
+ */
65252
+ /** The unit key a series lives on (`''` for no / unknown unit). */
65253
+ function unitKey(unit) {
65254
+ return unit ?? "";
65255
+ }
65256
+ /** Units in order of first appearance — one value axis each. */
65257
+ function unitAxes(series) {
65258
+ const out = [];
65259
+ for (const s of series) {
65260
+ const k = unitKey(s.unit);
65261
+ if (!out.includes(k)) out.push(k);
65262
+ }
65263
+ return out;
65264
+ }
65265
+ /** Align, stack (non-compare series only) and find the shared holes. */
65266
+ function buildTimeSeriesModel(series, stacked) {
65267
+ const aligned = alignSeries(series.map((s) => ({
65268
+ points: s.points,
65269
+ gapMs: s.gapMs
65270
+ })));
65271
+ let drawn = aligned.ys;
65272
+ if (stacked) {
65273
+ const primaryIdx = series.flatMap((s, i) => s.compare ? [] : [i]);
65274
+ const stackedRows = stackSeries(primaryIdx.map((i) => aligned.ys[i] ?? []));
65275
+ drawn = aligned.ys.map((row, i) => {
65276
+ const at = primaryIdx.indexOf(i);
65277
+ return at >= 0 ? stackedRows[at] ?? row : row;
65278
+ });
65279
+ }
65280
+ const empty = !aligned.ys.some((row) => row.some((v) => typeof v === "number"));
65281
+ return {
65282
+ xs: aligned.xs,
65283
+ raw: aligned.ys,
65284
+ drawn,
65285
+ mins: aligned.mins,
65286
+ maxs: aligned.maxs,
65287
+ units: unitAxes(series),
65288
+ gaps: sharedGapSpans(series.filter((s) => !s.compare).map((s) => ({
65289
+ points: s.points,
65290
+ gapMs: s.gapMs
65291
+ }))),
65292
+ empty
65293
+ };
65294
+ }
65295
+ /** The value per series a reader sees at `idx` (`null` = cursor off → the latest). */
65296
+ function readoutAt(model, idx) {
65297
+ const at = idx ?? model.xs.length - 1;
65298
+ return model.raw.map((row) => at < 0 ? null : valueAtOrBefore(model.xs, row, at));
65299
+ }
65300
+ /** The last known value of each series (for the accessible summary). */
65301
+ function latestValues(model) {
65302
+ return readoutAt(model, null);
65303
+ }
65304
+ //#endregion
65305
+ //#region src/composites/charts/TimeSeriesChart.tsx
65306
+ /**
65307
+ * TimeSeriesChart — gauges and counters over wall time (uPlot).
65308
+ *
65309
+ * Lines, an optional min/max band, stacked areas, null gaps (a hole breaks
65310
+ * the line AND is shaded), one value axis per unit (left, right, left …),
65311
+ * a synced cursor across every chart sharing `syncKey`, an optional window
65312
+ * toggle. Identity is never colour alone: one series is named by the title,
65313
+ * two or more always carry a legend with direct values.
65314
+ *
65315
+ * A device page draws ONE unit per chart; overlaying units (several axes) is
65316
+ * for the cross-device History page, where the reader chose the series.
65317
+ */
65318
+ var DEFAULT_HEIGHT = 120;
65319
+ function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacked = false, area = false, syncKey, formatValue, axisFor, emptyMessage, pending = false, actions, footer, windowToggle, className }) {
65320
+ const t = useUiText();
65321
+ const model = useMemo(() => buildTimeSeriesModel(series, stacked), [series, stacked]);
65322
+ const [cursorIdx, setCursorIdx] = useState(null);
65323
+ const modelRef = useRef(model);
65324
+ modelRef.current = model;
65325
+ const rangeRef = useRef(range);
65326
+ rangeRef.current = range;
65327
+ const fmt = useCallback((v, unit) => (formatValue ?? formatChartValue)(v, unit), [formatValue]);
65328
+ const spanMs = () => {
65329
+ const r = rangeRef.current;
65330
+ if (r) return r.to - r.from;
65331
+ const xs = modelRef.current.xs;
65332
+ return xs.length > 1 ? (xs.at(-1) ?? 0) - (xs[0] ?? 0) : 0;
65333
+ };
65334
+ const unitOf = (s) => s.unit ?? null;
65335
+ const specFor = useCallback((unit) => (axisFor ?? defaultAxisSpec)(unit === "" ? null : unit), [axisFor]);
65336
+ const optionsKey = [
65337
+ series.map((s) => `${s.id}|${unitKey(s.unit)}|${s.colorIndex}|${s.interpolation ?? ""}|${s.band ? 1 : 0}|${s.compare ? 1 : 0}`).join(","),
65338
+ stacked ? "S" : "",
65339
+ area ? "A" : "",
65340
+ syncKey ?? "",
65341
+ range ? "R" : ""
65342
+ ].join("#");
65343
+ const data = useMemo(() => {
65344
+ const ys = [];
65345
+ series.forEach((s, i) => {
65346
+ ys.push([...model.drawn[i] ?? []]);
65347
+ if (s.band) {
65348
+ ys.push([...model.mins[i] ?? []]);
65349
+ ys.push([...model.maxs[i] ?? []]);
65350
+ }
65351
+ });
65352
+ return [[...model.xs], ...ys];
65353
+ }, [model, series]);
65354
+ const buildOptions = useCallback((_width, lib) => {
65355
+ const units = modelRef.current.units;
65356
+ const scales = { x: {
65357
+ time: true,
65358
+ ...rangeRef.current ? { range: () => [rangeRef.current?.from ?? 0, rangeRef.current?.to ?? 1] } : {}
65359
+ } };
65360
+ const axes = [timeAxis(spanMs)];
65361
+ units.forEach((u, ui) => {
65362
+ const key = `y:${u}`;
65363
+ const spec = specFor(u);
65364
+ scales[key] = { range: (self) => {
65365
+ const vals = [];
65366
+ self.series.forEach((s, si) => {
65367
+ if (si === 0 || s.scale !== key) return;
65368
+ for (const v of self.data[si] ?? []) vals.push(v);
65369
+ });
65370
+ const r = valueRange(vals, spec);
65371
+ return [r[0], r[1]];
65372
+ } };
65373
+ const unit = u === "" ? null : u;
65374
+ const ax = valueAxis(key, ui % 2 === 0 ? 3 : 1, (v) => fmt(v, unit), ui === 0);
65375
+ axes.push({
65376
+ ...ax,
65377
+ splits: (_self, _axisIdx, min, max) => valueSplits([min, max], spec)
65378
+ });
65379
+ });
65380
+ const uSeries = [{}];
65381
+ const bands = [];
65382
+ const defs = series;
65383
+ const primaryCount = defs.filter((d) => !d.compare).length;
65384
+ let seriesIdx = 1;
65385
+ let prevStackIdx = null;
65386
+ defs.forEach((d) => {
65387
+ const style = chartSeriesStyle(d.colorIndex);
65388
+ const color = () => resolveChartColor(style.token);
65389
+ const paths = d.interpolation === "step" ? lib.paths.stepped?.({ align: 1 }) : lib.paths.linear?.();
65390
+ const isStacked = stacked && !d.compare;
65391
+ const fillSingle = !stacked && area && primaryCount === 1 && !d.compare;
65392
+ const line = {
65393
+ label: d.label,
65394
+ scale: `y:${unitKey(d.unit)}`,
65395
+ stroke: d.compare ? () => withAlpha(color(), .6) : color,
65396
+ width: d.compare ? 1.25 : 2,
65397
+ dash: d.compare ? [4, 4] : style.dash.length > 0 ? [...style.dash] : void 0,
65398
+ spanGaps: false,
65399
+ points: { show: false },
65400
+ ...paths ? { paths } : {},
65401
+ ...isStacked && prevStackIdx === null ? {
65402
+ fill: () => withAlpha(color(), .85),
65403
+ fillTo: 0
65404
+ } : {},
65405
+ ...fillSingle ? { fill: () => withAlpha(color(), .15) } : {}
65406
+ };
65407
+ uSeries.push(line);
65408
+ const thisIdx = seriesIdx;
65409
+ if (isStacked && prevStackIdx !== null) bands.push({
65410
+ series: [thisIdx, prevStackIdx],
65411
+ fill: () => withAlpha(color(), .85)
65412
+ });
65413
+ if (isStacked) prevStackIdx = thisIdx;
65414
+ seriesIdx += 1;
65415
+ if (d.band) {
65416
+ const hidden = {
65417
+ scale: `y:${unitKey(d.unit)}`,
65418
+ stroke: () => "transparent",
65419
+ width: 0,
65420
+ points: { show: false },
65421
+ spanGaps: false,
65422
+ ...paths ? { paths } : {}
65423
+ };
65424
+ uSeries.push({
65425
+ ...hidden,
65426
+ label: `${d.label} min`
65427
+ }, {
65428
+ ...hidden,
65429
+ label: `${d.label} max`
65430
+ });
65431
+ bands.push({
65432
+ series: [seriesIdx + 1, seriesIdx],
65433
+ fill: () => withAlpha(color(), .15)
65434
+ });
65435
+ seriesIdx += 2;
65436
+ }
65437
+ });
65438
+ return {
65439
+ ms: 1,
65440
+ legend: { show: false },
65441
+ padding: [
65442
+ 8,
65443
+ 30,
65444
+ 0,
65445
+ 0
65446
+ ],
65447
+ scales,
65448
+ axes,
65449
+ series: uSeries,
65450
+ bands,
65451
+ cursor: chartCursor(syncKey),
65452
+ hooks: {
65453
+ drawClear: [gapShadeHook(() => modelRef.current.gaps)],
65454
+ setCursor: [(u) => setCursorIdx(u.cursor.idx ?? null)]
65455
+ }
65456
+ };
65457
+ }, [
65458
+ optionsKey,
65459
+ fmt,
65460
+ specFor
65461
+ ]);
65462
+ const shown = readoutAt(model, cursorIdx);
65463
+ const legend = series.map((s, i) => {
65464
+ const style = chartSeriesStyle(s.colorIndex);
65465
+ const v = shown[i];
65466
+ return {
65467
+ id: s.id,
65468
+ label: s.label,
65469
+ token: style.token,
65470
+ dashed: s.compare === true || style.dash.length > 0,
65471
+ muted: s.compare === true,
65472
+ value: v === null || v === void 0 ? model.empty ? void 0 : "—" : fmt(v, unitOf(s))
65473
+ };
65474
+ });
65475
+ const readoutTime = cursorIdx !== null && model.xs[cursorIdx] !== void 0 ? formatCursorTime(model.xs[cursorIdx] ?? 0, spanMs()) : null;
65476
+ const summary = useMemo(() => {
65477
+ const latest = latestValues(model);
65478
+ const from = range?.from ?? model.xs[0];
65479
+ const to = range?.to ?? model.xs.at(-1);
65480
+ const parts = series.map((s, i) => {
65481
+ const v = latest[i];
65482
+ return `${s.label} ${v === null || v === void 0 ? t("uiLibrary.charts.unknown", "unknown") : fmt(v, unitOf(s))}`;
65483
+ }).join("; ");
65484
+ if (from === void 0 || to === void 0 || !(to > from)) return t("uiLibrary.charts.seriesSummaryNoRange", "{{title}}: {{parts}}", {
65485
+ title,
65486
+ parts
65487
+ });
65488
+ return t("uiLibrary.charts.seriesSummary", "{{title}}, {{from}} to {{to}}: {{parts}}", {
65489
+ title,
65490
+ from: formatTimeLabel(from, to - from),
65491
+ to: formatTimeLabel(to, to - from),
65492
+ parts
65493
+ });
65494
+ }, [
65495
+ model,
65496
+ series,
65497
+ range,
65498
+ title,
65499
+ t,
65500
+ fmt
65501
+ ]);
65502
+ const toolbar = /* @__PURE__ */ jsxs(Fragment$1, { children: [windowToggle && /* @__PURE__ */ jsx(ChartWindowToggle, { ...windowToggle }), actions] });
65503
+ const sizeClass = height <= 64 ? "sm" : height <= 120 ? "md" : "lg";
65504
+ return /* @__PURE__ */ jsx(ChartFrame, {
65505
+ title,
65506
+ actions: toolbar,
65507
+ readoutTime,
65508
+ legend,
65509
+ summary,
65510
+ footer,
65511
+ placeholder: pending ? /* @__PURE__ */ jsx(ChartPlaceholder, {
65512
+ message: t("uiLibrary.charts.loading", "Loading…"),
65513
+ height: sizeClass
65514
+ }) : model.empty ? /* @__PURE__ */ jsx(ChartPlaceholder, {
65515
+ message: emptyMessage ?? t("uiLibrary.charts.empty", "Nothing was recorded in this window."),
65516
+ height: sizeClass
65517
+ }) : void 0,
65518
+ className,
65519
+ children: /* @__PURE__ */ jsx(UPlotHost, {
65520
+ optionsKey,
65521
+ buildOptions,
65522
+ data,
65523
+ height
65524
+ })
65525
+ });
65526
+ }
65527
+ //#endregion
65528
+ //#region src/composites/charts/StateTimeline.tsx
65529
+ /**
65530
+ * StateTimeline — boolean and enum states as bands over wall time (uPlot).
65531
+ *
65532
+ * One row per state series. A state is a filled band in its categorical slot
65533
+ * (the ordinal of the value in its `states` list, so a colour follows the
65534
+ * state, never its rank); a boolean's `false` is a recessive fill, and a span
65535
+ * nothing was recorded for is left EMPTY — unknown never looks like a state.
65536
+ * The legend gives the time spent in each state over the window ("open
65537
+ * 2h 14m"), and the cursor (synced through `syncKey`) names the state under it.
65538
+ */
65539
+ /** Pixel height of one row band — matches the `h-5` label cells. */
65540
+ var ROW_PX = 20;
65541
+ var AXIS_PX = 22;
65542
+ /** The colour role of a state on a row: a categorical slot, or the recessive "off". */
65543
+ function stateStyle(row, state) {
65544
+ const base = row.colorIndex ?? 0;
65545
+ if (row.kind === "boolean") return state === row.states[1] ? { token: chartSeriesStyle(base).token } : { token: "border" };
65546
+ return { token: chartSeriesStyle(base + Math.max(0, row.states.indexOf(state))).token };
65547
+ }
65548
+ function StateTimeline({ title, rows, range, syncKey, pending = false, emptyMessage, actions, className }) {
65549
+ const t = useUiText();
65550
+ const [cursorT, setCursorT] = useState(null);
65551
+ const rowsRef = useRef(rows);
65552
+ rowsRef.current = rows;
65553
+ const rangeRef = useRef(range);
65554
+ rangeRef.current = range;
65555
+ const span = range.to - range.from;
65556
+ const empty = rows.every((r) => r.segments.length === 0);
65557
+ const labelOf = (row, s) => row.stateLabel?.(s) ?? s;
65558
+ const optionsKey = `${rows.map((r) => `${r.id}:${r.states.join("|")}`).join(",")}#${syncKey ?? ""}`;
65559
+ const data = useMemo(() => [[range.from, range.to], [null, null]], [range.from, range.to]);
65560
+ const buildOptions = useCallback(() => ({
65561
+ ms: 1,
65562
+ legend: { show: false },
65563
+ padding: [
65564
+ 0,
65565
+ 30,
65566
+ 0,
65567
+ 40
65568
+ ],
65569
+ scales: {
65570
+ x: {
65571
+ time: true,
65572
+ range: () => [rangeRef.current.from, rangeRef.current.to]
65573
+ },
65574
+ y: { range: () => [0, 1] }
65575
+ },
65576
+ axes: [timeAxis(() => rangeRef.current.to - rangeRef.current.from), {
65577
+ show: false,
65578
+ scale: "y"
65579
+ }],
65580
+ series: [{}, {
65581
+ scale: "y",
65582
+ stroke: () => "transparent",
65583
+ points: { show: false }
65584
+ }],
65585
+ cursor: {
65586
+ ...chartCursor(syncKey),
65587
+ points: { show: false }
65588
+ },
65589
+ hooks: {
65590
+ draw: [(u) => {
65591
+ const ctx = u.ctx;
65592
+ const list = rowsRef.current;
65593
+ const pr = u.bbox.height / Math.max(1, list.length);
65594
+ ctx.save();
65595
+ list.forEach((row, ri) => {
65596
+ const y = u.bbox.top + ri * pr;
65597
+ ctx.fillStyle = withAlpha(resolveChartColor("foreground-subtle"), .06);
65598
+ ctx.fillRect(u.bbox.left, y + 2, u.bbox.width, pr - 4);
65599
+ for (const seg of row.segments) {
65600
+ if (seg.state === null) continue;
65601
+ const x0 = u.valToPos(Math.max(seg.from, rangeRef.current.from), "x", true);
65602
+ const x1 = u.valToPos(Math.min(seg.to, rangeRef.current.to), "x", true);
65603
+ if (!(x1 > x0)) continue;
65604
+ ctx.fillStyle = resolveChartColor(stateStyle(row, seg.state).token);
65605
+ ctx.fillRect(x0, y + 2, Math.max(1, x1 - x0), pr - 4);
65606
+ }
65607
+ });
65608
+ ctx.restore();
65609
+ }],
65610
+ setCursor: [(u) => {
65611
+ const left = u.cursor.left ?? -1;
65612
+ setCursorT(left < 0 ? null : u.posToVal(left, "x"));
65613
+ }]
65614
+ }
65615
+ }), [optionsKey]);
65616
+ const durations = useMemo(() => rows.map((r) => r.totals ?? timeInState(r.segments)), [rows]);
65617
+ const summary = rows.map((row, i) => {
65618
+ const parts = row.states.map((s) => {
65619
+ const ms = durations[i]?.get(s) ?? 0;
65620
+ return ms > 0 ? `${labelOf(row, s)} ${formatSpan(ms)}` : null;
65621
+ }).filter((p) => p !== null).join(", ");
65622
+ return `${row.label}: ${parts.length > 0 ? parts : t("uiLibrary.charts.noState", "nothing recorded")}`;
65623
+ }).join("; ");
65624
+ const fullSummary = t("uiLibrary.charts.stateSummary", "{{title}}, {{from}} to {{to}}: {{parts}}", {
65625
+ title,
65626
+ from: formatTimeLabel(range.from, span),
65627
+ to: formatTimeLabel(range.to, span),
65628
+ parts: summary
65629
+ });
65630
+ const multi = rows.length > 1;
65631
+ const placeholder = pending ? t("uiLibrary.charts.loading", "Loading…") : empty ? emptyMessage ?? t("uiLibrary.charts.empty", "Nothing was recorded in this window.") : null;
65632
+ return /* @__PURE__ */ jsxs("figure", {
65633
+ className: cn("min-w-0 m-0", className),
65634
+ "data-chart-frame": "",
65635
+ children: [
65636
+ /* @__PURE__ */ jsxs("div", {
65637
+ className: "flex items-baseline justify-between gap-2 mb-1 min-w-0",
65638
+ children: [/* @__PURE__ */ jsx("figcaption", {
65639
+ className: "text-[10px] uppercase tracking-wider text-foreground-subtle truncate min-w-0",
65640
+ children: title
65641
+ }), /* @__PURE__ */ jsxs("div", {
65642
+ className: "flex items-center gap-2 shrink-0",
65643
+ children: [cursorT !== null && /* @__PURE__ */ jsx("span", {
65644
+ className: "text-[10px] tabular-nums text-foreground-subtle",
65645
+ children: formatCursorTime(cursorT, span)
65646
+ }), actions]
65647
+ })]
65648
+ }),
65649
+ placeholder !== null ? /* @__PURE__ */ jsx(ChartPlaceholder, {
65650
+ message: placeholder,
65651
+ height: "sm"
65652
+ }) : /* @__PURE__ */ jsxs("div", {
65653
+ role: "img",
65654
+ "aria-label": fullSummary,
65655
+ className: "flex min-w-0",
65656
+ children: [multi && /* @__PURE__ */ jsx("div", {
65657
+ className: "w-20 sm:w-28 shrink-0 pr-2",
65658
+ "aria-hidden": "true",
65659
+ children: rows.map((r) => /* @__PURE__ */ jsx("div", {
65660
+ className: "h-5 flex items-center text-[10px] text-foreground-muted truncate",
65661
+ children: r.label
65662
+ }, r.id))
65663
+ }), /* @__PURE__ */ jsx("div", {
65664
+ className: "flex-1 min-w-0",
65665
+ children: /* @__PURE__ */ jsx(UPlotHost, {
65666
+ optionsKey,
65667
+ buildOptions,
65668
+ data,
65669
+ height: rows.length * ROW_PX + AXIS_PX
65670
+ })
65671
+ })]
65672
+ }),
65673
+ /* @__PURE__ */ jsx("p", {
65674
+ className: "sr-only",
65675
+ children: fullSummary
65676
+ }),
65677
+ placeholder === null && /* @__PURE__ */ jsx("ul", {
65678
+ className: "mt-1 space-y-0.5 list-none p-0 m-0",
65679
+ children: rows.map((row, i) => {
65680
+ const now = cursorT === null ? null : stateAt(row.segments, cursorT);
65681
+ return /* @__PURE__ */ jsxs("li", {
65682
+ className: "flex flex-wrap items-center gap-x-3 gap-y-0.5 min-w-0",
65683
+ children: [multi && /* @__PURE__ */ jsx("span", {
65684
+ className: "text-[10px] text-foreground-muted",
65685
+ children: row.label
65686
+ }), row.states.map((s) => {
65687
+ const ms = durations[i]?.get(s) ?? 0;
65688
+ if (ms === 0) return null;
65689
+ return /* @__PURE__ */ jsxs("span", {
65690
+ className: cn("inline-flex items-center gap-1 text-[10px]", now === s ? "text-foreground" : "text-foreground-muted"),
65691
+ children: [
65692
+ /* @__PURE__ */ jsx(ChartSwatch, { token: stateStyle(row, s).token }),
65693
+ /* @__PURE__ */ jsx("span", { children: labelOf(row, s) }),
65694
+ /* @__PURE__ */ jsx("span", {
65695
+ className: "tabular-nums text-foreground",
65696
+ children: formatSpan(ms)
65697
+ })
65698
+ ]
65699
+ }, s);
65700
+ })]
65701
+ }, row.id);
65702
+ })
65703
+ })
65704
+ ]
65705
+ });
65706
+ }
65707
+ //#endregion
65708
+ //#region src/composites/charts/Sparkline.tsx
65709
+ /**
65710
+ * Sparkline — one series, no axes, no cursor: a trend beside a number in a
65711
+ * row (a training job's mAP per epoch). The x is whatever the caller orders
65712
+ * by (an epoch, a time); the value next to it names what the line is.
65713
+ */
65714
+ function Sparkline({ points, ariaLabel, token = "chart-series-a", height = 32, widthClassName = "w-40", zeroBased = true, className }) {
65715
+ const data = useMemo(() => [points.map((p) => p.x), points.map((p) => p.v)], [points]);
65716
+ const buildOptions = useCallback((_w, lib) => ({
65717
+ legend: { show: false },
65718
+ cursor: { show: false },
65719
+ padding: [
65720
+ 2,
65721
+ 1,
65722
+ 2,
65723
+ 1
65724
+ ],
65725
+ scales: {
65726
+ x: { time: false },
65727
+ y: { range: (_u, min, max) => {
65728
+ const lo = zeroBased ? Math.min(0, min) : min;
65729
+ return [lo, max > lo ? max : lo + 1];
65730
+ } }
65731
+ },
65732
+ axes: [{ show: false }, { show: false }],
65733
+ series: [{}, {
65734
+ stroke: () => resolveChartColor(token),
65735
+ width: 1.5,
65736
+ points: { show: false },
65737
+ spanGaps: false,
65738
+ ...lib.paths.linear ? { paths: lib.paths.linear() } : {}
65739
+ }]
65740
+ }), [token, zeroBased]);
65741
+ return /* @__PURE__ */ jsx("span", {
65742
+ role: "img",
65743
+ "aria-label": ariaLabel,
65744
+ className: cn("inline-block align-middle", widthClassName, className),
65745
+ children: /* @__PURE__ */ jsx(UPlotHost, {
65746
+ optionsKey: `${token}#${zeroBased ? "z" : ""}`,
65747
+ buildOptions,
65748
+ data,
65749
+ height,
65750
+ inline: true
65751
+ })
65752
+ });
65753
+ }
65754
+ //#endregion
65755
+ //#region src/composites/device-history/history-window.ts
65756
+ var HISTORY_WINDOW_IDS = [
65757
+ "24h",
65758
+ "7d",
65759
+ "30d",
65760
+ "1y",
65761
+ "custom"
65762
+ ];
65763
+ var H = 36e5;
65764
+ var D = 24 * H;
65765
+ var HISTORY_WINDOW_MS = {
65766
+ "24h": D,
65767
+ "7d": 7 * D,
65768
+ "30d": 30 * D,
65769
+ "1y": 365 * D
65770
+ };
65771
+ function isHistoryWindowId(v) {
65772
+ return v !== null && v !== void 0 && HISTORY_WINDOW_IDS.includes(v);
65773
+ }
65774
+ /**
65775
+ * The window a preset means at `now`. `custom` without a valid range (from
65776
+ * before to) falls back to the last 24 hours rather than an empty window.
65777
+ */
65778
+ function resolveHistoryWindow(id, now, custom) {
65779
+ if (id === "custom") {
65780
+ if (custom && custom.to > custom.from) return {
65781
+ from: custom.from,
65782
+ to: custom.to
65783
+ };
65784
+ return {
65785
+ from: now - HISTORY_WINDOW_MS["24h"],
65786
+ to: now
65787
+ };
65788
+ }
65789
+ return {
65790
+ from: now - HISTORY_WINDOW_MS[id],
65791
+ to: now
65792
+ };
65793
+ }
65794
+ /** The period of the same length just before `w`, and the shift that lays it over `w`. */
65795
+ function previousWindow(w) {
65796
+ const span = w.to - w.from;
65797
+ return {
65798
+ window: {
65799
+ from: w.from - span,
65800
+ to: w.to - span
65801
+ },
65802
+ shiftMs: span
65803
+ };
65804
+ }
65805
+ /** Encode a custom range for the URL: `<fromMs>-<toMs>`. */
65806
+ function encodeCustomWindow(w) {
65807
+ return `${String(Math.round(w.from))}-${String(Math.round(w.to))}`;
65808
+ }
65809
+ function decodeCustomWindow(raw) {
65810
+ if (!raw) return null;
65811
+ const m = /^(\d+)-(\d+)$/.exec(raw);
65812
+ if (!m) return null;
65813
+ const from = Number(m[1]);
65814
+ const to = Number(m[2]);
65815
+ return to > from ? {
65816
+ from,
65817
+ to
65818
+ } : null;
65819
+ }
65820
+ /** Milliseconds a rollup bucket of a resolution covers (`1d` is nominal across DST). */
65821
+ var RESOLUTION_BUCKET_MS = {
65822
+ "5m": 5 * 6e4,
65823
+ "1h": H,
65824
+ "1d": D
65825
+ };
65826
+ //#endregion
65827
+ //#region src/composites/device-history/history-model.ts
65828
+ /**
65829
+ * The pure half of a device's History view: which charts a device's series
65830
+ * make, in which sections, and how a `query` answer becomes chart data.
65831
+ *
65832
+ * Grouping mirrors the State panel: one section per capability in
65833
+ * `HISTORY_CATALOG` order (its fields in catalog order), then one section per
65834
+ * producer addon, sorted. Inside a section: one line chart per UNIT (never
65835
+ * two units on one axis), one state timeline per boolean or enum, the
65836
+ * derived energy as bars. A camera's occupancy is ONE chart of every
65837
+ * (zone × class) scope.
65838
+ */
65839
+ /** The producer series a camera's occupancy chart reads. */
65840
+ var OCCUPANCY_KEY = "addon:pipeline-analytics/occupancy";
65841
+ /** `cap:<capName>.<fieldPath>` → its parts; `null` for any other key. */
65842
+ function parseCapKey(key) {
65843
+ if (!key.startsWith("cap:")) return null;
65844
+ const body = key.slice(4);
65845
+ const dot = body.indexOf(".");
65846
+ if (dot <= 0) return null;
65847
+ return {
65848
+ cap: body.slice(0, dot),
65849
+ field: body.slice(dot + 1)
65850
+ };
65851
+ }
65852
+ /** `addon:<addonId>/<name>` → its parts; `null` for any other key. */
65853
+ function parseProducerKey(key) {
65854
+ if (!key.startsWith("addon:")) return null;
65855
+ const body = key.slice(6);
65856
+ const slash = body.indexOf("/");
65857
+ if (slash <= 0) return null;
65858
+ return {
65859
+ addonId: body.slice(0, slash),
65860
+ name: body.slice(slash + 1)
65861
+ };
65862
+ }
65863
+ var CATALOG_CAPS = Object.keys(HISTORY_CATALOG);
65864
+ function capRank(cap) {
65865
+ const i = CATALOG_CAPS.indexOf(cap);
65866
+ return i < 0 ? CATALOG_CAPS.length : i;
65867
+ }
65868
+ function fieldRank(cap, field) {
65869
+ const fields = Object.keys(HISTORY_CATALOG[cap] ?? {});
65870
+ const i = fields.indexOf(field);
65871
+ return i < 0 ? fields.length : i;
65872
+ }
65873
+ var isState = (s) => s.kind === "boolean" || s.kind === "enum";
65874
+ /** One section's charts: a line chart per unit (first-seen order), then one timeline per state. */
65875
+ function chartsOf(sectionId, title, list) {
65876
+ const out = [];
65877
+ const byUnit = /* @__PURE__ */ new Map();
65878
+ for (const s of list) {
65879
+ if (s.key === DEVICE_HISTORY_ENERGY_KEY) {
65880
+ out.push({
65881
+ type: "energy",
65882
+ id: `${sectionId}:energy`,
65883
+ title: s.displayName,
65884
+ series: s
65885
+ });
65886
+ continue;
65887
+ }
65888
+ if (isState(s)) continue;
65889
+ const u = s.unit ?? "";
65890
+ byUnit.set(u, [...byUnit.get(u) ?? [], s]);
65891
+ }
65892
+ const lines = [...byUnit.entries()].map(([unit, series]) => {
65893
+ const names = [...new Set(series.map((s) => s.displayName))];
65894
+ return {
65895
+ type: "lines",
65896
+ id: `${sectionId}:unit:${unit}`,
65897
+ title: names.length === 1 ? names[0] ?? title : unit === "" ? title : `${title} (${unit})`,
65898
+ unit: unit === "" ? null : unit,
65899
+ series
65900
+ };
65901
+ });
65902
+ const states = list.filter(isState).map((s) => ({
65903
+ type: "states",
65904
+ id: `${sectionId}:state:${s.key}:${s.scope}`,
65905
+ title: s.displayName,
65906
+ series: s
65907
+ }));
65908
+ return [
65909
+ ...lines,
65910
+ ...out,
65911
+ ...states
65912
+ ];
65913
+ }
65914
+ /**
65915
+ * Group a device's series into sections. Energy (a derived key) joins the
65916
+ * `power-meter` section when the device has one, else gets its own.
65917
+ */
65918
+ function groupDeviceSeries(series, capTitle) {
65919
+ const caps = /* @__PURE__ */ new Map();
65920
+ const producers = /* @__PURE__ */ new Map();
65921
+ const occupancy = [];
65922
+ let energy = null;
65923
+ for (const s of series) {
65924
+ if (s.key === DEVICE_HISTORY_ENERGY_KEY) {
65925
+ energy = s;
65926
+ continue;
65927
+ }
65928
+ if (s.key === "addon:pipeline-analytics/occupancy") {
65929
+ occupancy.push(s);
65930
+ continue;
65931
+ }
65932
+ const cap = parseCapKey(s.key);
65933
+ if (cap) {
65934
+ caps.set(cap.cap, [...caps.get(cap.cap) ?? [], s]);
65935
+ continue;
65936
+ }
65937
+ const addon = parseProducerKey(s.key)?.addonId ?? "other";
65938
+ producers.set(addon, [...producers.get(addon) ?? [], s]);
65939
+ }
65940
+ if (energy !== null) if (caps.has("power-meter")) caps.set("power-meter", [...caps.get("power-meter") ?? [], energy]);
65941
+ else caps.set("energy", [energy]);
65942
+ const capSections = [...caps.entries()].sort(([a], [b]) => capRank(a) - capRank(b) || a.localeCompare(b)).map(([cap, list]) => {
65943
+ const sorted = [...list].sort((x, y) => {
65944
+ const fx = parseCapKey(x.key)?.field ?? "";
65945
+ const fy = parseCapKey(y.key)?.field ?? "";
65946
+ return fieldRank(cap, fx) - fieldRank(cap, fy) || x.scope.localeCompare(y.scope);
65947
+ });
65948
+ const id = `cap:${cap}`;
65949
+ return {
65950
+ id,
65951
+ kind: "cap",
65952
+ name: cap,
65953
+ charts: chartsOf(id, capTitle(cap), sorted)
65954
+ };
65955
+ });
65956
+ if (occupancy.length > 0) {
65957
+ const list = producers.get("pipeline-analytics") ?? [];
65958
+ producers.set("pipeline-analytics", list);
65959
+ }
65960
+ const producerSections = [...producers.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([addon, list]) => {
65961
+ const id = `addon:${addon}`;
65962
+ const charts = chartsOf(id, capTitle(addon), list);
65963
+ return {
65964
+ id,
65965
+ kind: "producer",
65966
+ name: addon,
65967
+ charts: [...addon === "pipeline-analytics" && occupancy.length > 0 ? [{
65968
+ type: "occupancy",
65969
+ id: `${id}:occupancy`,
65970
+ title: occupancy[0]?.displayName ?? "Occupancy",
65971
+ series: [...occupancy].sort((a, b) => a.scope.localeCompare(b.scope))
65972
+ }] : [], ...charts]
65973
+ };
65974
+ });
65975
+ return [...capSections, ...producerSections].filter((s) => s.charts.length > 0);
65976
+ }
65977
+ /** The unique keys a set of charts reads (one `queryMany` item each: every scope comes back). */
65978
+ function chartKeys(charts) {
65979
+ const keys = /* @__PURE__ */ new Set();
65980
+ for (const c of charts) if (c.type === "lines" || c.type === "occupancy") for (const s of c.series) keys.add(s.key);
65981
+ else keys.add(c.series.key);
65982
+ return [...keys];
65983
+ }
65984
+ /** One series of a `query` answer, found by scope. */
65985
+ function seriesOf(result, scope) {
65986
+ return result?.series.find((s) => s.scope === scope);
65987
+ }
65988
+ /** The number a line shows for a point: time-weighted mean, else mean, else the last value. */
65989
+ function levelOf(p, kind) {
65990
+ if (kind === "counter") return p.last;
65991
+ return p.twMean ?? p.mean ?? p.last;
65992
+ }
65993
+ /**
65994
+ * Line points for a gauge or counter. A rolled-up answer carries its
65995
+ * min/max (a band). A CAP series is change-only and held, so the window
65996
+ * opens on the value in force (`before`); a producer's buckets are not held
65997
+ * and an absent bucket is a hole.
65998
+ */
65999
+ function linePoints(info, result, from) {
66000
+ const s = seriesOf(result, info.scope);
66001
+ const rollup = result !== void 0 && result.resolution !== "raw";
66002
+ const held = info.source === "cap";
66003
+ const points = (s?.points ?? []).map((p) => ({
66004
+ t: p.t,
66005
+ v: levelOf(p, info.kind),
66006
+ ...rollup && info.kind === "gauge" ? {
66007
+ min: p.min,
66008
+ max: p.max
66009
+ } : {}
66010
+ }));
66011
+ if (held && s?.before && s.before.value !== null && (points[0]?.t ?? Infinity) > from) points.unshift({
66012
+ t: from,
66013
+ v: s.before.value
66014
+ });
66015
+ return {
66016
+ points,
66017
+ gapMs: held || result === void 0 ? void 0 : gapThresholdMs(result.resolution === "raw" ? 1e4 : RESOLUTION_BUCKET_MS[result.resolution]),
66018
+ held
66019
+ };
66020
+ }
66021
+ /** Shift every point by `shiftMs` (a previous period laid over the current one). */
66022
+ function shiftPoints(points, shiftMs) {
66023
+ return points.map((p) => ({
66024
+ ...p,
66025
+ t: p.t + shiftMs
66026
+ }));
66027
+ }
66028
+ /** The state names a boolean series takes, `false` first. */
66029
+ var BOOLEAN_STATES = ["false", "true"];
66030
+ /**
66031
+ * A state series as segments plus the exact time spent in each state.
66032
+ *
66033
+ * Raw points are changes. A rollup bucket is drawn in its DOMINANT state
66034
+ * (the one held longest in it), while the totals add the bucket's real
66035
+ * per-state milliseconds — so "open 2h 14m" stays exact at any zoom.
66036
+ */
66037
+ function stateRow(info, result, from, to) {
66038
+ const states = info.kind === "boolean" ? BOOLEAN_STATES : info.values ?? [];
66039
+ const s = seriesOf(result, info.scope);
66040
+ const nameOf = (p) => info.kind === "boolean" ? p.last === null ? null : p.last >= .5 ? "true" : "false" : p.state;
66041
+ if (result === void 0 || s === void 0) return {
66042
+ segments: [],
66043
+ totals: /* @__PURE__ */ new Map(),
66044
+ states
66045
+ };
66046
+ const beforeState = s.before === null ? null : info.kind === "boolean" ? s.before.value === null ? null : s.before.value >= .5 ? "true" : "false" : s.before.state;
66047
+ if (result.resolution === "raw") {
66048
+ const segments = segmentsFromChanges(s.points.map((p) => ({
66049
+ t: p.t,
66050
+ state: nameOf(p)
66051
+ })), from, to, beforeState);
66052
+ const totals = /* @__PURE__ */ new Map();
66053
+ for (const seg of segments) if (seg.state !== null) totals.set(seg.state, (totals.get(seg.state) ?? 0) + (seg.to - seg.from));
66054
+ return {
66055
+ segments,
66056
+ totals,
66057
+ states
66058
+ };
66059
+ }
66060
+ const bucketMs = RESOLUTION_BUCKET_MS[result.resolution];
66061
+ const totals = /* @__PURE__ */ new Map();
66062
+ const segments = [];
66063
+ for (const p of s.points) {
66064
+ const end = Math.min(to, p.t + bucketMs);
66065
+ const start = Math.max(from, p.t);
66066
+ if (!(end > start)) continue;
66067
+ const perState = bucketStates(p, info, bucketMs);
66068
+ let dominant = null;
66069
+ let best = -1;
66070
+ for (const [name, ms] of perState) {
66071
+ totals.set(name, (totals.get(name) ?? 0) + ms * ((end - start) / bucketMs));
66072
+ if (ms > best) {
66073
+ best = ms;
66074
+ dominant = name;
66075
+ }
66076
+ }
66077
+ const prev = segments.at(-1);
66078
+ if (prev && prev.state === dominant && prev.to === start) segments[segments.length - 1] = {
66079
+ ...prev,
66080
+ to: end
66081
+ };
66082
+ else segments.push({
66083
+ from: start,
66084
+ to: end,
66085
+ state: dominant
66086
+ });
66087
+ }
66088
+ return {
66089
+ segments: segments.filter((x) => x.state !== null),
66090
+ totals,
66091
+ states
66092
+ };
66093
+ }
66094
+ function bucketStates(p, info, bucketMs) {
66095
+ if (info.kind === "boolean") {
66096
+ const frac = p.twMean ?? p.mean;
66097
+ if (frac === null) return p.last === null ? /* @__PURE__ */ new Map() : new Map([[p.last >= .5 ? "true" : "false", bucketMs]]);
66098
+ return new Map([["true", frac * bucketMs], ["false", (1 - frac) * bucketMs]]);
66099
+ }
66100
+ if (p.states) return new Map(Object.entries(p.states));
66101
+ return p.state === null ? /* @__PURE__ */ new Map() : new Map([[p.state, bucketMs]]);
66102
+ }
66103
+ /** Energy bars: one bucket per point, the kWh used in it (`delta`). */
66104
+ function energyBuckets(result) {
66105
+ const s = result?.series[0];
66106
+ const bucketMs = result === void 0 || result.resolution === "raw" ? RESOLUTION_BUCKET_MS["1h"] : RESOLUTION_BUCKET_MS[result.resolution];
66107
+ return {
66108
+ buckets: (s?.points ?? []).map((p) => ({
66109
+ t: p.t,
66110
+ values: [p.delta]
66111
+ })),
66112
+ bucketMs
66113
+ };
66114
+ }
66115
+ function occupancyLines(series) {
66116
+ return series.map((s) => {
66117
+ const parsed = parseOccupancyScope(s.scope);
66118
+ return {
66119
+ scope: s.scope,
66120
+ zoneId: parsed.zoneId,
66121
+ className: parsed.className
66122
+ };
66123
+ });
66124
+ }
66125
+ /** The filter values an occupancy chart offers: zones and classes that exist. */
66126
+ function occupancyFacets(lines) {
66127
+ return {
66128
+ zones: [...new Set(lines.map((l) => l.zoneId))].sort(),
66129
+ classes: [...new Set(lines.flatMap((l) => l.className === void 0 ? [] : [l.className]))].sort()
66130
+ };
66131
+ }
66132
+ /**
66133
+ * The lines a filter keeps. With no class picked the per-zone AGGREGATE lines
66134
+ * show (summing classes again would double count); a class shows that class
66135
+ * in every (or the picked) zone.
66136
+ */
66137
+ function filterOccupancyLines(lines, filter) {
66138
+ return lines.filter((l) => (filter.zone === null || l.zoneId === filter.zone) && (filter.className === null ? l.className === void 0 : l.className === filter.className));
66139
+ }
66140
+ //#endregion
66141
+ //#region src/composites/device-history/HistoryChart.tsx
66142
+ /**
66143
+ * One chart of a device's History view, from its spec and the reads.
66144
+ * Lines → `TimeSeriesChart`, states → `StateTimeline`, energy → `BarSeries`
66145
+ * with the window total, occupancy → one `TimeSeriesChart` of every
66146
+ * (zone × class) scope with zone and class filters.
66147
+ *
66148
+ * A read that failed says so in place of the chart; a read that answered no
66149
+ * point is the empty state — never a flat zero.
66150
+ */
66151
+ var ALL = "__all__";
66152
+ /** Operator words for a boolean field's two states. */
66153
+ function booleanLabels(key, t) {
66154
+ const field = key.split(".").at(-1) ?? "";
66155
+ if (/open/i.test(field)) return {
66156
+ on: t("uiLibrary.history.state.open", "open"),
66157
+ off: t("uiLibrary.history.state.closed", "closed")
66158
+ };
66159
+ if (/online|connected/i.test(field)) return {
66160
+ on: t("uiLibrary.history.state.online", "online"),
66161
+ off: t("uiLibrary.history.state.offline", "offline")
66162
+ };
66163
+ if (/detected|tampered/i.test(field)) return {
66164
+ on: t("uiLibrary.history.state.detected", "detected"),
66165
+ off: t("uiLibrary.history.state.clear", "clear")
66166
+ };
66167
+ return {
66168
+ on: t("uiLibrary.history.state.on", "on"),
66169
+ off: t("uiLibrary.history.state.off", "off")
66170
+ };
66171
+ }
66172
+ function failure(entry) {
66173
+ return entry !== void 0 && !entry.ok ? entry.error : null;
66174
+ }
66175
+ function HistoryChart({ spec, entryFor, reads, window, compare, syncKey, actions }) {
66176
+ const t = useUiText();
66177
+ const pending = reads === null;
66178
+ if (spec.type === "states") {
66179
+ const entry = entryFor(spec.series.key, false);
66180
+ const err = failure(entry);
66181
+ if (err) return /* @__PURE__ */ jsx(ReadFailed, {
66182
+ title: spec.title,
66183
+ error: err,
66184
+ actions
66185
+ });
66186
+ const result = entry?.ok ? entry.result : void 0;
66187
+ const row = stateRow(spec.series, result, window.from, window.to);
66188
+ const labels = booleanLabels(spec.series.key, t);
66189
+ return /* @__PURE__ */ jsx(StateTimeline, {
66190
+ title: spec.title,
66191
+ range: window,
66192
+ syncKey,
66193
+ pending,
66194
+ actions,
66195
+ rows: [{
66196
+ id: spec.id,
66197
+ label: spec.title,
66198
+ kind: spec.series.kind === "boolean" ? "boolean" : "enum",
66199
+ states: row.states,
66200
+ segments: row.segments,
66201
+ totals: row.totals,
66202
+ stateLabel: spec.series.kind === "boolean" ? (s) => s === "true" ? labels.on : labels.off : void 0
66203
+ }]
66204
+ });
66205
+ }
66206
+ if (spec.type === "energy") {
66207
+ const entry = entryFor(spec.series.key, false);
66208
+ const err = failure(entry);
66209
+ if (err) return /* @__PURE__ */ jsx(ReadFailed, {
66210
+ title: spec.title,
66211
+ error: err,
66212
+ actions
66213
+ });
66214
+ const { buckets, bucketMs } = energyBuckets(entry?.ok ? entry.result : void 0);
66215
+ return /* @__PURE__ */ jsx(BarSeries, {
66216
+ title: spec.title,
66217
+ layers: [{
66218
+ id: "kwh",
66219
+ label: spec.title,
66220
+ colorIndex: 4
66221
+ }],
66222
+ buckets,
66223
+ bucketMs,
66224
+ range: window,
66225
+ unit: spec.series.unit ?? "kWh",
66226
+ showTotal: true,
66227
+ syncKey,
66228
+ pending,
66229
+ actions
66230
+ });
66231
+ }
66232
+ if (spec.type === "occupancy") return /* @__PURE__ */ jsx(OccupancyChart, {
66233
+ spec,
66234
+ entryFor,
66235
+ reads,
66236
+ window,
66237
+ compare,
66238
+ syncKey,
66239
+ actions
66240
+ });
66241
+ return /* @__PURE__ */ jsx(LinesChart, {
66242
+ title: spec.title,
66243
+ series: spec.series,
66244
+ entryFor,
66245
+ reads,
66246
+ window,
66247
+ compare,
66248
+ syncKey,
66249
+ actions,
66250
+ labelOf: (s) => spec.series.length > 1 || s.scope !== "" ? seriesLabel(s) : spec.title
66251
+ });
66252
+ }
66253
+ function seriesLabel(s) {
66254
+ return s.scope === "" ? s.displayName : `${s.displayName} · ${s.scope}`;
66255
+ }
66256
+ function LinesChart({ title, series, entryFor, reads, window, compare, syncKey, actions, labelOf, toolbar, colorOf }) {
66257
+ const t = useUiText();
66258
+ const errors = [...new Set(series.map((s) => failure(entryFor(s.key, false))).filter((e) => e !== null))];
66259
+ const defs = useMemo(() => {
66260
+ const out = [];
66261
+ series.forEach((s, i) => {
66262
+ const cur = entryFor(s.key, false);
66263
+ const result = cur?.ok ? cur.result : void 0;
66264
+ const shaped = linePoints(s, result, window.from);
66265
+ const color = colorOf?.(s, i) ?? i;
66266
+ const interpolation = shaped.held && result?.resolution === "raw" ? "step" : "linear";
66267
+ out.push({
66268
+ id: `${s.key}|${s.scope}`,
66269
+ label: labelOf(s),
66270
+ unit: s.unit,
66271
+ colorIndex: color,
66272
+ points: shaped.points,
66273
+ gapMs: shaped.gapMs,
66274
+ interpolation,
66275
+ band: series.length === 1 && result !== void 0 && result.resolution !== "raw"
66276
+ });
66277
+ if (compare && reads?.previous) {
66278
+ const prev = entryFor(s.key, true);
66279
+ const prevResult = prev?.ok ? prev.result : void 0;
66280
+ if (prevResult) {
66281
+ const prevShaped = linePoints(s, prevResult, window.from - reads.shiftMs);
66282
+ out.push({
66283
+ id: `${s.key}|${s.scope}|previous`,
66284
+ label: t("uiLibrary.history.previousOf", "{{label}} (previous period)", { label: labelOf(s) }),
66285
+ unit: s.unit,
66286
+ colorIndex: color,
66287
+ points: shiftPoints(prevShaped.points, reads.shiftMs),
66288
+ gapMs: prevShaped.gapMs,
66289
+ interpolation,
66290
+ compare: true
66291
+ });
66292
+ }
66293
+ }
66294
+ });
66295
+ return out;
66296
+ }, [
66297
+ series,
66298
+ entryFor,
66299
+ window.from,
66300
+ compare,
66301
+ reads,
66302
+ labelOf,
66303
+ colorOf,
66304
+ t
66305
+ ]);
66306
+ if (errors.length > 0) return /* @__PURE__ */ jsx(ReadFailed, {
66307
+ title,
66308
+ error: errors.join("; "),
66309
+ actions
66310
+ });
66311
+ return /* @__PURE__ */ jsx(TimeSeriesChart, {
66312
+ title,
66313
+ series: defs,
66314
+ range: window,
66315
+ syncKey,
66316
+ pending: reads === null,
66317
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [toolbar, actions] })
66318
+ });
66319
+ }
66320
+ function OccupancyChart({ spec, entryFor, reads, window, compare, syncKey, actions }) {
66321
+ const t = useUiText();
66322
+ const [zone, setZone] = useState(ALL);
66323
+ const [cls, setCls] = useState(ALL);
66324
+ const lines = useMemo(() => occupancyLines(spec.series), [spec.series]);
66325
+ const facets = useMemo(() => occupancyFacets(lines), [lines]);
66326
+ const kept = new Set(filterOccupancyLines(lines, {
66327
+ zone: zone === ALL ? null : zone,
66328
+ className: cls === ALL ? null : cls
66329
+ }).map((l) => l.scope));
66330
+ const shown = spec.series.filter((s) => kept.has(s.scope));
66331
+ const zoneName = (z) => z === "" ? t("uiLibrary.history.wholeFrame", "Whole frame") : z;
66332
+ const labelOf = (s) => {
66333
+ const l = lines.find((x) => x.scope === s.scope);
66334
+ if (!l) return s.scope;
66335
+ return l.className === void 0 ? zoneName(l.zoneId) : `${zoneName(l.zoneId)} · ${l.className}`;
66336
+ };
66337
+ const colorOf = (s) => {
66338
+ const own = lines.find((l) => l.scope === s.scope);
66339
+ const peers = lines.filter((l) => l.className === own?.className);
66340
+ return Math.max(0, peers.findIndex((l) => l.scope === s.scope));
66341
+ };
66342
+ const toolbar = /* @__PURE__ */ jsxs(Fragment$1, { children: [facets.zones.length > 1 && /* @__PURE__ */ jsx(Select, {
66343
+ "aria-label": t("uiLibrary.history.filterZone", "Zone"),
66344
+ value: zone,
66345
+ onChange: (e) => setZone(e.target.value),
66346
+ className: "h-6 text-[10px] py-0",
66347
+ options: [{
66348
+ value: ALL,
66349
+ label: t("uiLibrary.history.allZones", "All zones")
66350
+ }, ...facets.zones.map((z) => ({
66351
+ value: z,
66352
+ label: zoneName(z)
66353
+ }))]
66354
+ }), facets.classes.length > 0 && /* @__PURE__ */ jsx(Select, {
66355
+ "aria-label": t("uiLibrary.history.filterClass", "Class"),
66356
+ value: cls,
66357
+ onChange: (e) => setCls(e.target.value),
66358
+ className: "h-6 text-[10px] py-0",
66359
+ options: [{
66360
+ value: ALL,
66361
+ label: t("uiLibrary.history.allClasses", "All classes")
66362
+ }, ...facets.classes.map((c) => ({
66363
+ value: c,
66364
+ label: c
66365
+ }))]
66366
+ })] });
66367
+ return /* @__PURE__ */ jsx(LinesChart, {
66368
+ title: spec.title,
66369
+ series: shown,
66370
+ entryFor,
66371
+ reads,
66372
+ window,
66373
+ compare,
66374
+ syncKey,
66375
+ actions,
66376
+ toolbar,
66377
+ labelOf,
66378
+ colorOf
66379
+ });
66380
+ }
66381
+ function ReadFailed({ title, error, actions }) {
66382
+ const t = useUiText();
66383
+ return /* @__PURE__ */ jsxs("figure", {
66384
+ className: "min-w-0 m-0",
66385
+ children: [/* @__PURE__ */ jsxs("div", {
66386
+ className: "flex items-baseline justify-between gap-2 mb-1",
66387
+ children: [/* @__PURE__ */ jsx("figcaption", {
66388
+ className: "text-[10px] uppercase tracking-wider text-foreground-subtle truncate",
66389
+ children: title
66390
+ }), actions]
66391
+ }), /* @__PURE__ */ jsx(ChartPlaceholder, {
66392
+ height: "md",
66393
+ message: t("uiLibrary.history.readFailed", "This history could not be read: {{error}}", { error })
66394
+ })]
66395
+ });
66396
+ }
66397
+ //#endregion
66398
+ //#region src/composites/device-history/history-selection.ts
66399
+ /**
66400
+ * A history SELECTION — which series a chart overlays — and its two
66401
+ * spellings: the History page URL (`?s=` repeated) and the account
66402
+ * preference row of saved panels (`admin.history-panels`, D894).
66403
+ *
66404
+ * A series is (subject, key, scope). The subject is a device (by numeric id)
66405
+ * or a node (by id); the scope `''` is the series' aggregate.
66406
+ */
66407
+ var HistorySubjectSchema = z.discriminatedUnion("kind", [z.object({
66408
+ kind: z.literal("device"),
66409
+ deviceId: z.number().int()
66410
+ }), z.object({
66411
+ kind: z.literal("node"),
66412
+ nodeId: z.string().min(1)
66413
+ })]);
66414
+ var HistorySeriesRefSchema = z.object({
66415
+ subject: HistorySubjectSchema,
66416
+ key: z.string().min(1),
66417
+ scope: z.string()
66418
+ });
66419
+ /** The most series one overlay may carry — one `queryMany` batch. */
66420
+ var HISTORY_SELECTION_MAX = 24;
66421
+ /** Stable identity of a series ref (React key, dedup, colour slot). */
66422
+ function seriesRefId(ref) {
66423
+ return `${ref.subject.kind === "device" ? `d:${String(ref.subject.deviceId)}` : `n:${ref.subject.nodeId}`}|${ref.key}|${ref.scope}`;
66424
+ }
66425
+ /** One `?s=` value: `d,<deviceId>,<key>,<scope>` or `n,<nodeId>,<key>,<scope>`, each part URI-encoded. */
66426
+ function encodeSeriesRef(ref) {
66427
+ return [
66428
+ ...ref.subject.kind === "device" ? ["d", String(ref.subject.deviceId)] : ["n", ref.subject.nodeId],
66429
+ ref.key,
66430
+ ref.scope
66431
+ ].map((p) => encodeURIComponent(p)).join(",");
66432
+ }
66433
+ function decodeSeriesRef(raw) {
66434
+ const parts = raw.split(",");
66435
+ if (parts.length !== 4) return null;
66436
+ let decoded;
66437
+ try {
66438
+ decoded = parts.map((p) => decodeURIComponent(p));
66439
+ } catch {
66440
+ return null;
66441
+ }
66442
+ const [kind, id, key, scope] = decoded;
66443
+ if (key === void 0 || key === "" || scope === void 0 || id === void 0) return null;
66444
+ if (kind === "d") {
66445
+ const deviceId = Number(id);
66446
+ if (!Number.isInteger(deviceId)) return null;
66447
+ return {
66448
+ subject: {
66449
+ kind: "device",
66450
+ deviceId
66451
+ },
66452
+ key,
66453
+ scope
66454
+ };
66455
+ }
66456
+ if (kind === "n" && id !== "") return {
66457
+ subject: {
66458
+ kind: "node",
66459
+ nodeId: id
66460
+ },
66461
+ key,
66462
+ scope
66463
+ };
66464
+ return null;
66465
+ }
66466
+ /** Unique refs, in order, capped at {@link HISTORY_SELECTION_MAX}. */
66467
+ function normalizeSelection(refs) {
66468
+ const seen = /* @__PURE__ */ new Set();
66469
+ const out = [];
66470
+ for (const r of refs) {
66471
+ const id = seriesRefId(r);
66472
+ if (seen.has(id)) continue;
66473
+ seen.add(id);
66474
+ out.push(r);
66475
+ if (out.length >= 24) break;
66476
+ }
66477
+ return out;
66478
+ }
66479
+ /** The History page path showing `refs` over `window`. */
66480
+ function historyPageHref(refs, window) {
66481
+ const params = new URLSearchParams();
66482
+ for (const r of normalizeSelection(refs)) params.append("s", encodeSeriesRef(r));
66483
+ if (window !== void 0 && window !== "custom") params.set("window", window);
66484
+ return `${HISTORY_PAGE_PATH}?${params.toString()}`;
66485
+ }
66486
+ /** The cross-device History page's route. */
66487
+ var HISTORY_PAGE_PATH = "/system/history";
66488
+ /** The account preference namespace that holds saved panels and hidden charts. */
66489
+ var HISTORY_PANELS_NAMESPACE = "admin.history-panels";
66490
+ var WindowIdSchema = z.enum(HISTORY_WINDOW_IDS);
66491
+ var HistoryPanelSchema = z.object({
66492
+ id: z.string().min(1),
66493
+ name: z.string().min(1).max(80),
66494
+ window: WindowIdSchema,
66495
+ selection: z.array(HistorySeriesRefSchema).max(24)
66496
+ });
66497
+ var HistoryPanelsValueSchema = z.object({
66498
+ version: z.literal(1),
66499
+ panels: z.array(HistoryPanelSchema).max(50),
66500
+ hidden: z.record(z.string(), z.array(z.string()).max(200))
66501
+ });
66502
+ var EMPTY_HISTORY_PANELS = {
66503
+ version: 1,
66504
+ panels: [],
66505
+ hidden: {}
66506
+ };
66507
+ /**
66508
+ * Read a stored row's value. A row this client cannot read (another
66509
+ * version, a hand edit) is answered as empty rather than throwing — and the
66510
+ * caller must not write over it blindly: `parsed: false` says so.
66511
+ */
66512
+ function readHistoryPanels(value) {
66513
+ if (value === void 0 || value === null) return {
66514
+ value: EMPTY_HISTORY_PANELS,
66515
+ parsed: true
66516
+ };
66517
+ const r = HistoryPanelsValueSchema.safeParse(value);
66518
+ return r.success ? {
66519
+ value: r.data,
66520
+ parsed: true
66521
+ } : {
66522
+ value: EMPTY_HISTORY_PANELS,
66523
+ parsed: false
66524
+ };
66525
+ }
66526
+ /** A copy of `value` with chart `chartId` hidden (or shown again) on device `deviceId`. */
66527
+ function withHiddenChart(value, deviceId, chartId, hidden) {
66528
+ const key = String(deviceId);
66529
+ const current = value.hidden[key] ?? [];
66530
+ const next = hidden ? current.includes(chartId) ? current : [...current, chartId] : current.filter((c) => c !== chartId);
66531
+ const rest = Object.fromEntries(Object.entries(value.hidden).filter(([k]) => k !== key));
66532
+ return {
66533
+ ...value,
66534
+ hidden: next.length > 0 ? {
66535
+ ...rest,
66536
+ [key]: next
66537
+ } : rest
66538
+ };
66539
+ }
66540
+ /** A copy of `value` with `panel` saved (replacing one with the same id). */
66541
+ function withPanel(value, panel) {
66542
+ const others = value.panels.filter((p) => p.id !== panel.id);
66543
+ return {
66544
+ ...value,
66545
+ panels: [...others, panel]
66546
+ };
66547
+ }
66548
+ function withoutPanel(value, panelId) {
66549
+ return {
66550
+ ...value,
66551
+ panels: value.panels.filter((p) => p.id !== panelId)
66552
+ };
66553
+ }
66554
+ //#endregion
66555
+ //#region src/composites/device-history/HistoryWindowBar.tsx
66556
+ /** `datetime-local` value for an instant, in the browser's zone. */
66557
+ function toLocalInput(ms) {
66558
+ const d = new Date(ms);
66559
+ const pad = (n) => String(n).padStart(2, "0");
66560
+ return `${String(d.getFullYear())}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
66561
+ }
66562
+ function fromLocalInput(v) {
66563
+ const ms = new Date(v).getTime();
66564
+ return Number.isFinite(ms) ? ms : null;
66565
+ }
66566
+ function HistoryWindowBar({ value, onChange, custom, onCustomChange, compare, onCompareChange }) {
66567
+ const t = useUiText();
66568
+ const now = Date.now();
66569
+ const range = custom ?? {
66570
+ from: now - 864e5,
66571
+ to: now
66572
+ };
66573
+ return /* @__PURE__ */ jsxs("div", {
66574
+ className: "flex flex-wrap items-center gap-x-3 gap-y-2 min-w-0",
66575
+ children: [
66576
+ /* @__PURE__ */ jsx(ChartWindowToggle, {
66577
+ ariaLabel: t("uiLibrary.history.window", "Time window"),
66578
+ value,
66579
+ onChange,
66580
+ options: [
66581
+ {
66582
+ id: "24h",
66583
+ label: t("uiLibrary.history.window24h", "24 h")
66584
+ },
66585
+ {
66586
+ id: "7d",
66587
+ label: t("uiLibrary.history.window7d", "7 d")
66588
+ },
66589
+ {
66590
+ id: "30d",
66591
+ label: t("uiLibrary.history.window30d", "30 d")
66592
+ },
66593
+ {
66594
+ id: "1y",
66595
+ label: t("uiLibrary.history.window1y", "1 y")
66596
+ },
66597
+ {
66598
+ id: "custom",
66599
+ label: t("uiLibrary.history.windowCustom", "Custom")
66600
+ }
66601
+ ]
66602
+ }),
66603
+ value === "custom" && /* @__PURE__ */ jsxs("div", {
66604
+ className: "flex flex-wrap items-center gap-2",
66605
+ children: [
66606
+ /* @__PURE__ */ jsx(Input, {
66607
+ type: "datetime-local",
66608
+ "aria-label": t("uiLibrary.history.from", "From"),
66609
+ className: "h-7 w-auto text-xs",
66610
+ value: toLocalInput(range.from),
66611
+ onChange: (e) => {
66612
+ const from = fromLocalInput(e.target.value);
66613
+ if (from !== null && from < range.to) onCustomChange({
66614
+ from,
66615
+ to: range.to
66616
+ });
66617
+ }
66618
+ }),
66619
+ /* @__PURE__ */ jsx("span", {
66620
+ className: "text-[10px] text-foreground-subtle",
66621
+ children: "–"
66622
+ }),
66623
+ /* @__PURE__ */ jsx(Input, {
66624
+ type: "datetime-local",
66625
+ "aria-label": t("uiLibrary.history.to", "To"),
66626
+ className: "h-7 w-auto text-xs",
66627
+ value: toLocalInput(range.to),
66628
+ onChange: (e) => {
66629
+ const to = fromLocalInput(e.target.value);
66630
+ if (to !== null && to > range.from) onCustomChange({
66631
+ from: range.from,
66632
+ to
66633
+ });
66634
+ }
66635
+ })
66636
+ ]
66637
+ }),
66638
+ compare !== void 0 && onCompareChange && /* @__PURE__ */ jsxs("label", {
66639
+ className: "inline-flex items-center gap-1.5 text-[11px] text-foreground-muted cursor-pointer select-none",
66640
+ children: [/* @__PURE__ */ jsx(Checkbox, {
66641
+ checked: compare,
66642
+ onChange: (e) => onCompareChange(e.target.checked)
66643
+ }), t("uiLibrary.history.compare", "Compare with previous period")]
66644
+ })
66645
+ ]
66646
+ });
66647
+ }
66648
+ //#endregion
66649
+ //#region src/composites/device-history/history-reads.ts
66650
+ /**
66651
+ * Batched history reads. `queryMany` answers up to 24 reads over one window
66652
+ * in one call — but it is admin-only (its subjects are nested where the
66653
+ * device scope gate cannot see them). A principal it refuses reads each
66654
+ * DEVICE series through `query` instead; a node series has no other door and
66655
+ * is reported as refused.
66656
+ *
66657
+ * Every item gets an entry, in order: an answer, or the error that item met.
66658
+ * A failed item never hides the others, and is never an empty series.
66659
+ */
66660
+ var DEFAULT_MAX_POINTS = 800;
66661
+ /** Split into batches of at most `size`. */
66662
+ function chunk(items, size) {
66663
+ const out = [];
66664
+ for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
66665
+ return out;
66666
+ }
66667
+ /** Did the hub refuse this principal (rather than fail)? */
66668
+ function isRefusal(err) {
66669
+ if (typeof err !== "object" || err === null) return false;
66670
+ const data = "data" in err ? err.data : void 0;
66671
+ const code = typeof data === "object" && data !== null && "code" in data ? data.code : void 0;
66672
+ return code === "FORBIDDEN" || code === "UNAUTHORIZED";
66673
+ }
66674
+ function messageOf(err) {
66675
+ return err instanceof Error ? err.message : String(err);
66676
+ }
66677
+ /** Read every item over one window; one entry per item, in order. */
66678
+ async function readHistory(reader, items, window) {
66679
+ const maxPoints = window.maxPoints ?? DEFAULT_MAX_POINTS;
66680
+ const out = [];
66681
+ for (const batch of chunk(items, DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS)) try {
66682
+ const r = await reader.queryMany({
66683
+ items: [...batch],
66684
+ from: window.from,
66685
+ to: window.to,
66686
+ resolution: "auto",
66687
+ maxPoints
66688
+ });
66689
+ batch.forEach((_item, i) => {
66690
+ out.push(r.results[i] ?? {
66691
+ ok: false,
66692
+ error: "no answer for this item"
66693
+ });
66694
+ });
66695
+ } catch (err) {
66696
+ if (!isRefusal(err)) {
66697
+ const error = messageOf(err);
66698
+ for (const _item of batch) out.push({
66699
+ ok: false,
66700
+ error
66701
+ });
66702
+ continue;
66703
+ }
66704
+ out.push(...await readOneByOne(reader, batch, window.from, window.to, maxPoints, messageOf(err)));
66705
+ }
66706
+ return out;
66707
+ }
66708
+ async function readOneByOne(reader, batch, from, to, maxPoints, refusal) {
66709
+ return Promise.all(batch.map(async (item) => {
66710
+ if (!("deviceId" in item)) return {
66711
+ ok: false,
66712
+ error: refusal
66713
+ };
66714
+ try {
66715
+ return {
66716
+ ok: true,
66717
+ result: await reader.query({
66718
+ deviceId: item.deviceId,
66719
+ key: item.key,
66720
+ ...item.scope !== void 0 ? { scope: item.scope } : {},
66721
+ from,
66722
+ to,
66723
+ resolution: "auto",
66724
+ maxPoints
66725
+ })
66726
+ };
66727
+ } catch (err) {
66728
+ return {
66729
+ ok: false,
66730
+ error: messageOf(err)
66731
+ };
66732
+ }
66733
+ }));
66734
+ }
66735
+ //#endregion
66736
+ //#region src/composites/device-history/use-device-history.ts
66737
+ /**
66738
+ * The data half of the History views: is device-history installed, which
66739
+ * series exist, and their reads over a window (plus the previous period when
66740
+ * comparing). Each stage keeps "not answered" apart from "answered nothing"
66741
+ * (D315): a pending list is never an empty one, a failed read is never a
66742
+ * flat line.
66743
+ */
66744
+ var MINUTE = 6e4;
66745
+ /**
66746
+ * The instant a window ends at, rounded so the query key is stable between
66747
+ * renders and the view refreshes at a pace its width justifies.
66748
+ */
66749
+ function anchorNow(now, spanMs) {
66750
+ const step = spanMs <= HISTORY_WINDOW_MS["24h"] ? MINUTE : spanMs <= HISTORY_WINDOW_MS["7d"] ? 5 * MINUTE : spanMs <= HISTORY_WINDOW_MS["30d"] ? 15 * MINUTE : 60 * MINUTE;
66751
+ return Math.ceil(now / step) * step;
66752
+ }
66753
+ /** The tRPC client as the two doors `readHistory` needs. */
66754
+ function useHistoryReader() {
66755
+ const client = useSystem().trpcClient;
66756
+ return useMemo(() => ({
66757
+ queryMany: (input) => client.deviceHistory.queryMany.query(input),
66758
+ query: (input) => client.deviceHistory.query.query(input)
66759
+ }), [client]);
66760
+ }
66761
+ /** A device's series list, gated on device-history being present. */
66762
+ function useDeviceSeriesList(deviceId) {
66763
+ const presence = useCapabilityPresence("device-history");
66764
+ const client = useSystem().trpcClient;
66765
+ const q = useQuery({
66766
+ queryKey: [
66767
+ "device-history",
66768
+ "listSeries",
66769
+ deviceId
66770
+ ],
66771
+ queryFn: () => client.deviceHistory.listSeries.query({ deviceId }),
66772
+ enabled: presence === "present",
66773
+ staleTime: 6e4
66774
+ });
66775
+ return {
66776
+ presence,
66777
+ list: q.status === "success" ? {
66778
+ status: "ok",
66779
+ data: q.data
66780
+ } : q.status === "error" ? {
66781
+ status: "error",
66782
+ error: q.error.message
66783
+ } : { status: "pending" }
66784
+ };
66785
+ }
66786
+ /** Identity of a read item: subject + key (+ scope when narrowed). */
66787
+ function readItemId(item) {
66788
+ return `${"deviceId" in item ? `d:${String(item.deviceId)}` : `n:${item.forNodeId}`}|${item.key}|${item.scope ?? "*"}`;
66789
+ }
66790
+ /** Read `items` over `window` (and the previous period when `compare`). */
66791
+ function useHistoryReads(items, window, compare, enabled = true) {
66792
+ const reader = useHistoryReader();
66793
+ const ids = items.map(readItemId);
66794
+ const q = useQuery({
66795
+ queryKey: [
66796
+ "device-history",
66797
+ "reads",
66798
+ ids,
66799
+ window.from,
66800
+ window.to,
66801
+ compare
66802
+ ],
66803
+ queryFn: async () => {
66804
+ const prev = previousWindow(window);
66805
+ const [cur, before] = await Promise.all([readHistory(reader, items, window), compare ? readHistory(reader, items, prev.window) : Promise.resolve(null)]);
66806
+ const toMap = (entries) => new Map(entries.map((e, i) => [ids[i] ?? String(i), e]));
66807
+ return {
66808
+ current: toMap(cur),
66809
+ previous: before === null ? null : toMap(before),
66810
+ shiftMs: prev.shiftMs
66811
+ };
66812
+ },
66813
+ enabled: enabled && items.length > 0,
66814
+ staleTime: 3e4,
66815
+ placeholderData: (previous) => previous
66816
+ });
66817
+ if (q.status === "success") return {
66818
+ status: "ok",
66819
+ data: q.data
66820
+ };
66821
+ if (q.status === "error") return {
66822
+ status: "error",
66823
+ error: q.error.message
66824
+ };
66825
+ return { status: "pending" };
66826
+ }
66827
+ //#endregion
66828
+ //#region src/composites/device-history/use-history-panels.ts
66829
+ /**
66830
+ * Saved history panels and hidden charts, in the signed-in user's ACCOUNT
66831
+ * preferences (D894): one row, namespace `admin.history-panels`, synced to
66832
+ * every device signed in as that user.
66833
+ *
66834
+ * A write is a compare-and-set on the revision it was based on; a conflict
66835
+ * re-applies the same change to the row as it now stands, once. A row this
66836
+ * client cannot read is never overwritten.
66837
+ */
66838
+ var QUERY_KEY = ["preferences", HISTORY_PANELS_NAMESPACE];
66839
+ function useHistoryPanels() {
66840
+ const client = useSystem().trpcClient;
66841
+ const qc = useQueryClient();
66842
+ const q = useQuery({
66843
+ queryKey: QUERY_KEY,
66844
+ queryFn: async () => {
66845
+ const row = (await client.preferences.get.query({ namespaces: [HISTORY_PANELS_NAMESPACE] })).rows.find((x) => x.namespace === HISTORY_PANELS_NAMESPACE);
66846
+ return row === void 0 ? null : {
66847
+ value: row.value,
66848
+ revision: row.revision
66849
+ };
66850
+ },
66851
+ staleTime: 3e4
66852
+ });
66853
+ const row = q.data ?? null;
66854
+ const parsed = readHistoryPanels(row?.value);
66855
+ const update = useCallback(async (change) => {
66856
+ const write = async (base, retry) => {
66857
+ const current = readHistoryPanels(base?.value);
66858
+ if (!current.parsed) throw new Error("history-panels: stored row is not readable");
66859
+ const next = change(current.value);
66860
+ const res = await client.preferences.put.mutate({
66861
+ namespace: HISTORY_PANELS_NAMESPACE,
66862
+ value: { ...next },
66863
+ baseRevision: base?.revision ?? 0
66864
+ });
66865
+ if (res.status === "saved") {
66866
+ qc.setQueryData(QUERY_KEY, {
66867
+ value: res.row.value,
66868
+ revision: res.row.revision
66869
+ });
66870
+ return;
66871
+ }
66872
+ const fresh = res.current === null ? null : {
66873
+ value: res.current.value,
66874
+ revision: res.current.revision
66875
+ };
66876
+ qc.setQueryData(QUERY_KEY, fresh);
66877
+ if (!retry) throw new Error("history-panels: the row changed while saving");
66878
+ await write(fresh, false);
66879
+ };
66880
+ await write(row, true);
66881
+ }, [
66882
+ client,
66883
+ qc,
66884
+ row
66885
+ ]);
66886
+ return {
66887
+ status: q.status === "success" ? "ok" : q.status === "error" ? "error" : "pending",
66888
+ value: q.status === "success" ? parsed.value : EMPTY_HISTORY_PANELS,
66889
+ writable: q.status === "success" && parsed.parsed,
66890
+ update
66891
+ };
66892
+ }
66893
+ //#endregion
66894
+ //#region src/composites/device-history/DeviceHistoryView.tsx
66895
+ /**
66896
+ * DeviceHistoryView — a device's recorded history (device-history, D850):
66897
+ * the device page's History tab, and — with `fixedWindow="24h"` — the last
66898
+ * day on a camera's Live Stats tab. One code path for both.
66899
+ *
66900
+ * Grouping mirrors the State panel: one collapsible section per capability
66901
+ * (catalog order), then per producer addon; inside, one chart per unit and
66902
+ * one timeline per state. Every chart shares one cursor. A chart's ⋯ menu
66903
+ * opens its series on the History page or hides it (remembered in the
66904
+ * user's account preferences).
66905
+ *
66906
+ * Absent and pending are different screens (D315): device-history not
66907
+ * installed, the list not answered yet, and "nothing recorded" each say so.
66908
+ */
66909
+ /** `climate-control` → `Climate control`. */
66910
+ function humanizeName(name) {
66911
+ const words = name.replace(/[-_]/g, " ").trim();
66912
+ return words.length === 0 ? name : words.charAt(0).toUpperCase() + words.slice(1);
66913
+ }
66914
+ function refsOf(deviceId, spec) {
66915
+ return (spec.type === "lines" || spec.type === "occupancy" ? spec.series : [spec.series]).map((s) => ({
66916
+ subject: {
66917
+ kind: "device",
66918
+ deviceId
66919
+ },
66920
+ key: s.key,
66921
+ scope: s.scope
66922
+ }));
66923
+ }
66924
+ function DeviceHistoryView({ deviceId, fixedWindow, onNavigate }) {
66925
+ const t = useUiText();
66926
+ const [windowParam, setWindowParam] = useUrlQueryParam("hw");
66927
+ const [customParam, setCustomParam] = useUrlQueryParam("hr");
66928
+ const [compareParam, setCompareParam] = useUrlQueryParam("hc");
66929
+ const windowId = fixedWindow ?? (isHistoryWindowId(windowParam) ? windowParam : "24h");
66930
+ const custom = fixedWindow ? null : decodeCustomWindow(customParam);
66931
+ const compare = fixedWindow === void 0 && compareParam === "1";
66932
+ const spanGuess = resolveHistoryWindow(windowId, Date.now(), custom);
66933
+ const range = resolveHistoryWindow(windowId, anchorNow(Date.now(), spanGuess.to - spanGuess.from), custom);
66934
+ const { presence, list } = useDeviceSeriesList(deviceId);
66935
+ const panels = useHistoryPanels();
66936
+ const hidden = useMemo(() => new Set(panels.value.hidden[String(deviceId)] ?? []), [panels.value.hidden, deviceId]);
66937
+ const [collapsed, setCollapsed] = useState(/* @__PURE__ */ new Set());
66938
+ const sections = useMemo(() => list.status === "ok" ? groupDeviceSeries(list.data, humanizeName) : [], [list]);
66939
+ const visible = useMemo(() => sections.map((s) => ({
66940
+ ...s,
66941
+ charts: s.charts.filter((c) => !hidden.has(c.id))
66942
+ })).filter((s) => s.charts.length > 0), [sections, hidden]);
66943
+ const hiddenCount = sections.reduce((n, s) => n + s.charts.filter((c) => hidden.has(c.id)).length, 0);
66944
+ const reads = useHistoryReads(useMemo(() => chartKeys(visible.flatMap((s) => s.charts)).map((key) => ({
66945
+ deviceId,
66946
+ key
66947
+ })), [visible, deviceId]), range, compare, list.status === "ok");
66948
+ const readsData = reads.status === "ok" ? reads.data : null;
66949
+ const entryFor = useCallback((key, previous) => {
66950
+ const id = readItemId({
66951
+ deviceId,
66952
+ key
66953
+ });
66954
+ if (reads.status === "error") return {
66955
+ ok: false,
66956
+ error: reads.error
66957
+ };
66958
+ return (previous ? readsData?.previous : readsData?.current)?.get(id);
66959
+ }, [
66960
+ deviceId,
66961
+ reads,
66962
+ readsData
66963
+ ]);
66964
+ const navigate = useCallback((path) => {
66965
+ if (onNavigate) onNavigate(path);
66966
+ else globalThis.location.assign(`${routerBasename()}${path}`);
66967
+ }, [onNavigate]);
66968
+ const setHidden = (chartId, hide) => {
66969
+ if (!panels.writable) return;
66970
+ panels.update((v) => withHiddenChart(v, deviceId, chartId, hide));
66971
+ };
66972
+ const showAllHidden = () => {
66973
+ if (!panels.writable) return;
66974
+ panels.update((v) => {
66975
+ let next = v;
66976
+ for (const id of hidden) next = withHiddenChart(next, deviceId, id, false);
66977
+ return next;
66978
+ });
66979
+ };
66980
+ if (presence === "pending") return /* @__PURE__ */ jsx(HistorySkeleton, {});
66981
+ if (presence === "unknown") return /* @__PURE__ */ jsx(ErrorBox, {
66982
+ title: t("uiLibrary.history.presenceUnknownTitle", "Could not reach the hub"),
66983
+ message: t("uiLibrary.history.presenceUnknown", "The hub did not say whether device history is installed. Try again in a moment.")
66984
+ });
66985
+ if (presence === "absent") return /* @__PURE__ */ jsx(EmptyState, {
66986
+ icon: History,
66987
+ title: t("uiLibrary.history.absentTitle", "Device history is not running on this hub"),
66988
+ description: t("uiLibrary.history.absent", "Nothing is recorded until the device-history system addon is enabled.")
66989
+ });
66990
+ if (list.status === "pending") return /* @__PURE__ */ jsx(HistorySkeleton, {});
66991
+ if (list.status === "error") return /* @__PURE__ */ jsx(ErrorBox, {
66992
+ title: t("uiLibrary.history.listFailedTitle", "History could not be read"),
66993
+ message: list.error
66994
+ });
66995
+ if (sections.length === 0) return /* @__PURE__ */ jsx(EmptyState, {
66996
+ icon: ChartLine,
66997
+ title: t("uiLibrary.history.emptyTitle", "Nothing recorded for this device yet"),
66998
+ description: t("uiLibrary.history.empty", "A value appears here once it changes, or at the next five-minute check.")
66999
+ });
67000
+ const syncKey = `device-history:${String(deviceId)}`;
67001
+ return /* @__PURE__ */ jsxs("div", {
67002
+ className: STACK_GAP,
67003
+ children: [fixedWindow === void 0 && /* @__PURE__ */ jsxs("div", {
67004
+ className: "flex flex-wrap items-center justify-between gap-2",
67005
+ children: [/* @__PURE__ */ jsx(HistoryWindowBar, {
67006
+ value: windowId,
67007
+ onChange: (id) => setWindowParam(id === "24h" ? null : id),
67008
+ custom,
67009
+ onCustomChange: (w) => setCustomParam(encodeCustomWindow(w)),
67010
+ compare,
67011
+ onCompareChange: (on) => setCompareParam(on ? "1" : null)
67012
+ }), hiddenCount > 0 && /* @__PURE__ */ jsxs(Button, {
67013
+ variant: "ghost",
67014
+ size: "sm",
67015
+ onClick: showAllHidden,
67016
+ disabled: !panels.writable,
67017
+ children: [/* @__PURE__ */ jsx(EyeOff, {
67018
+ className: "h-3.5 w-3.5",
67019
+ "aria-hidden": "true"
67020
+ }), t("uiLibrary.history.showHidden", {
67021
+ one: "Show {{count}} hidden chart",
67022
+ other: "Show {{count}} hidden charts"
67023
+ }, { count: hiddenCount })]
67024
+ })]
67025
+ }), visible.map((section) => {
67026
+ return /* @__PURE__ */ jsx(CollapsibleCard, {
67027
+ expanded: !collapsed.has(section.id),
67028
+ onExpandedChange: (next) => setCollapsed((prev) => {
67029
+ const s = new Set(prev);
67030
+ if (next) s.delete(section.id);
67031
+ else s.add(section.id);
67032
+ return s;
67033
+ }),
67034
+ header: /* @__PURE__ */ jsxs("div", {
67035
+ className: "flex items-center gap-2 min-w-0",
67036
+ children: [/* @__PURE__ */ jsx("span", {
67037
+ className: "text-xs font-semibold text-foreground truncate",
67038
+ children: humanizeName(section.name)
67039
+ }), /* @__PURE__ */ jsx("span", {
67040
+ className: "text-[10px] text-foreground-subtle shrink-0",
67041
+ children: t("uiLibrary.history.chartCount", {
67042
+ one: "{{count}} chart",
67043
+ other: "{{count}} charts"
67044
+ }, { count: section.charts.length })
67045
+ })]
67046
+ }),
67047
+ children: /* @__PURE__ */ jsx("div", {
67048
+ className: "grid grid-cols-1 gap-4 2xl:grid-cols-2",
67049
+ children: section.charts.map((spec) => /* @__PURE__ */ jsx(HistoryChart, {
67050
+ spec,
67051
+ entryFor,
67052
+ reads: readsData,
67053
+ window: range,
67054
+ compare,
67055
+ syncKey,
67056
+ actions: /* @__PURE__ */ jsx(KebabMenu, {
67057
+ title: t("uiLibrary.history.chartMenu", "Chart actions"),
67058
+ items: [{
67059
+ id: "open",
67060
+ label: t("uiLibrary.history.openInHistory", "Open in History"),
67061
+ icon: ChartLine,
67062
+ onClick: () => navigate(historyPageHref(refsOf(deviceId, spec), windowId))
67063
+ }, {
67064
+ id: "hide",
67065
+ label: t("uiLibrary.history.hide", "Hide"),
67066
+ icon: EyeOff,
67067
+ hidden: fixedWindow !== void 0,
67068
+ disabled: !panels.writable,
67069
+ onClick: () => setHidden(spec.id, true)
67070
+ }]
67071
+ })
67072
+ }, spec.id))
67073
+ })
67074
+ }, section.id);
67075
+ })]
67076
+ });
67077
+ }
67078
+ function HistorySkeleton() {
67079
+ return /* @__PURE__ */ jsxs("div", {
67080
+ className: STACK_GAP,
67081
+ "aria-busy": "true",
67082
+ children: [
67083
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-72" }),
67084
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-40 w-full" }),
67085
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-40 w-full" })
67086
+ ]
67087
+ });
67088
+ }
67089
+ //#endregion
63789
67090
  //#region src/contexts/zone-editing.tsx
63790
67091
  /**
63791
67092
  * Per-device zone-editing state shared between the player overlay
@@ -64614,12 +67915,12 @@ function mirror(input, t = defaultUiText) {
64614
67915
  * Render inside `<Suspense>` at all call sites. */
64615
67916
  var MaskShapeCanvas = lazy(() => import("./MaskShapeCanvas-C9cJlxvG.js").then((m) => ({ default: m.MaskShapeCanvas })));
64616
67917
  /** Lazy-wrapped `MotionZonesSettings` — code-split off the main bundle. */
64617
- var MotionZonesSettings = lazy(() => import("./MotionZonesSettings-C3Tj12yc.js").then((m) => ({ default: m.MotionZonesSettings })));
67918
+ var MotionZonesSettings = lazy(() => import("./MotionZonesSettings-DR1g9ja3.js").then((m) => ({ default: m.MotionZonesSettings })));
64618
67919
  /** Lazy-wrapped `SceneMonitorEditor` — code-split off the main bundle for the
64619
67920
  * same reason: it mounts `MaskShapeCanvas`, which chains react-konva. Render
64620
67921
  * inside `<Suspense>`; the admin-ui `/scenes` dialog is its only caller. */
64621
- var SceneMonitorEditor = lazy(() => import("./SceneMonitorEditor-30H29vN4.js").then((m) => ({ default: m.SceneMonitorEditor })));
67922
+ var SceneMonitorEditor = lazy(() => import("./SceneMonitorEditor-BQhVO2F2.js").then((m) => ({ default: m.SceneMonitorEditor })));
64622
67923
  /** Lazy-wrapped `PrivacyMaskSettings` — code-split off the main bundle. */
64623
- var PrivacyMaskSettings = lazy(() => import("./PrivacyMaskSettings-C9wfHkiB.js").then((m) => ({ default: m.PrivacyMaskSettings })));
67924
+ var PrivacyMaskSettings = lazy(() => import("./PrivacyMaskSettings-CtetT6Jl.js").then((m) => ({ default: m.PrivacyMaskSettings })));
64624
67925
  //#endregion
64625
- export { ShieldOff as $, Clock as $n, LoaderCircle as $t, TrainFront as A, AddProviderEntryDialog, AddonGlobalSettingsForm, AdminLinkQuickAction, AgentStepEditor, AlarmHeroCard, AlarmInlineControl as AlarmPanelInlineControl, Feather as An, AppShell, Bell as Ar, ArcKnob, Pause as At, AudioClassificationList, AudioLevelWaveform, AudioWaveform, AutomationHeroCard, AutomationInlineControl, AutotrackSection, SunMedium as B, BTN_COMPACT, BTN_COMPACT_DANGER, BTN_COMPACT_PRIMARY, BTN_COMPACT_WARNING, BackToStepsButton, Badge, BatteryBadge, DoorClosed as Bn, BottomSheet, ArrowUp as Br, Breadcrumb, BrightnessPanel, MonitorPlay as Bt, Button, ButtonControl, ButtonHeroCard, Undo2 as C, CENTER, CHIP_ACTIVE, CHIP_BASE, CHIP_INACTIVE, CLASS_COLORS, COLUMN_BREAKPOINT_CLASS, COLUMN_PRIORITY, COMMIT_DEDUPE_TOLERANCE_MS, COMMIT_DEDUPE_WINDOW_MS, CONTROL_CAP_NAMES, CONTROL_FILLS, CameraStreamPlayer, Card, Checkbox, ChildSectionAccordion, ClimatePanel, Funnel as Cn, CodeBlock, CollapsibleCard, ConfigFormBuilder, FormField as ConfigFormField, ConfigNotificationTargetMultiSelectFieldView, ConfigNotificationTargetSelectFieldView, ConfigSchemaField, ConfirmActionButton, ConfirmDialogProvider, ConsumablesPanel, ContainerChildrenProvider, ContainerPrimaryHero, ControlColumn, ControlHeroCard, ControlInlineControl, ControlPanel, CopyButton, CoverHeroCard, CoverInlineControl, CoverPanel, Bug as Cr, Play as Ct, CustomFieldRenderersProvider, TrendingDown as D, DATA_RECORDS_KIND_PARAM, DATA_RECORDS_OWNER_PARAM, DATA_RECORD_FACET_GROUPS, DEFAULT_COLOR, DEFAULT_PAGINATION_LABELS, DEVICE_COLUMNS, DEVICE_EXPORT_PAGE_SIZE_KEY, DEVICE_LIST_PAGE_SIZE_KEY, DEVICE_LIST_PAGE_SIZE_OPTIONS, DEVICE_PICKER_GROUP_STORAGE_KEY, DEVICE_ROLE_META, DEVICE_TYPE_CONTROL, DEVICE_TYPE_META, DataRecordsHub, DataTable, DataTableFilterBar, DataTablePager, DetectionCanvas, DetectionOverlay, DetectionResultTree, DevShell, DeviceActivityPanel, DeviceBatchToolbar, DeviceCard, DeviceContextProvider, DeviceExportPanel, DeviceExportSetup, DeviceFilterBar, DeviceGrid, DeviceItem, DeviceList, DeviceMultiSelectField, DevicePicker, DeviceQuickDetailProvider, DeviceSelectField, DeviceSelectorPicker, DeviceStepMatrix, DevicesFailedLine, DevicesLoadingLine, DevicesWidenWarning, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DiscoveryPanel, FlashlightOff as Dn, DoorbellRecentPanel, Blinds as Dr, Dropdown, DropdownContent, DropdownItem, DropdownTrigger, Phone as Dt, DummyHeroCard, DummyInline, TriangleAlert as E, EMPTY_DATA_TABLE_FILTER, EVENT_KIND_ICONS, EmptyState, Flashlight as En, EndpointResultsCard, EndpointTestCard, EntryJsonSchemaForm, Bone as Er, ErrorBox, Pin as Et, EventKindGlyph, EventStream, ExtensionsPanel, Thermometer as F, FAST_ICON_GLYPHS, FILL, FLEET_LIST_GC_TIME_MS, FLEET_LIST_QUERY_KEY, FLEET_LIST_STALE_TIME_MS, FanHeroCard, FanInlineControl, FanPanel, FilterBar, FloatingEventStream, FloatingLogStream, FloatingPanel, EllipsisVertical as Fn, FormField$1 as FormField, BatteryLow as Fr, Move as Ft, Snowflake as G, GRID_GAP, GRID_PAIRED, GRID_QUICK_STATS, GateBadge, GateControl, Copy as Gn, ArrowDownToLine as Gr, GripTrack, GroupedModelSelector, GroupedTilePicker, Menu as Gt, Square as H, HOST_WIDGETS, HUB_SESSION_RUN_MS, HlsVideo, Database as Hn, HoverZoomImage, ArrowRight as Hr, Minimize2 as Ht, HubSessionTestCard, HumidifierHeroCard, HumidifierInlineControl, Target as I, INPUT_COMPACT, INPUT_SHELL, IconAction, IconButton, IconPicker, ImageHeroCard, ImageInlineControl, ImageSelector, Droplets as In, InferenceConfigSelector, Input, BatteryFull as Ir, MoveVertical as It, ItemFormDialog, SkipForward as J, CloudSun as Jn, Activity as Jr, MapPin as Jt, Smartphone as K, KebabMenu, KeyValueList, Cog as Kn, AlarmSmoke as Kr, Megaphone as Kt, Tag as L, LIST_ROW, Label, LawnMowerHeroCard, LawnMowerInlineControl, LayeredSettingsForm, LightHeroCard, LightInlineControl, Droplet as Ln, LockHeroCard, LockInlineControl, LockPanel, LogStream, LoginForm, Bath as Lr, MoveHorizontal as Lt, ToggleRight as M, MEDIA_GRID, MODE_COLOR, MaskShapeCanvas, MediaCard, MediaGrid, MediaPlayerHeroCard, MediaPlayerInlineControl, MediaPlayerPanel, Eye as Mn, MobileDrawer, ModelPicker, MotionZonesSettings, Battery as Mr, Network as Mt, ToggleLeft as N, NAVIGATION_DRIVE_INTERVAL_MS, NODE_COLOR_IDS, NatResultCard, NavigationPanel, NetworkLinkBadge, EyeOff as Nn, NodeMultiSelectField, NodePicker, NodeSelectField, NotificationTargetSelectField, NotificationTargetsField, BatteryWarning as Nr, Navigation as Nt, TrendingUp as O, OPTION_SELECT_SEARCH_MIN_OPTIONS, OfflineBadge, Flame as On, OptionSelect, Bird as Or, Pencil as Ot, Timer as P, PAGE_SIZE_ALL, PHASE_CONFIG, PICKER_CHIP_LIMIT, PICKER_DEFAULT_GROUP_BY, PICKER_GROUP_BY_MODES, PICKER_GROUP_COLLAPSE_AT, PICKER_NONE_KEY, PICKER_NOUN_PLURAL, PICKER_NO_FILTERS, PICKER_PAGE_SIZE, PICKER_SEARCH_DEBOUNCE_MS, PICKER_WINDOW, PRIORITY, PTZOverlay, PageHeader, PageShell, PageSizeSelect, Pagination, PasswordInput, PhaseIcon, PipelineBuilder, PipelineRuntimeSelector, PipelineStep, PlaceholderChipField, PlayerOverlaysProvider, ExternalLink as Pn, Popover, PopoverContent, PopoverRowAction, PopoverTrigger, BatteryMedium as Pr, PrimaryChildPicker, PrivacyMaskSettings, ProviderBadge, ProviderEntryPills, Music as Pt, PtzPanel, Shield as Q, CloudFog as Qn, QrCode, LocateFixed as Qt, Sunrise as R, RECONNECT_POLICY, RECORDED_PLAYBACK_MODES, RIGHT, ROI_AREA_WARN_FRACTION, ROLE_DESCRIPTOR, ROOT_DETECTOR_STEP_ID, RadialGauge, RecordEntriesPanel, RecordedPlaybackProvider, RecordingPanel, ResponseLog, RetrainReviewBadge, Download as Rn, RoleDisplayDefaultsFromServer, RoleDisplayDefaultsProvider, RoleNodePill, RootModelSourceControl, RootSuppliedModelNote, Ban as Rr, MousePointerClick as Rt, RunReport, Unlink2 as S, SCENE_CAPTURE_VARIANTS, SECRET_MASK_PLACEHOLDER, SECRET_REVEAL_MS, SECTION_BODY, SECTION_CARD, SECTION_HEADER, SETTING_ROW, SETTING_ROW_LABEL, SETTING_ROW_STACK_BREAKPOINT, SETTING_ROW_VALUE, SETTING_ROW_VALUE_TEXT, SNAPSHOT_MEDIA_PATH, SPLIT_PANEL_OUTER, SPLIT_PANEL_SIDE, STACK_GAP, STATE_COLOR, STEP_DOCK_WIDTH_PX, SceneMonitorEditor, ScopePicker, ScriptHeroCard, ScriptInlineControl, ScrollArea, SecretFieldControl, SecretRevealProvider, SectionCard, SectionToolbar, SectionedPage, SegmentedChoice, Select, SelectedChip, SemanticBadge, SendFrameToTrainingButton, SensorHeroCard, SensorInlineControl, SensorValueAtom, Separator, SettingRow, Sidebar, SidebarItem, Skeleton, SlideToggle, Gamepad2 as Sn, SnapshotButton, SortHeaderButton, Bus as Sr, Plus as St, StableTabPanel, StatCard, StateValuesStream, StatusBadge, StepDetailDock, StepInspectorPanel, StepModelRow, StepTimings, Stepper, StorageRecordsPanel, StreamBrokerSelector, StreamDebugOverlay, StreamPanel, Switch, SwitchHeroCard, SwitchInlineControl, SwitchPanel, SystemProvider, Truck as T, TABLE_SCROLL_MAX_H, TEXT_FIELD_LABEL, TEXT_HINT, TEXT_METRIC, TEXT_SECTION_LABEL, TEXT_VALUE, TIMEZONES, Tabs, TabsContent, TabsList, TabsTrigger, TapToggle, ThemeProvider, ThermostatHeroCard, ThermostatInlineControl, TimezoneSelector, FlaskConical as Tn, Tooltip, TooltipContent, TooltipTrigger, Bot as Tr, TrainingBaseModelPicker, TrainingCollectionPicker, Pipette as Tt, Sprout as U, UiTextProvider, UiTrans, Crosshair as Un, UnitPicker, ArrowLeft as Ur, Mic as Ut, Star as V, VacuumHeroCard, VacuumInlineControl, ValueReadout, ValveHeroCard, ValveInlineControl, VersionBadge, Dog as Vn, VodPlaybackProvider, ArrowUpToLine as Vr, Minus as Vt, Sparkles as W, WaterHeaterHeroCard, WaterHeaterInlineControl, WeatherHeroCard, WeatherInlineControl, WidgetMetricCard, WidgetPanel, WidgetRegistryProvider, WidgetSlot, Cpu as Wn, ArrowDown as Wr, MessageSquare as Wt, Siren as X, CloudRain as Xn, Lock as Xt, SkipBack as Y, CloudSnow as Yn, Mail as Yt, Ship as Z, CloudLightning as Zn, ZoneEditingProvider, LockOpen as Zt, UtensilsCrossed as _, Hammer as _n, Cat as _r, QrCode$1 as _t, Wrench as a, acceleratorLabel, acceleratorShortLabel, agentColumnKey, allDeviceTypeFilterOptions, LayoutGrid as an, applyFleetQueryDefaults, applyPickerFilters, CirclePause as ar, ariaSortForColumn, assignNodeColorId, Search as at, UserCheck as b, badgeFor, bindsToModel, GraduationCap as bn, Camera as br, PowerOff as bt, buildColorByNodeId, buildDataRecordRows, buildPeerConnectionConfig, buildStepTreeFromSchema, Wifi as c, catalogHasDraft, childEntityId, childListName, chipOverflow, classifyAddressScope, classifyCapability, classifySessionVerdict, clearPickerGroup, clusterRootModelIds, cn, colorIdForNode, columnsForContext, compareSrflxMappings, computePageButtons, containerChildToRef, countableDevices, coverHighlight, coverageLine, CircleCheckBig as cr, createAddonI18n, createSharedContext, createTheme, Save as ct, currentAddonLanguage, cursorFractionFor, WifiLow as d, darkColors, dataRecordContents, dataRecordKey, dataRecordSearchText, dataRecordType, dataTableFacetGroups, dataTableFacets, defaultOfField, defaultSelectClusterRoots, defaultSingleClusterRoot, defaultTheme, defaultUiText, deriveDeviceKind, deriveWebrtcDebugStats, describeDeviceSelector, deviceIntegrationResolver, deviceListStateOf, deviceMatchesFilter, deviceOptionLabel, deviceRoleMeta, deviceRoleMetaOf, deviceTypeLabel, deviceTypeMeta, deviceTypeMetaOf, devicesToOptions, ImageOff as dn, dockBox, ChevronsDownUp as dr, draftFromQuery, RotateCw as dt, effectivePageSize, emptyDevicesWidens, emptySessionRun, List as en, ensureMfHostInit, Clapperboard as er, ShieldCheck as et, eventKindLabel, WifiHigh as f, filterDataTableRows, filterDeviceOptions, filterOptions as filterOptionSelectOptions, filterPickerDevices, finalizeSessionRun, findTimezone, flattenPickerGrouping, flattenPickerRows, fleetViewOf, House as fn, formatControlDateTime, formatLastSeen, formatNumeric, formatSettingValue, ChevronUp as fr, freezeVerdict, RotateCcw as ft, fuzzyMatch, Vibrate as g, getClassColor, getPhaseVisual, Hand as gn, Check as gr, groupAgentColumns, groupChildrenByLayout, groupPickerDevices, groupPickerDevicesBy, Radar as gt, Volume2 as h, hardwareLabel, HardDrive as hn, ChevronDown as hr, RadioTower as ht, hubOrigin, hubPath, hubUrl, hubWsUrl, humanDuration, humidifierTint, X as i, iconGlyphsFailed, iconGlyphsLoaded, Leaf as in, initialScrubState, insertAtSelection, CirclePlus as ir, isAbsentProvider, isDataTableFilterActive, isFieldVisible, isGroupExpanded, isKeychainFieldValue, isRedactedSecret, isSecretField, isValueField, Server as it, Tractor as j, Fan as jn, BellRing as jr, Package as jt, Trash2 as k, Film as kn, Bike as kr, PawPrint as kt, WifiZero as l, lawnMowerActivityMeta, lightColors, Inbox as ln, loadIconGlyphs, loadRemoteBundle, CircleAlert as lr, Sailboat as lt, lucideExportName, VolumeX as m, makeScrubBridge, mapRootDetectorPlacements, mergeSecretForSubmit, metadataEntries, metadataString, mirror, Hash as mn, mountAddonPage, mountPath, ChevronLeft as mr, Radio as mt, ZoomIn as n, nextGridPosition, nextReconnectAction, nextSort, nextSortDirection, nextTableSort, Link2 as nn, nodeColorClass, nodeSoftBgClass, nodeTextColorClass, normalizeForSearch, normalizeLanguage, CircleStop as nr, Share2 as nt, Workflow as o, offListSelections, Laugh as on, openTopLayer, optionSearchText as optionSelectSearchText, CircleDot as or, orphanDeviceLabel, ScrollText as ot, overrideEntityIdFromLink, Waves as p, pageRows, pageSizeLabel, paginateRows, parentDetailOf, parseIceCandidate, parsePickerGroupBy, parseRecordedServerMessage, partitionSelected as partitionOptionSelectSelected, partitionSuggested, pickerFacetFilterShown, pickerFacetKey, pickerFacetOptions, pickerFiltersActive, pickerGroupKey, pickerHiddenSelectedCount, pickerLocationKey, pickerQueryTokens, pickerRowFacts, pickerSearchText, pickerZoneFilterSuspended, pinnedStreamChoices, placePopover, Heater as pn, ChevronRight as pr, providerIcons, RefreshCw as pt, SlidersHorizontal as q, Cloud as qn, AirVent as qr, Maximize2 as qt, Zap as r, readStoredGroupBy, reduceSessionRun, registerAddonBundle, renderMarkup, renderTagged, resetHubMountForTests, resetPickerFilters, resolveContainerPrimary, resolveControlAlign, resolveDeviceControl, resolveDeviceIntegration, resolveDisplayIcon, resolveEffectiveDefaultModel, resolveEffectiveStepModel, resolveEventKindIcon, resolveSearchable as resolveOptionSelectSearchable, resolveParentNames, resolvePrimaryChild, resolveSelectedDevices, resolveSensorDisplay, resolveStepDefaultModel, retrainReviewBadgeLook, Lightbulb as rn, roiTooLarge, rootSourceState, routerBasename, CircleQuestionMark as rr, Settings as rt, Wind as s, sameLocationSuggestions, scrubReducer, searchSteps, secretDisplayValue, secretSubmitValue, selectPickerGroup, selectedDeviceOptions, serializeRecordedCommand, settingsModelIdFor, shouldCommit, shouldEmit, shouldShowAddNewFooter, shouldShowUseQueryRow, shouldUseSingleNode, showsNoCamerasHint, Info as sn, sortRows, sortRowsByColumn, CircleCheck as sr, Scan as st, statusIcons, statusLine, stepFieldDomId, stepHasModelForFormat, stepLocalFields, stepModelOptions, stripParentNamePrefix, summarizeHubCandidates, ZoomOut as t, tabForSelector, tankAlert, themeToCss, themeToRootCss, ListChecks as tn, toStationaryOverlayBox, toggleDataTableFacet, toggleDataTableGroupFacet, togglePickerDevice, togglePickerKey, CircleX as tr, trainingRefusalText, translateAddonKey, trpc, ShieldAlert as tt, typeLabel, WifiOff as u, Image as un, ChevronsUpDown as ur, useAccessoriesGetStatus, useAccessoriesSetChildHidden, useAddonPagesListPages, useAddonSettingsGetDeviceSettings, useAddonSettingsGetGlobalSettings, useAddonSettingsGetIntegrationSettings, useAddonSettingsUpdateDeviceSettings, useAddonSettingsUpdateGlobalSettings, useAddonT, useAddonWidgetsListWidgets, useAddonsApplyAutoUpdateToAll, useAddonsCancelJob, useAddonsCustom, useAddonsForceRefresh, useAddonsGetAddonAutoUpdate, useAddonsGetAutoUpdateSettings, useAddonsGetJob, useAddonsGetLastRestart, useAddonsGetLogs, useAddonsGetVersions, useAddonsInstallFromWorkspace, useAddonsInstallPackage, useAddonsIsWorkspaceAvailable, useAddonsList, useAddonsListCapabilityProviders, useAddonsListFrameworkPackages, useAddonsListInstallations, useAddonsListJobs, useAddonsListPackages, useAddonsListUpdates, useAddonsListWorkspacePackages, useAddonsOnAddonLogs, useAddonsReloadPackages, useAddonsRestartAddon, useAddonsRestartServer, useAddonsRetryLoad, useAddonsRollbackPackage, useAddonsSearchAvailable, useAddonsSetAddonAutoUpdate, useAddonsSetAutoUpdateSettings, useAddonsSetCapabilityProviderEnabled, useAddonsStartJob, useAddonsUninstallPackage, useAddonsUpdatePackage, useAirQualitySensorGetStatus, useAlarmPanelArm, useAlarmPanelDisarm, useAlarmPanelGetStatus, useAlarmPanelTrigger, useAlert, useAlertsDismiss, useAlertsEmit, useAlertsGetUnreadCount, useAlertsList, useAlertsMarkAllRead, useAlertsMarkRead, useAlertsUpdate, useAllWidgets, useAmbientLightSensorGetStatus, useAudioAnalysisApplyDeviceSettingsPatch, useAudioAnalysisGetDeviceLiveContribution, useAudioAnalysisGetDeviceSettingsContribution, useAudioAnalysisResolveDeviceSettings, useAudioAnalyzerAnalyseChunk, useAudioAnalyzerAttachDevice, useAudioAnalyzerClassify, useAudioAnalyzerDetachDevice, useAudioAnalyzerDispose, useAudioAnalyzerIsReady, useAudioAnalyzerReprobeAudioEngine, useAudioCodecCanHandle, useAudioCodecCloseSession, useAudioCodecCreateDecodeSession, useAudioCodecCreateEncodeSession, useAudioCodecFlushEncode, useAudioCodecListActiveSessions, useAudioCodecListSupportedCodecs, useAudioCodecPullEncoded, useAudioCodecPullPcm, useAudioCodecPushEncodedFrame, useAudioCodecPushPcm, useAudioMetricsGetCurrentSnapshot, useAudioMetricsGetHistory, useAutomationControlDisable, useAutomationControlEnable, useAutomationControlGetStatus, useAutomationControlTrigger, useBackupCancel, useBackupCheckBackupKey, useBackupDelete, useBackupDeleteSchedule, useBackupDisableEncryption, useBackupGetEncryption, useBackupGetEntries, useBackupList, useBackupListArchives, useBackupListAvailableDatabases, useBackupListDestinations, useBackupListLocations, useBackupListRuns, useBackupListSchedules, useBackupPreviewSchedule, useBackupRestore, useBackupSetupEncryption, useBackupTrigger, useBackupUpsertDestinationPolicy, useBackupUpsertSchedule, useBatteryGetStatus, useBatteryWakeForStream, useBinaryGetStatus, useBrightnessGetStatus, useBrightnessSetBrightness, useBrokerAdd, useBrokerGet, useBrokerGetBrokerConfig, useBrokerGetSettings, useBrokerGetSettingsSchema, useBrokerGetState, useBrokerGetStatus, useBrokerList, useBrokerListProviders, useBrokerPublish, useBrokerRemove, useBrokerSetSettings, useBrokerSubscribe, useBrokerTestConnection, useBrokerTestSettings, useBrokerUnsubscribe, useButtonPress, useCalendarsGetPreferences, useCalendarsListCalendars, useCalendarsListEvents, useCalendarsSetCalendarPreference, useCameraCredentialsGetCredentials, useCameraCredentialsGetStatus, useCameraGridLayoutApplyDeviceSettingsPatch, useCameraGridLayoutGetDeviceLiveContribution, useCameraGridLayoutGetDeviceSettingsContribution, useCameraGridLayoutGetLayout, useCameraGridLayoutSaveLayout, useCameraPipelineConfigApplyDeviceSettingsPatch, useCameraPipelineConfigGetDeviceLiveContribution, useCameraPipelineConfigGetDeviceSettingsContribution, useCameraStreamsGetBrokerStreams, useCameraStreamsGetCameraStreams, useCameraStreamsGetProfileRtspEntries, useCameraStreamsGetRtspEntries, useCameraStreamsPickStream, useCapabilityPresence, useCarbonMonoxideGetStatus, useClimateControlGetStatus, useClimateControlSetFanMode, useClimateControlSetMode, useClimateControlSetPreset, useClimateControlSetSwingHorizontal, useClimateControlSetSwingVertical, useClimateControlSetTarget, useClimateControlSetTargetHumidity, useClimateControlSetTargetRange, useClusterNodes, useClusterTopology, useColorGetStatus, useColorSetColor, useConfirm, useConnectionTestDescribeTest, useConnectionTestTestSettings, useConnectivityGetStatus, useConsumablesGetStatus, useConsumablesReset, useContactGetStatus, useContainerChildren, useControlGetStatus, useControlSetValue, useCoreBlocksCompile, useCoreBlocksCreate, useCoreBlocksCreateComposition, useCoreBlocksDelete, useCoreBlocksGet, useCoreBlocksGetBlockSettings, useCoreBlocksGetComposition, useCoreBlocksGetCustomization, useCoreBlocksGetTypeDefs, useCoreBlocksList, useCoreBlocksListComposableCaps, useCoreBlocksListCompositions, useCoreBlocksMigrateRawSecrets, useCoreBlocksRestart, useCoreBlocksSetEnabled, useCoreBlocksUpdate, useCoreBlocksUpdateBlockSettings, useCoreBlocksUpdateComposition, useCoreBlocksValidateComposition, useCoverClose, useCoverGetStatus, useCoverOpen, useCoverSetPosition, useCoverSetTiltPosition, useCoverStop, useCustomFieldRenderer, useDataTableFilter, useDayNightGetOptions, useDayNightGetStatus, useDayNightSetSettings, useDebouncedString, useDecoderGetInfo, useDecoderReprobeHwaccel, useDetectionPipelineApplyDeviceSettingsPatch, useDetectionPipelineGetDeviceLiveContribution, useDetectionPipelineGetDeviceSettingsContribution, useDevShell, useDevice, useDeviceAdminLink, useDeviceAdminLinkGetAdminLink, useDeviceAdoptionAdopt, useDeviceAdoptionGetCandidate, useDeviceAdoptionGetStatus, useDeviceAdoptionListCandidateFilters, useDeviceAdoptionListCandidates, useDeviceAdoptionRefresh, useDeviceAdoptionRelease, useDeviceAdoptionResync, useDeviceAutotrack, useDeviceBattery, useDeviceCapSlice, useDeviceCapability, useDeviceCredentialsDeleteDeviceCredential, useDeviceCredentialsGetDeviceCaStatus, useDeviceCredentialsGetDeviceCertificateSettings, useDeviceCredentialsListDeviceCredentials, useDeviceCredentialsRenameDeviceCredential, useDeviceCredentialsRevokeDeviceCredential, useDeviceCredentialsSetDeviceCertificateSettings, useDeviceCredentialsStartEnrollment, useDeviceDetections, useDeviceDiscoveryAdoptDevice, useDeviceDiscoveryGetStatus, useDeviceDiscoveryListDiscovered, useDeviceDiscoveryRefreshDiscovery, useDeviceDiscoveryReleaseDevice, useDeviceExportApplyDeviceSettingsPatch, useDeviceExportExposeDevice, useDeviceExportGetDeviceLiveContribution, useDeviceExportGetDeviceSettingsContribution, useDeviceExportGetStatus, useDeviceExportListExposedDevices, useDeviceExportListSupportedDeviceKinds, useDeviceExportUnexposeDevice, useDeviceExtensionListCandidates, useDeviceExtensionListSlots, useDeviceExtensionListSources, useDeviceExtensionSetSlot, useDeviceHistoryDeclareSeries, useDeviceHistoryGetDeviceEnabled, useDeviceHistoryGetStatus, useDeviceHistoryListNodeSeries, useDeviceHistoryListSeries, useDeviceHistoryQuery, useDeviceHistoryQueryNode, useDeviceHistoryRecord, useDeviceHistoryRecordBuckets, useDeviceHistorySetDeviceEnabled, useDeviceId, useDeviceListPageSize, useDeviceManagerAddLocation, useDeviceManagerAdoptDevice, useDeviceManagerAdoptionAdopt, useDeviceManagerAdoptionCancelJob, useDeviceManagerAdoptionListCandidateFilters, useDeviceManagerAdoptionListCandidates, useDeviceManagerAdoptionListJobs, useDeviceManagerAdoptionRefresh, useDeviceManagerAdoptionRelease, useDeviceManagerAdoptionResync, useDeviceManagerAdoptionStartJob, useDeviceManagerAllocateDeviceId, useDeviceManagerApplyInitialMeta, useDeviceManagerCleanupDeletedDevicesData, useDeviceManagerCreateDevice, useDeviceManagerDisable, useDeviceManagerDiscoverAllProviders, useDeviceManagerDiscoverDevices, useDeviceManagerDiscoverProvider, useDeviceManagerDiscoveryProviders, useDeviceManagerEnable, useDeviceManagerGetAllBindings, useDeviceManagerGetBindings, useDeviceManagerGetBindingsBatch, useDeviceManagerGetChildren, useDeviceManagerGetChildrenBatch, useDeviceManagerGetConfigSchema, useDeviceManagerGetCreationSchema, useDeviceManagerGetDevice, useDeviceManagerGetDeviceAggregate, useDeviceManagerGetDeviceLiveInfoAggregate, useDeviceManagerGetDeviceSettingsAggregate, useDeviceManagerGetDeviceStatusAggregate, useDeviceManagerGetDeviceStatusAggregateBatch, useDeviceManagerGetLinkedDevices, useDeviceManagerGetLinkedDevicesBatch, useDeviceManagerGetRoleDisplayDefaults, useDeviceManagerGetSettingsSchema, useDeviceManagerGetStreamProfileMap, useDeviceManagerGetStreamSources, useDeviceManagerGetWireableFields, useDeviceManagerGetWrapperActivation, useDeviceManagerListAll, useDeviceManagerListBindableCapsForDeviceType, useDeviceManagerListDeletedSince, useDeviceManagerListDeviceIdsByCaps, useDeviceManagerListLocationDescriptions, useDeviceManagerListLocationIcons, useDeviceManagerListLocations, useDeviceManagerListPersistedByAddon, useDeviceManagerListSiteZones, useDeviceManagerListWrappersForCap, useDeviceManagerLoadConfig, useDeviceManagerLoadMeta, useDeviceManagerLoadRuntimeState, useDeviceManagerMigrateDevice, useDeviceManagerPersistConfig, useDeviceManagerProbeStreams, useDeviceManagerProviderCreationType, useDeviceManagerProviderDiscoveryParamsSchema, useDeviceManagerRegisterDevice, useDeviceManagerRemove, useDeviceManagerRemoveByIntegration, useDeviceManagerRemoveDevice, useDeviceManagerRemoveLocation, useDeviceManagerRemoveSiteZone, useDeviceManagerRenameLocation, useDeviceManagerRenameSiteZone, useDeviceManagerRunDeviceAction, useDeviceManagerSetChildLayout, useDeviceManagerSetDisabled, useDeviceManagerSetDisplay, useDeviceManagerSetIntegrationId, useDeviceManagerSetLinkDeviceId, useDeviceManagerSetLocation, useDeviceManagerSetLocationDescription, useDeviceManagerSetLocationIcon, useDeviceManagerSetLocationZone, useDeviceManagerSetMetadata, useDeviceManagerSetName, useDeviceManagerSetPrimaryChildEntityId, useDeviceManagerSetRole, useDeviceManagerSetRoleDisplayDefaults, useDeviceManagerSetSiteZone, useDeviceManagerSetStreamProfileMap, useDeviceManagerSetType, useDeviceManagerSetWrapperActive, useDeviceManagerTestCreationField, useDeviceManagerTestField, useDeviceManagerUpdateConfig, useDeviceManagerUpdateDeviceField, useDeviceManagerUpdateDeviceFieldsBatch, useDeviceNetworkLink, useDeviceOpsGetConfigEntries, useDeviceOpsGetRawState, useDeviceOpsGetSettingsSchema, useDeviceOpsGetStreamSources, useDeviceOpsRemoveDevice, useDeviceOpsRunAction, useDeviceOpsSetConfig, useDeviceProviderAdoptDiscoveredDevice, useDeviceProviderCreateDevice, useDeviceProviderDiscoverDevices, useDeviceProviderGetChildCreationSchema, useDeviceProviderGetDevices, useDeviceProviderGetDiscoveryParamsSchema, useDeviceProviderGetManualCreationType, useDeviceProviderGetStatus, useDeviceProviderReloadDevice, useDeviceProviderStart, useDeviceProviderStop, useDeviceProviderSupportsDiscovery, useDeviceProviderSupportsManualCreation, useDeviceProviderTestCreationField, useDeviceProxy, useDeviceQuickDetailOpen, useDeviceSnapshot, useDeviceSnapshotImage, useDeviceState, useDeviceStateClaimFields, useDeviceStateGetAllSnapshots, useDeviceStateGetCapSlice, useDeviceStateGetNativeCapSlice, useDeviceStateGetSnapshot, useDeviceStateListClaims, useDeviceStatePatchOwnedFields, useDeviceStateReleaseClaim, useDeviceStateSetCapSlice, useDeviceStateSlice, useDeviceStatusGetStatus, useDeviceWebrtc, useDevices, useDishwasherGetOptions, useDishwasherGetStatus, useDishwasherPause, useDishwasherResume, useDishwasherSetChildLock, useDishwasherSetExtraDry, useDishwasherSetHalfLoad, useDishwasherSetHygienePlus, useDishwasherSetIntensiveZone, useDishwasherSetOption, useDishwasherSetPower, useDishwasherSetProgram, useDishwasherSetVarioSpeedPlus, useDishwasherStart, useDishwasherStop, useDisplayIconResolver, useDoorbellEvents, useDoorbellGetStatus, useEnumSensorGetStatus, useEventEmitterGetStatus, useEventInvalidation, useEventStreamLatest, useEventStreamMap, useFaceGalleryAssignFace, useFaceGalleryAssignFaces, useFaceGalleryAuditIdentitySamples, useFaceGalleryCreateIdentity, useFaceGalleryDeleteFace, useFaceGalleryDeleteIdentity, useFaceGalleryGetFaceByTrack, useFaceGalleryGetFaceMedia, useFaceGalleryListIdentities, useFaceGalleryListIdentitySamples, useFaceGalleryListRecentFaces, useFaceGalleryRemoveSample, useFaceGalleryRenameIdentity, useFaceGallerySuggestFaceClusters, useFaceGalleryUnassignFace, useFaceGalleryUnassignFaces, useFanControlGetStatus, useFanControlSetDirection, useFanControlSetOscillating, useFanControlSetPercentage, useFanControlSetPreset, useFeatureProbeGetStatus, useFloodGetStatus, useGasGetStatus, useHeatingValveGetStatus, useHumidifierGetStatus, useHumidifierSetMode, useHumidifierSetOn, useHumidifierSetTargetHumidity, useHumiditySensorGetStatus, useIconGlyphsVersion, useImageGetStatus, useImageSettingsGetOptions, useImageSettingsGetStatus, useImageSettingsSetSettings, useIntegrationsCreate, useIntegrationsDelete, useIntegrationsGet, useIntegrationsGetAvailableTypes, useIntegrationsGetByAddonId, useIntegrationsGetSettings, useIntegrationsList, useIntegrationsSetSettings, useIntegrationsTestConnection, useIntegrationsUpdate, useIntercomEndTalkSession, useIntercomGetStatus, useIntercomHandleAnswer, useIntercomPushTalkAudio, useIntercomStartSession, useIntercomStartTalkSession, useIntercomStopSession, useIsMidWidth, useIsMobile, useItemFormUrl, useKeychainAdminAcknowledgeReset, useKeychainAdminDeleteOrphans, useKeychainAdminDownloadRecoveryKey, useKeychainAdminImportSecret, useKeychainAdminListAudit, useKeychainAdminListSecrets, useKeychainAdminPurgeRetiredKeys, useKeychainAdminResetKeychain, useKeychainAdminResetReport, useKeychainAdminReveal, useKeychainAdminRotateMasterKey, useKeychainAdminSetSystemSecret, useKeychainAdminStatus, useLawnMowerControlDock, useLawnMowerControlGetStatus, useLawnMowerControlPause, useLawnMowerControlStartMowing, useLiveBuffer, useLiveEvent, useLlmCancel, useLlmDeleteModel, useLlmDeleteProfile, useLlmGenerate, useLlmGenerateVision, useLlmGetDefaults, useLlmGetRuntimeStatus, useLlmGetUsage, useLlmInstallModel, useLlmListModelCatalog, useLlmListModels, useLlmListNodeModels, useLlmListProfileKinds, useLlmListProfiles, useLlmListRuntimeNodes, useLlmMigrateRawSecrets, useLlmResolveModelRef, useLlmSetDefault, useLlmStartRuntime, useLlmStopRuntime, useLlmTestProfile, useLlmUpsertProfile, useLocalNetworkAddCustomAddress, useLocalNetworkDownloadCa, useLocalNetworkGetAddresses, useLocalNetworkGetAllowedAddresses, useLocalNetworkGetConnectionEndpoints, useLocalNetworkGetHubAddresses, useLocalNetworkGetNotificationEndpoint, useLocalNetworkGetPreferred, useLocalNetworkGetTlsStatus, useLocalNetworkGetViewerEndpoints, useLocalNetworkList, useLocalNetworkRegenerateCertificate, useLocalNetworkRemoveCustomAddress, useLocalNetworkResetAllowlistToBestMatch, useLocalNetworkRevertToGeneratedCertificate, useLocalNetworkSetAddressRole, useLocalNetworkSetAllowedAddresses, useLocalNetworkSetNotificationEndpoint, useLocalNetworkSetViewerEndpoints, useLocalNetworkUploadCertificate, useLocalNetworkUseStoredUploadedCertificate, useLockControlGetStatus, useLockControlLock, useLockControlOpen, useLockControlUnlock, useMediaPlayerGetStatus, useMediaPlayerNext, useMediaPlayerPause, useMediaPlayerPlay, useMediaPlayerPlayMedia, useMediaPlayerPrevious, useMediaPlayerSeek, useMediaPlayerSelectSource, useMediaPlayerSetMute, useMediaPlayerSetRepeat, useMediaPlayerSetShuffle, useMediaPlayerSetVolume, useMediaPlayerStop, useMeshNetworkGetStatus, useMeshNetworkJoin, useMeshNetworkLeave, useMeshNetworkListPeers, useMeshNetworkLogout, useMeshNetworkStartLogin, useMeshNetworkTestConnection, useMetricsProviderCollectSnapshot, useMetricsProviderGetAddonStats, useMetricsProviderGetCached, useMetricsProviderGetCpuTemperature, useMetricsProviderGetCurrent, useMetricsProviderGetDiskSpace, useMetricsProviderGetGpuInfo, useMetricsProviderGetLoadSeries, useMetricsProviderGetProcessStats, useMetricsProviderListAddonInstances, useMetricsProviderListNodeProcesses, useMotionDetectionAnalyze, useMotionDetectionApplyDeviceSettingsPatch, useMotionDetectionGetDeviceLiveContribution, useMotionDetectionGetDeviceSettingsContribution, useMotionDetectionRemoveCamera, useMotionDetectionReset, useMotionGetStatus, useMotionIsDetected, useMotionTriggerGetStatus, useMotionTriggerSetMotionTrigger, useMotionZonesGetOptions, useMotionZonesGetStatus, useMotionZonesSetZone, useMqttBrokerAddBroker, useMqttBrokerGetBrokerConfig, useMqttBrokerGetStatus, useMqttBrokerListBrokers, useMqttBrokerRemoveBroker, useMqttBrokerStartEmbeddedBroker, useMqttBrokerStopEmbeddedBroker, useMqttBrokerTestConnection, useNativeObjectDetectionGetOptions, useNativeObjectDetectionGetStatus, useNativeObjectDetectionSetEnabled, useNativeObjectDetectionSetSettings, useNavigation, useNavigationGetFeatures, useNavigationGetStatus, useNavigationGoToPoint, useNavigationListActions, useNavigationMove, useNavigationPlaySound, useNavigationRunAction, useNavigationSetLightLevel, useNavigationSetLightMode, useNavigationSetLightOn, useNavigationStop, useNetworkAccessGetEndpoint, useNetworkAccessGetStatus, useNetworkAccessListEndpoints, useNetworkAccessStart, useNetworkAccessStop, useNetworkLinkGetStatus, useNetworkQualityGetAllStats, useNetworkQualityGetDeviceStats, useNetworkQualityReportClientStats, useNodesClusterAddonStatus, useNodesDeployAddon, useNodesDirectory, useNodesExecuteQuery, useNodesGetCapUsageGraph, useNodesGetNodeAddons, useNodesRenameNode, useNodesRestartAddon, useNodesRestartNode, useNodesRestartProcess, useNodesSetProcessLogLevel, useNodesShutdownNode, useNodesTopology, useNodesUndeployAddon, useNotificationOutputDeleteTarget, useNotificationOutputDiscoverTargets, useNotificationOutputGetTarget, useNotificationOutputListTargetKinds, useNotificationOutputListTargets, useNotificationOutputProbeTargetConfig, useNotificationOutputSend, useNotificationOutputSetTargetEnabled, useNotificationOutputTestTarget, useNotificationOutputUpsertTarget, useNotificationRulesActivateAlarmProfile, useNotificationRulesCancelSnooze, useNotificationRulesCreateAlarmProfile, useNotificationRulesCreateRule, useNotificationRulesCreateSequence, useNotificationRulesCreateSnooze, useNotificationRulesDeleteAlarmProfile, useNotificationRulesDeleteRule, useNotificationRulesDeleteSequence, useNotificationRulesGetAlarmConfig, useNotificationRulesGetAlarmSensors, useNotificationRulesGetClipRetentionAsks, useNotificationRulesGetConditionCatalog, useNotificationRulesGetHistory, useNotificationRulesGetRule, useNotificationRulesGetRuleLastRuns, useNotificationRulesGetSequence, useNotificationRulesGetTemplateCatalog, useNotificationRulesListDeviceMutes, useNotificationRulesListRules, useNotificationRulesListSequences, useNotificationRulesListSnoozes, useNotificationRulesPreviewStateSummary, useNotificationRulesPreviewTemplate, useNotificationRulesRenameAlarmProfile, useNotificationRulesResolveArtifactUrl, useNotificationRulesSetAlarmConfig, useNotificationRulesSetDeviceMuted, useNotificationRulesSetRuleEnabled, useNotificationRulesTestRule, useNotificationRulesUpdateRule, useNotificationRulesUpdateSequence, useNotifierCancel, useNotifierGetStatus, useNotifierSend, useNumericSensorGetStatus, useOidcConnectionsConnectionFormSchema, useOidcConnectionsCreateConnection, useOidcConnectionsDeleteConnection, useOidcConnectionsListConnections, useOidcConnectionsUpdateConnection, useOptimisticSlice, useOptionalSystem, useOptionalWidgetRegistry, useOsdGetStatus, useOsdManagerClearSlotBinding, useOsdManagerCopyDeviceConfiguration, useOsdManagerDeleteTemplate, useOsdManagerGetConditionSupport, useOsdManagerGetDeviceOsd, useOsdManagerGetSourceCatalog, useOsdManagerListTemplates, useOsdManagerPreviewSlot, useOsdManagerRenderDevice, useOsdManagerSetSlotBinding, useOsdManagerSetSlotCameraEnabled, useOsdManagerUpsertTemplate, useOsdSetOverlay, usePTZ, usePersistedPageSize, usePetFeederCallPet, usePetFeederCancelFeed, usePetFeederFeed, usePetFeederGetStatus, usePetFeederMarkFoodReplenished, usePetFeederPlaySound, usePetFeederResetDesiccant, usePetFeederSetChildLock, usePetFeederSetFeedSound, usePetFeederSetIndicatorLight, usePetFeederSetVolume, usePickerSiteZones, usePipelineAnalyticsAddRetrainFrameFromRecording, usePipelineAnalyticsAddRetrainFramesToCollection, usePipelineAnalyticsApplyDeviceSettingsPatch, usePipelineAnalyticsBackfillRetrainOrigins, usePipelineAnalyticsCancelRelocateMedia, usePipelineAnalyticsCancelStorageMigrationMove, usePipelineAnalyticsClearTrackRelation, usePipelineAnalyticsClearTracks, usePipelineAnalyticsCompleteRetrainTrack, usePipelineAnalyticsCountRelocatableMedia, usePipelineAnalyticsCountUnstampedEventMedia, usePipelineAnalyticsCreateRetrainCollection, usePipelineAnalyticsDeleteDeviceEvents, usePipelineAnalyticsDeleteRetrainCollection, usePipelineAnalyticsDeleteTracks, usePipelineAnalyticsDeselectRetrainFrame, usePipelineAnalyticsGetActiveTracks, usePipelineAnalyticsGetAudioEvents, usePipelineAnalyticsGetDeviceLiveContribution, usePipelineAnalyticsGetDeviceSettingsContribution, usePipelineAnalyticsGetEventDensity, usePipelineAnalyticsGetEventDensityBatch, usePipelineAnalyticsGetEventMedia, usePipelineAnalyticsGetEventMediaFootprintByKind, usePipelineAnalyticsGetEventStoreFootprint, usePipelineAnalyticsGetKeyEvents, usePipelineAnalyticsGetKeyEventsBatch, usePipelineAnalyticsGetMediaReclaimStatus, usePipelineAnalyticsGetMotionEvents, usePipelineAnalyticsGetObjectEmbeddingRebuildStatus, usePipelineAnalyticsGetObjectEvents, usePipelineAnalyticsGetRetrainCorpusKpi, usePipelineAnalyticsGetRetrainExportUrl, usePipelineAnalyticsGetRetrainFrameImage, usePipelineAnalyticsGetSensorEvents, usePipelineAnalyticsGetStorageMigrationMoveStatus, usePipelineAnalyticsGetTrack, usePipelineAnalyticsGetTrackMedia, usePipelineAnalyticsGetTrainingExportSummary, usePipelineAnalyticsGetTrainingExportUrl, usePipelineAnalyticsListArchivedDebugNotes, usePipelineAnalyticsListBirthDecisions, usePipelineAnalyticsListEventKinds, usePipelineAnalyticsListEventKindsBatch, usePipelineAnalyticsListEventMedia, usePipelineAnalyticsListOpsLog, usePipelineAnalyticsListRecentTracks, usePipelineAnalyticsListRelocateMediaJobs, usePipelineAnalyticsListRetrainAnnotations, usePipelineAnalyticsListRetrainAssistProfiles, usePipelineAnalyticsListRetrainCollectionFrames, usePipelineAnalyticsListRetrainCollections, usePipelineAnalyticsListRetrainCorpusPage, usePipelineAnalyticsListRetrainFrames, usePipelineAnalyticsListRetrainStaging, usePipelineAnalyticsListTrackMedia, usePipelineAnalyticsListTrackRelations, usePipelineAnalyticsListTracks, usePipelineAnalyticsOfferRetrainArchive, usePipelineAnalyticsOwnersWithMedia, usePipelineAnalyticsPauseForStorageMigration, usePipelineAnalyticsProposeRetrainAnnotations, usePipelineAnalyticsProposeRetrainAnnotationsWithLlm, usePipelineAnalyticsPruneEvents, usePipelineAnalyticsPruneEventsBefore, usePipelineAnalyticsPruneTracksBefore, usePipelineAnalyticsReadLongTermSeries, usePipelineAnalyticsRebuildObjectEmbeddings, usePipelineAnalyticsReclaimDebugMedia, usePipelineAnalyticsReconcileFromDisk, usePipelineAnalyticsRefreshStorageLocationsForMigration, usePipelineAnalyticsRelocateMedia, usePipelineAnalyticsRemoveRetrainFrameFromCollection, usePipelineAnalyticsRenameRetrainCollection, usePipelineAnalyticsResolveRetrainTrainingCorpus, usePipelineAnalyticsRestageRetrainTrack, usePipelineAnalyticsResumeForStorageMigration, usePipelineAnalyticsSaveRetrainAnnotations, usePipelineAnalyticsSearchObjectEvents, usePipelineAnalyticsSelectRetrainFrames, usePipelineAnalyticsSetRetrainFrameComplete, usePipelineAnalyticsSetTrackFlags, usePipelineAnalyticsSetTrackPurity, usePipelineAnalyticsSetTrackRelation, usePipelineAnalyticsStartStorageMigrationMove, usePipelineAnalyticsWipeAllAnalytics, usePipelineAnalyticsWipeObjectEmbeddings, usePipelineAnalyticsWriteRetrainArchive, usePipelineExecutorCacheFrameInPool, usePipelineExecutorClearDeviceOverrides, usePipelineExecutorClearModelBoundDeviceOverrides, usePipelineExecutorDeleteModel, usePipelineExecutorDownloadModel, usePipelineExecutorGetAddonModels, usePipelineExecutorGetAudioCapabilities, usePipelineExecutorGetAvailableEngines, usePipelineExecutorGetCapabilities, usePipelineExecutorGetDefaultSteps, usePipelineExecutorGetDetectionConfigSchema, usePipelineExecutorGetEffectiveTuning, usePipelineExecutorGetEngineProvisioning, usePipelineExecutorGetGlobalPipelineConfig, usePipelineExecutorGetGlobalSteps, usePipelineExecutorGetInferenceDeviceHealth, usePipelineExecutorGetOrchestratorConfigSchema, usePipelineExecutorGetReferenceAudio, usePipelineExecutorGetReferenceAudioFiles, usePipelineExecutorGetReferenceImage, usePipelineExecutorGetSchema, usePipelineExecutorGetSelectedEngine, usePipelineExecutorGetVideoPipelineSteps, usePipelineExecutorInferCached, usePipelineExecutorKillEngine, usePipelineExecutorListLoadedEngines, usePipelineExecutorListReferenceImages, usePipelineExecutorRearmInferenceDevice, usePipelineExecutorRunAudioTest, usePipelineExecutorRunPipeline, usePipelineExecutorRunPipelineBatch, usePipelineExecutorSetVideoPipelineSteps, usePipelineExecutorSpinEngine, usePipelineExecutorUncacheFrame, usePipelineExecutorValidatePipeline, usePipelineOrchestratorAbortNodeBenchmark, usePipelineOrchestratorApplyAutoTune, usePipelineOrchestratorApplyDeviceSettingsPatch, usePipelineOrchestratorAssignAudio, usePipelineOrchestratorAssignPipeline, usePipelineOrchestratorDeleteNodeBenchmarkResult, usePipelineOrchestratorGetAgentLoad, usePipelineOrchestratorGetAgentSettings, usePipelineOrchestratorGetAudioAssignment, usePipelineOrchestratorGetAudioAssignments, usePipelineOrchestratorGetAudioNodeLoad, usePipelineOrchestratorGetCameraDetectionSource, usePipelineOrchestratorGetCameraMetrics, usePipelineOrchestratorGetCameraSettings, usePipelineOrchestratorGetCameraStatus, usePipelineOrchestratorGetCameraStatuses, usePipelineOrchestratorGetCameraStepOverrides, usePipelineOrchestratorGetCameraSwitches, usePipelineOrchestratorGetCapabilityBindings, usePipelineOrchestratorGetClusterModelChoice, usePipelineOrchestratorGetDeviceLiveContribution, usePipelineOrchestratorGetDeviceSettingsContribution, usePipelineOrchestratorGetEffectiveShares, usePipelineOrchestratorGetGlobalMetrics, usePipelineOrchestratorGetIngestOwner, usePipelineOrchestratorGetNodeBenchmarkCandidates, usePipelineOrchestratorGetNodeBenchmarkPreflight, usePipelineOrchestratorGetNodeBenchmarkStatus, usePipelineOrchestratorGetNodeInferenceDevices, usePipelineOrchestratorGetPipelineAssignment, usePipelineOrchestratorGetPipelineAssignments, usePipelineOrchestratorGetPipelineDevicePin, usePipelineOrchestratorGetPlacementStats, usePipelineOrchestratorGetReconcileFromDiskStatus, usePipelineOrchestratorListAgentSettings, usePipelineOrchestratorListNodeBenchmarkResults, usePipelineOrchestratorPauseForStorageMigration, usePipelineOrchestratorPlanAutoTune, usePipelineOrchestratorRebalance, usePipelineOrchestratorReconcileFromDisk, usePipelineOrchestratorRemoveAgentSettings, usePipelineOrchestratorRequestOccupancyLook, usePipelineOrchestratorResetNodePipelineDefaults, usePipelineOrchestratorResolvePipeline, usePipelineOrchestratorResumeForStorageMigration, usePipelineOrchestratorSetAgentCapabilities, usePipelineOrchestratorSetAgentDecodeLimits, usePipelineOrchestratorSetAgentInferenceDevices, usePipelineOrchestratorSetAgentMaxCameras, usePipelineOrchestratorSetAgentReachableHost, usePipelineOrchestratorSetCameraDetectionSource, usePipelineOrchestratorSetCameraPipelineForAgent, usePipelineOrchestratorSetCameraSensors, usePipelineOrchestratorSetCameraStepOverride, usePipelineOrchestratorSetCameraStepSettings, usePipelineOrchestratorSetCameraStepToggle, usePipelineOrchestratorSetCameraSwitch, usePipelineOrchestratorSetCapabilityBinding, usePipelineOrchestratorSetLoadBalancingShares, usePipelineOrchestratorSetPipelineDevicePin, usePipelineOrchestratorSetShareSource, usePipelineOrchestratorStartNodeBenchmark, usePipelineOrchestratorSuggestShares, usePipelineOrchestratorUnassignAudio, usePipelineOrchestratorUnassignPipeline, usePipelineRunnerAbortNodeBench, usePipelineRunnerAttachCamera, usePipelineRunnerDetachCamera, usePipelineRunnerGetAllCameraMetrics, usePipelineRunnerGetCameraMetrics, usePipelineRunnerGetLocalCameras, usePipelineRunnerGetLocalLoad, usePipelineRunnerGetLocalMetrics, usePipelineRunnerGetNativeCrop, usePipelineRunnerGetNodeBenchStatus, usePipelineRunnerGetParkedTrackFrame, usePipelineRunnerParkTrackFrame, usePipelineRunnerReleaseParkedTrackFrames, usePipelineRunnerReportMotion, usePipelineRunnerRequestOccupancyBurst, usePipelineRunnerRunDetailSubtree, usePipelineRunnerRunStatelessStep, usePipelineRunnerStartNodeBench, usePlateGalleryAssignPlate, usePlateGalleryAssignPlates, usePlateGalleryCorrectPlateText, usePlateGalleryCreateVehicle, usePlateGalleryDeletePlate, usePlateGalleryDeleteVehicle, usePlateGalleryGetPlateByTrack, usePlateGalleryGetPlateMedia, usePlateGalleryListPlates, usePlateGalleryListVehicleSamples, usePlateGalleryListVehicles, usePlateGalleryRemoveVehicleSample, usePlateGalleryRenameVehicle, usePlateGallerySearchPlates, usePlateGallerySuggestPlateClusters, usePlateGalleryUnassignPlate, usePlateGalleryUnassignPlates, usePlayerOverlayLayer, usePlayerOverlayLayers, usePlayerToolbarButton, usePlayerToolbarButtons, usePowerMeterGetStatus, usePresenceGetStatus, usePressureSensorGetStatus, usePrivacyMaskGetOptions, usePrivacyMaskGetStatus, usePrivacyMaskSetAudioEnabled, usePrivacyMaskSetMask, usePrompt, usePtzAutotrackGetSettings, usePtzAutotrackGetStatus, usePtzAutotrackSetEnabled, usePtzAutotrackSetSettings, usePtzCaptureHomeHere, usePtzContinuousMove, usePtzDeletePreset, usePtzGetHomePreset, usePtzGetHomeReturn, usePtzGetOptions, usePtzGetPosition, usePtzGetPresets, usePtzGetStatus, usePtzGoHome, usePtzGoToPreset, usePtzMove, usePtzSavePreset, usePtzSetAutofocus, usePtzSetHomePreset, usePtzSetHomeReturn, usePtzStop, useRebootReboot, useRecordEntriesDeleteEntry, useRecordEntriesListEntries, useRecordEntriesListEntryKinds, useRecordEntriesPutEntry, useRecordedPlayback, useRecordingArchiveApplyDeviceSettingsPatch, useRecordingArchiveCancelRelocateJob, useRecordingArchiveCancelStorageMigrationMove, useRecordingArchiveDeleteFootprint, useRecordingArchiveGetAvailabilityBatch, useRecordingArchiveGetDaysWithRecordingsBatch, useRecordingArchiveGetDeviceConfig, useRecordingArchiveGetDeviceLiveContribution, useRecordingArchiveGetDeviceSettingsContribution, useRecordingArchiveGetPlacement, useRecordingArchiveGetRelocateResidue, useRecordingArchiveGetStatus, useRecordingArchiveGetStorageMigrationMoveStatus, useRecordingArchiveGetStorageUsage, useRecordingArchiveListOpsLog, useRecordingArchiveListRelocateJobs, useRecordingArchiveLocateSegment, useRecordingArchivePauseForStorageMigration, useRecordingArchivePlanStorageRebalance, useRecordingArchivePruneFootage, useRecordingArchiveReadGopBytes, useRecordingArchiveReadSegmentBytes, useRecordingArchiveReadWindowBytes, useRecordingArchiveReconcileLedgerAgainstDisk, useRecordingArchiveRefreshStorageLocationsForMigration, useRecordingArchiveRelocateFootage, useRecordingArchiveRenderClip, useRecordingArchiveRenderGif, useRecordingArchiveRescanStorage, useRecordingArchiveResumeForStorageMigration, useRecordingArchiveSetDeviceConfig, useRecordingArchiveSetDevicePlacement, useRecordingArchiveStartStorageMigrationMove, useRecordingArchiveStartStorageRebalance, useRecordingExportCancelExport, useRecordingExportCreateExport, useRecordingExportDeleteExport, useRecordingExportGetDownloadUrl, useRecordingExportGetExport, useRecordingExportListExports, useRecordingExportReadExportBytes, useRecordingGetAvailability, useRecordingGetDaysWithRecordings, useRecordingGetPlayback, useRecordingGetPlaybackOptions, useRecordingListSources, useRecordingOnboardGetOptions, useRecordingOnboardGetStatus, useRecordingOnboardSetSettings, useRecordingSignalGetStatus, useRemoteComponent, useRoleDisplayDefaults, useSceneMonitorCaptureReference, useSceneMonitorCreateScene, useSceneMonitorDeleteReference, useSceneMonitorDeleteScene, useSceneMonitorGetStatus, useSceneMonitorListScenes, useSceneMonitorListScenesBatch, useSceneMonitorRecheckNow, useSceneMonitorResetScene, useSceneMonitorUpdateScene, useScriptRunnerGetStatus, useScriptRunnerRun, useScriptRunnerStop, useScrubController, useServerManagementApplyServerUpdate, useServerManagementCheckServerUpdate, useServerManagementGetServerPackageStatus, useServerManagementRestartServer, useServerManagementRollbackServerUpdate, useSettingsStoreAggregate, useSettingsStoreCount, useSettingsStoreDeclareCollection, useSettingsStoreDelete, useSettingsStoreDeleteWhere, useSettingsStoreGet, useSettingsStoreHistogram, useSettingsStoreInsert, useSettingsStoreInsertMany, useSettingsStoreIsEmpty, useSettingsStoreQuery, useSettingsStoreSet, useSettingsStoreTransact, useSettingsStoreUpdate, useSettingsStoreUpdateWhere, useSettingsStoreUpsert, useSettingsStoreUpsertMany, useSmokeGetStatus, useSnapshotApplyDeviceSettingsPatch, useSnapshotGetDebugState, useSnapshotGetDeviceLiveContribution, useSnapshotGetDeviceSettingsContribution, useSnapshotGetSnapshot, useSnapshotGetSnapshotLinks, useSnapshotGetSnapshotOverview, useSnapshotGetStatus, useSnapshotInvalidateCache, useStationaryOverlay, useStorageAbortUpload, useStorageBeginDownload, useStorageBeginUpload, useStorageDelete, useStorageDeleteLocation, useStorageEndDownload, useStorageExists, useStorageFinalizeUpload, useStorageGetAvailableSpace, useStorageList, useStorageListDrainProgress, useStorageListLocationDeclarations, useStorageListLocations, useStorageListProviders, useStorageMigrationCancel, useStorageMigrationCleanupCancel, useStorageMigrationCleanupStart, useStorageMigrationCleanupStatus, useStorageMigrationDrain, useStorageMigrationHistory, useStorageMigrationMoverClearance, useStorageMigrationMovers, useStorageMigrationPlan, useStorageMigrationResidue, useStorageMigrationStart, useStorageMigrationStatus, useStorageRead, useStorageReadChunk, useStorageResolve, useStorageTestConfig, useStorageTestLocation, useStorageUpsertLocation, useStorageWrite, useStorageWriteChunk, useStoredGroupBy, useStreamBrokerAcquireEgressTranscode, useStreamBrokerApplyDeviceSettingsPatch, useStreamBrokerAssignProfile, useStreamBrokerFetchEventMedia, useStreamBrokerForgetDeviceHardware, useStreamBrokerGetAllRtspEntries, useStreamBrokerGetBrokerStats, useStreamBrokerGetDeviceAudioMute, useStreamBrokerGetDeviceLiveContribution, useStreamBrokerGetDeviceSettingsContribution, useStreamBrokerGetLiveViewerCounts, useStreamBrokerGetPreBufferInfo, useStreamBrokerGetRtspEntry, useStreamBrokerGetRtspPort, useStreamBrokerGetStreamUrl, useStreamBrokerGetStreamWithCodec, useStreamBrokerIsRtspEnabled, useStreamBrokerKillClient, useStreamBrokerListAllCameraStreams, useStreamBrokerListAllProfileSlots, useStreamBrokerListClients, useStreamBrokerProbeStream, useStreamBrokerProduceEventMedia, useStreamBrokerPublishCameraStream, useStreamBrokerPullAudioChunks, useStreamBrokerRegenerateRtspToken, useStreamBrokerReleaseEgressTranscode, useStreamBrokerReleaseStreamWithCodec, useStreamBrokerRenderPreBufferClip, useStreamBrokerReportStreamSignal, useStreamBrokerRestartProfile, useStreamBrokerRetractCameraStream, useStreamBrokerSetDeviceAudioMute, useStreamBrokerSetPreBufferDuration, useStreamBrokerSetRtspEnabled, useStreamBrokerSubscribeAudioChunks, useStreamBrokerUnassignProfile, useStreamBrokerUnsubscribeAudioChunks, useStreamCatalogGetCatalog, useStreamParamsGetConfigSchema, useStreamParamsGetOptions, useStreamParamsGetStatus, useStreamParamsSetProfile, useStreamSignalsListSignals, useSwitchGetStatus, useSwitchSetState, useSystem, useSystemDetectSiteLocation, useSystemFeatureFlags, useSystemForceRetentionCleanup, useSystemGetFailureContributions, useSystemGetLoadContributions, useSystemGetLoggingSettings, useSystemGetRequestCensus, useSystemGetRetentionConfig, useSystemGetSiteLocation, useSystemHealth, useSystemInfo, useSystemMutation, useSystemNetworkAddresses, useSystemQuery, useSystemSetLoggingSettings, useSystemSetRetentionConfig, useSystemSetSiteLocation, useTamperGetStatus, useTemperatureSensorGetStatus, useTerminalSessionAdoptLegacyMonitor, useTerminalSessionClose, useTerminalSessionCreateInstance, useTerminalSessionDeleteInstance, useTerminalSessionListInstances, useTerminalSessionListLegacyCameras, useTerminalSessionListProfiles, useTerminalSessionListSessions, useTerminalSessionOpenSession, useTerminalSessionPullOutput, useTerminalSessionResize, useTerminalSessionSetInstanceEnabled, useTerminalSessionUpdateInstance, useTerminalSessionWriteInput, useThemeMode, useToastOnToast, useTrainingCanaryResult, useTrainingCancelJob, useTrainingDiscardResult, useTrainingGetJob, useTrainingGetJobResult, useTrainingListBaseModels, useTrainingListJobs, useTrainingPlanJob, useTrainingRegisterResult, useTrainingRollbackCanary, useTrainingStartJob, useTrainingStartJobBatch, useTurnProviderGetTurnServers, useUiText, useUpdateGetStatus, useUpdateInstallUpdate, useUrlQueryParam, useUserManagementConfirmTotp, useUserManagementCreateUser, useUserManagementDeleteUser, useUserManagementDisableTotp, useUserManagementFindUserByExternalIdentity, useUserManagementGetTotpStatus, useUserManagementListApiKeys, useUserManagementListExternalIdentities, useUserManagementListOauthSessions, useUserManagementListUsers, useUserManagementMigrateRawSecrets, useUserManagementOauthExchangeCode, useUserManagementOauthIssueCode, useUserManagementOauthRefresh, useUserManagementOauthVerifyAccessToken, useUserManagementResetPassword, useUserManagementRevokeApiKey, useUserManagementRevokeOauthSession, useUserManagementSetUserScopes, useUserManagementSetupTotp, useUserManagementUnlinkExternalIdentity, useUserManagementUpdateUser, useUserManagementValidateCredentials, useUserManagementVerifyTotp, useVacuumControlGetStatus, useVacuumControlLocate, useVacuumControlPause, useVacuumControlReturnToBase, useVacuumControlSetFanSpeed, useVacuumControlStart, useVacuumControlStop, useValveClose, useValveGetStatus, useValveOpen, useValveSetPosition, useValveStop, useVibrationGetStatus, useVideoclipsDialClipStream, useVideoclipsGetClipPlayback, useVideoclipsGetPlaybackOptions, useVideoclipsListClips, useVideoclipsListSources, useVideoclipsOfferClipBytes, useVideoclipsReadClipBytes, useVodPlayback, useWaterHeaterGetStatus, useWaterHeaterSetAway, useWaterHeaterSetOperationMode, useWaterHeaterSetTargetTemp, useWeatherGetStatus, useWebrtcSessionAddIceCandidate, useWebrtcSessionApplyDownlinkProbe, useWebrtcSessionCloseSession, useWebrtcSessionCreateSession, useWebrtcSessionGetIceCandidates, useWebrtcSessionGetSessionDebug, useWebrtcSessionGetSessionState, useWebrtcSessionHandleAnswer, useWebrtcSessionHandleOffer, useWebrtcSessionHasAdaptiveBitrate, useWebrtcSessionListStreams, useWidget, useWidgetMetadata, useWidgetRegistry, useZoneAnalyticsGetCameraHistory, useZoneAnalyticsGetCurrentSnapshot, useZoneAnalyticsGetCurrentSnapshotBatch, useZoneAnalyticsGetUnzonedHistory, useZoneAnalyticsGetZoneHistory, useZoneEditing, useZoneRulesListRules, useZoneRulesSetRules, useZonesAddZone, useZonesListZones, useZonesRemoveZone, useZonesUpdateZone, Route as ut, Users as v, vacuumStateMeta, validateScopes, valveStateMeta, variantLabel, GripHorizontal as vn, Cast as vr, Puzzle as vt, Type as w, waterHeaterPhase, waterHeaterTint, weatherConditionMeta, weatherTint, withResolvedParentDetail, withUseRootModel, Footprints as wn, Box as wr, writeStoredGroupBy, Plane as wt, Upload as x, Gauge as xn, Cable as xr, Plug as xt, User as y, Grid2x2 as yn, Car as yr, Power as yt, Sun as z, DoorOpen as zn, Baby as zr, Moon as zt };
67926
+ export { ShieldOff as $, CloudFog as $n, LoaderCircle as $t, TrainFront as A, AddProviderEntryDialog, AddonGlobalSettingsForm, AdminLinkQuickAction, AgentStepEditor, AlarmHeroCard, AlarmInlineControl as AlarmPanelInlineControl, Film as An, AppShell, Bird as Ar, ArcKnob, Pause as At, AudioClassificationList, AudioLevelWaveform, AudioWaveform, AutomationHeroCard, AutomationInlineControl, AutotrackSection, SunMedium as B, BOOLEAN_STATES, BTN_COMPACT, BTN_COMPACT_DANGER, BTN_COMPACT_PRIMARY, BTN_COMPACT_WARNING, BackToStepsButton, Badge, BarSeries, BatteryBadge, DoorOpen as Bn, BottomSheet, Ban as Br, Breadcrumb, BrightnessPanel, MonitorPlay as Bt, Button, ButtonControl, ButtonHeroCard, Undo2 as C, CENTER, CHART_SERIES_TOKENS, CHIP_ACTIVE, CHIP_BASE, CHIP_INACTIVE, CLASS_COLORS, COLUMN_BREAKPOINT_CLASS, COLUMN_PRIORITY, COMMIT_DEDUPE_TOLERANCE_MS, COMMIT_DEDUPE_WINDOW_MS, CONTROL_CAP_NAMES, CONTROL_FILLS, CameraStreamPlayer, Card, ChartFrame, ChartPlaceholder, ChartSwatch, ChartWindowToggle, Checkbox, ChildSectionAccordion, ClimatePanel, Gamepad2 as Cn, CodeBlock, CollapsibleCard, ConfigFormBuilder, FormField as ConfigFormField, ConfigNotificationTargetMultiSelectFieldView, ConfigNotificationTargetSelectFieldView, ConfigSchemaField, ConfirmActionButton, ConfirmDialogProvider, ConsumablesPanel, ContainerChildrenProvider, ContainerPrimaryHero, ControlColumn, ControlHeroCard, ControlInlineControl, ControlPanel, CopyButton, CoverHeroCard, CoverInlineControl, CoverPanel, Cable as Cr, Play as Ct, CustomFieldRenderersProvider, TrendingDown as D, DATA_RECORDS_KIND_PARAM, DATA_RECORDS_OWNER_PARAM, DATA_RECORD_FACET_GROUPS, DEFAULT_COLOR, DEFAULT_PAGINATION_LABELS, DEVICE_COLUMNS, DEVICE_EXPORT_PAGE_SIZE_KEY, DEVICE_LIST_PAGE_SIZE_KEY, DEVICE_LIST_PAGE_SIZE_OPTIONS, DEVICE_PICKER_GROUP_STORAGE_KEY, DEVICE_ROLE_META, DEVICE_TYPE_CONTROL, DEVICE_TYPE_META, DataRecordsHub, DataTable, DataTableFilterBar, DataTablePager, DetectionCanvas, DetectionOverlay, DetectionResultTree, DevShell, DeviceActivityPanel, DeviceBatchToolbar, DeviceCard, DeviceContextProvider, DeviceExportPanel, DeviceExportSetup, DeviceFilterBar, DeviceGrid, DeviceHistoryView, DeviceItem, DeviceList, DeviceMultiSelectField, DevicePicker, DeviceQuickDetailProvider, DeviceSelectField, DeviceSelectorPicker, DeviceStepMatrix, DevicesFailedLine, DevicesLoadingLine, DevicesWidenWarning, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DiscoveryPanel, Flashlight as Dn, DoorbellRecentPanel, Bot as Dr, Dropdown, DropdownContent, DropdownItem, DropdownTrigger, Phone as Dt, DummyHeroCard, DummyInline, TriangleAlert as E, EMPTY_DATA_TABLE_FILTER, EMPTY_HISTORY_PANELS, EVENT_KIND_ICONS, EmptyState, FlaskConical as En, EndpointResultsCard, EndpointTestCard, EntryJsonSchemaForm, Box as Er, ErrorBox, Pin as Et, EventKindGlyph, EventStream, ExtensionsPanel, Thermometer as F, FAST_ICON_GLYPHS, FILL, FLEET_LIST_GC_TIME_MS, FLEET_LIST_QUERY_KEY, FLEET_LIST_STALE_TIME_MS, FanHeroCard, FanInlineControl, FanPanel, FilterBar, FloatingEventStream, FloatingLogStream, FloatingPanel, ExternalLink as Fn, FormField$1 as FormField, BatteryWarning as Fr, Move as Ft, Snowflake as G, GAP_INTERVAL_FACTOR, GRID_GAP, GRID_PAIRED, GRID_QUICK_STATS, GateBadge, GateControl, Cpu as Gn, ArrowLeft as Gr, GripTrack, GroupedModelSelector, GroupedTilePicker, Menu as Gt, Square as H, HISTORY_PAGE_PATH, HISTORY_PANELS_NAMESPACE, HISTORY_SELECTION_MAX, HISTORY_WINDOW_IDS, HISTORY_WINDOW_MS, HOST_WIDGETS, HUB_SESSION_RUN_MS, HistoryChart, HistoryWindowBar, HlsVideo, Dog as Hn, HoverZoomImage, ArrowUp as Hr, Minimize2 as Ht, HubSessionTestCard, HumidifierHeroCard, HumidifierInlineControl, Target as I, INPUT_COMPACT, INPUT_SHELL, IconAction, IconButton, IconPicker, ImageHeroCard, ImageInlineControl, ImageSelector, EllipsisVertical as In, InferenceConfigSelector, Input, BatteryMedium as Ir, MoveVertical as It, ItemFormDialog, SkipForward as J, Cloud as Jn, AlarmSmoke as Jr, MapPin as Jt, Smartphone as K, KebabMenu, KeyValueList, Copy as Kn, ArrowDown as Kr, Megaphone as Kt, Tag as L, LIST_ROW, Label, LawnMowerHeroCard, LawnMowerInlineControl, LayeredSettingsForm, LightHeroCard, LightInlineControl, Droplets as Ln, LockHeroCard, LockInlineControl, LockPanel, LogStream, LoginForm, BatteryLow as Lr, MoveHorizontal as Lt, ToggleRight as M, MEDIA_GRID, MODE_COLOR, MaskShapeCanvas, MediaCard, MediaGrid, MediaPlayerHeroCard, MediaPlayerInlineControl, MediaPlayerPanel, Fan as Mn, MobileDrawer, ModelPicker, MotionZonesSettings, Bell as Mr, Network as Mt, ToggleLeft as N, NAVIGATION_DRIVE_INTERVAL_MS, NODE_COLOR_IDS, NatResultCard, NavigationPanel, NetworkLinkBadge, Eye as Nn, NodeMultiSelectField, NodePicker, NodeSelectField, NotificationTargetSelectField, NotificationTargetsField, BellRing as Nr, Navigation as Nt, TrendingUp as O, OCCUPANCY_KEY, OPTION_SELECT_SEARCH_MIN_OPTIONS, OfflineBadge, FlashlightOff as On, OptionSelect, Bone as Or, Pencil as Ot, Timer as P, PAGE_SIZE_ALL, PHASE_CONFIG, PICKER_CHIP_LIMIT, PICKER_DEFAULT_GROUP_BY, PICKER_GROUP_BY_MODES, PICKER_GROUP_COLLAPSE_AT, PICKER_NONE_KEY, PICKER_NOUN_PLURAL, PICKER_NO_FILTERS, PICKER_PAGE_SIZE, PICKER_SEARCH_DEBOUNCE_MS, PICKER_WINDOW, PRIORITY, PTZOverlay, PageHeader, PageShell, PageSizeSelect, Pagination, PasswordInput, PhaseIcon, PipelineBuilder, PipelineRuntimeSelector, PipelineStep, PlaceholderChipField, PlayerOverlaysProvider, EyeOff as Pn, Popover, PopoverContent, PopoverRowAction, PopoverTrigger, Battery as Pr, PrimaryChildPicker, PrivacyMaskSettings, ProviderBadge, ProviderEntryPills, Music as Pt, PtzPanel, Shield as Q, CloudLightning as Qn, QrCode, LocateFixed as Qt, Sunrise as R, RECONNECT_POLICY, RECORDED_PLAYBACK_MODES, RESOLUTION_BUCKET_MS, RIGHT, ROI_AREA_WARN_FRACTION, ROLE_DESCRIPTOR, ROOT_DETECTOR_STEP_ID, RadialGauge, RecordEntriesPanel, RecordedPlaybackProvider, RecordingPanel, ResponseLog, RetrainReviewBadge, Droplet as Rn, RoleDisplayDefaultsFromServer, RoleDisplayDefaultsProvider, RoleNodePill, RootModelSourceControl, RootSuppliedModelNote, BatteryFull as Rr, MousePointerClick as Rt, RunReport, Unlink2 as S, SCENE_CAPTURE_VARIANTS, SECRET_MASK_PLACEHOLDER, SECRET_REVEAL_MS, SECTION_BODY, SECTION_CARD, SECTION_HEADER, SETTING_ROW, SETTING_ROW_LABEL, SETTING_ROW_STACK_BREAKPOINT, SETTING_ROW_VALUE, SETTING_ROW_VALUE_TEXT, SNAPSHOT_MEDIA_PATH, SPLIT_PANEL_OUTER, SPLIT_PANEL_SIDE, STACK_GAP, STATE_COLOR, STEP_DOCK_WIDTH_PX, SceneMonitorEditor, ScopePicker, ScriptHeroCard, ScriptInlineControl, ScrollArea, SecretFieldControl, SecretRevealProvider, SectionCard, SectionToolbar, SectionedPage, SegmentedChoice, Select, SelectedChip, SemanticBadge, SendFrameToTrainingButton, SensorHeroCard, SensorInlineControl, SensorValueAtom, Separator, SettingRow, Sidebar, SidebarItem, Skeleton, SlideToggle, Gauge as Sn, SnapshotButton, SortHeaderButton, Sparkline, Camera as Sr, Plus as St, StableTabPanel, StatCard, StateTimeline, StateValuesStream, StatusBadge, StepDetailDock, StepInspectorPanel, StepModelRow, StepTimings, Stepper, StorageRecordsPanel, StreamBrokerSelector, StreamDebugOverlay, StreamPanel, Switch, SwitchHeroCard, SwitchInlineControl, SwitchPanel, SystemProvider, Truck as T, TABLE_SCROLL_MAX_H, TEXT_FIELD_LABEL, TEXT_HINT, TEXT_METRIC, TEXT_SECTION_LABEL, TEXT_VALUE, TIMEZONES, Tabs, TabsContent, TabsList, TabsTrigger, TapToggle, ThemeProvider, ThermostatHeroCard, ThermostatInlineControl, TimeSeriesChart, TimezoneSelector, Footprints as Tn, Tooltip, TooltipContent, TooltipTrigger, Bug as Tr, TrainingBaseModelPicker, TrainingCollectionPicker, Pipette as Tt, Sprout as U, UiTextProvider, UiTrans, Database as Un, UnitPicker, ArrowUpToLine as Ur, Mic as Ut, Star as V, VacuumHeroCard, VacuumInlineControl, ValueReadout, ValveHeroCard, ValveInlineControl, VersionBadge, DoorClosed as Vn, VodPlaybackProvider, Baby as Vr, Minus as Vt, Sparkles as W, WaterHeaterHeroCard, WaterHeaterInlineControl, WeatherHeroCard, WeatherInlineControl, WidgetMetricCard, WidgetPanel, WidgetRegistryProvider, WidgetSlot, Crosshair as Wn, ArrowRight as Wr, MessageSquare as Wt, Siren as X, CloudSnow as Xn, Activity as Xr, Lock as Xt, SkipBack as Y, CloudSun as Yn, AirVent as Yr, Mail as Yt, Ship as Z, CloudRain as Zn, ZoneEditingProvider, LockOpen as Zt, UtensilsCrossed as _, Hand as _n, Check as _r, QrCode$1 as _t, Wrench as a, acceleratorLabel, acceleratorShortLabel, agentColumnKey, alignSeries, allDeviceTypeFilterOptions, LayoutGrid as an, anchorNow, applyFleetQueryDefaults, applyPickerFilters, CirclePlus as ar, ariaSortForColumn, assignNodeColorId, Search as at, axisTicks, UserCheck as b, badgeFor, bindsToModel, Grid2x2 as bn, booleanLabels, Cast as br, PowerOff as bt, bucketTotals, buildColorByNodeId, buildDataRecordRows, buildPeerConnectionConfig, buildStepTreeFromSchema, buildTimeSeriesModel, Wifi as c, catalogHasDraft, chartKeys, chartSeriesStyle, childEntityId, childListName, chipOverflow, chunk, classifyAddressScope, classifyCapability, classifySessionVerdict, clearPickerGroup, clusterRootModelIds, cn, colorIdForNode, columnsForContext, compareSrflxMappings, computePageButtons, containerChildToRef, countableDevices, coverHighlight, coverageLine, CircleCheck as cr, createAddonI18n, createSharedContext, createTheme, Save as ct, currentAddonLanguage, cursorFractionFor, WifiLow as d, darkColors, dataRecordContents, dataRecordKey, dataRecordSearchText, dataRecordType, dataTableFacetGroups, dataTableFacets, decodeCustomWindow, decodeSeriesRef, defaultAxisSpec, defaultOfField, defaultSelectClusterRoots, defaultSingleClusterRoot, defaultTheme, defaultUiText, deriveDeviceKind, deriveWebrtcDebugStats, describeDeviceSelector, deviceIntegrationResolver, deviceListStateOf, deviceMatchesFilter, deviceOptionLabel, deviceRoleMeta, deviceRoleMetaOf, deviceTypeLabel, deviceTypeMeta, deviceTypeMetaOf, devicesToOptions, ImageOff as dn, dockBox, ChevronsUpDown as dr, draftFromQuery, RotateCw as dt, effectivePageSize, emptyDevicesWidens, emptySessionRun, List as en, encodeCustomWindow, encodeSeriesRef, energyBuckets, ensureMfHostInit, Clock as er, ShieldCheck as et, eventKindLabel, WifiHigh as f, filterDataTableRows, filterDeviceOptions, filterOccupancyLines, filterOptions as filterOptionSelectOptions, filterPickerDevices, finalizeSessionRun, findTimezone, flattenPickerGrouping, flattenPickerRows, fleetViewOf, House as fn, formatChartBytes, formatChartNumber, formatChartValue, formatControlDateTime, formatLastSeen, formatNumeric, formatSettingValue, formatSpan, formatTimeLabel, ChevronsDownUp as fr, freezeVerdict, RotateCcw as ft, fuzzyMatch, Vibrate as g, gapThresholdMs, getClassColor, getPhaseVisual, HardDrive as gn, ChevronDown as gr, groupAgentColumns, groupChildrenByLayout, groupDeviceSeries, groupPickerDevices, groupPickerDevicesBy, Radar as gt, Volume2 as h, hardwareLabel, historyPageHref, Hash as hn, ChevronLeft as hr, RadioTower as ht, hubOrigin, hubPath, hubUrl, hubWsUrl, humanDuration, humanizeName, humidifierTint, X as i, iconGlyphsFailed, iconGlyphsLoaded, Leaf as in, initialScrubState, insertAtSelection, CircleQuestionMark as ir, isAbsentProvider, isByteUnit, isDataTableFilterActive, isFieldVisible, isGroupExpanded, isHistoryWindowId, isKeychainFieldValue, isRedactedSecret, isRefusal, isSecretField, isValueField, Server as it, Tractor as j, Feather as jn, Bike as jr, Package as jt, Trash2 as k, Flame as kn, Blinds as kr, PawPrint as kt, WifiZero as l, lawnMowerActivityMeta, lightColors, linePoints, Inbox as ln, loadIconGlyphs, loadRemoteBundle, CircleCheckBig as lr, Sailboat as lt, lucideExportName, VolumeX as m, makeScrubBridge, mapRootDetectorPlacements, mergeSecretForSubmit, metadataEntries, metadataString, mirror, Heater as mn, mountAddonPage, mountPath, ChevronRight as mr, Radio as mt, ZoomIn as n, nextGridPosition, nextReconnectAction, nextSort, nextSortDirection, nextTableSort, niceMax, niceRange, Link2 as nn, nodeColorClass, nodeSoftBgClass, nodeTextColorClass, normalizeForSearch, normalizeLanguage, normalizeSelection, CircleX as nr, Share2 as nt, Workflow as o, occupancyFacets, occupancyLines, offListSelections, Laugh as on, openTopLayer, optionSearchText as optionSelectSearchText, CirclePause as or, orphanDeviceLabel, ScrollText as ot, overrideEntityIdFromLink, Waves as p, pageRows, pageSizeLabel, paginateRows, parentDetailOf, parseCapKey, parseIceCandidate, parsePickerGroupBy, parseProducerKey, parseRecordedServerMessage, partitionSelected as partitionOptionSelectSelected, partitionSuggested, pickerFacetFilterShown, pickerFacetKey, pickerFacetOptions, pickerFiltersActive, pickerGroupKey, pickerHiddenSelectedCount, pickerLocationKey, pickerQueryTokens, pickerRowFacts, pickerSearchText, pickerZoneFilterSuspended, pinnedStreamChoices, placePopover, History as pn, ChevronUp as pr, previousWindow, providerIcons, RefreshCw as pt, SlidersHorizontal as q, Cog as qn, ArrowDownToLine as qr, Maximize2 as qt, Zap as r, readHistory, readHistoryPanels, readItemId, readStoredGroupBy, reduceSessionRun, registerAddonBundle, renderMarkup, renderTagged, resetHubMountForTests, resetPickerFilters, resolveContainerPrimary, resolveControlAlign, resolveDeviceControl, resolveDeviceIntegration, resolveDisplayIcon, resolveEffectiveDefaultModel, resolveEffectiveStepModel, resolveEventKindIcon, resolveHistoryWindow, resolveSearchable as resolveOptionSelectSearchable, resolveParentNames, resolvePrimaryChild, resolveSelectedDevices, resolveSensorDisplay, resolveStepDefaultModel, retrainReviewBadgeLook, Lightbulb as rn, roiTooLarge, rootSourceState, routerBasename, CircleStop as rr, Settings as rt, Wind as s, sameLocationSuggestions, scrubReducer, searchSteps, secretDisplayValue, secretSubmitValue, segmentsFromChanges, selectPickerGroup, selectedDeviceOptions, serializeRecordedCommand, seriesRefId, settingsModelIdFor, sharedGapSpans, shiftPoints, shouldCommit, shouldEmit, shouldShowAddNewFooter, shouldShowUseQueryRow, shouldUseSingleNode, showsNoCamerasHint, Info as sn, sortRows, sortRowsByColumn, CircleDot as sr, Scan as st, stackBuckets, stackSeries, stateAt, stateRow, stateStyle, statusIcons, statusLine, stepFieldDomId, stepHasModelForFormat, stepLocalFields, stepModelOptions, stripParentNamePrefix, summarizeHubCandidates, swatchFillClass, swatchLineClass, ZoomOut as t, tabForSelector, tankAlert, themeToCss, themeToRootCss, timeInState, ListChecks as tn, toStationaryOverlayBox, toggleDataTableFacet, toggleDataTableGroupFacet, togglePickerDevice, togglePickerKey, Clapperboard as tr, trainingRefusalText, translateAddonKey, trpc, ShieldAlert as tt, typeLabel, WifiOff as u, Image as un, unitAxes, CircleAlert as ur, useAccessoriesGetStatus, useAccessoriesSetChildHidden, useAddonPagesListPages, useAddonSettingsGetDeviceSettings, useAddonSettingsGetGlobalSettings, useAddonSettingsGetIntegrationSettings, useAddonSettingsUpdateDeviceSettings, useAddonSettingsUpdateGlobalSettings, useAddonT, useAddonWidgetsListWidgets, useAddonsApplyAutoUpdateToAll, useAddonsCancelJob, useAddonsCustom, useAddonsForceRefresh, useAddonsGetAddonAutoUpdate, useAddonsGetAutoUpdateSettings, useAddonsGetJob, useAddonsGetLastRestart, useAddonsGetLogs, useAddonsGetVersions, useAddonsInstallFromWorkspace, useAddonsInstallPackage, useAddonsIsWorkspaceAvailable, useAddonsList, useAddonsListCapabilityProviders, useAddonsListFrameworkPackages, useAddonsListInstallations, useAddonsListJobs, useAddonsListPackages, useAddonsListUpdates, useAddonsListWorkspacePackages, useAddonsOnAddonLogs, useAddonsReloadPackages, useAddonsRestartAddon, useAddonsRestartServer, useAddonsRetryLoad, useAddonsRollbackPackage, useAddonsSearchAvailable, useAddonsSetAddonAutoUpdate, useAddonsSetAutoUpdateSettings, useAddonsSetCapabilityProviderEnabled, useAddonsStartJob, useAddonsUninstallPackage, useAddonsUpdatePackage, useAirQualitySensorGetStatus, useAlarmPanelArm, useAlarmPanelDisarm, useAlarmPanelGetStatus, useAlarmPanelTrigger, useAlert, useAlertsDismiss, useAlertsEmit, useAlertsGetUnreadCount, useAlertsList, useAlertsMarkAllRead, useAlertsMarkRead, useAlertsUpdate, useAllWidgets, useAmbientLightSensorGetStatus, useAudioAnalysisApplyDeviceSettingsPatch, useAudioAnalysisGetDeviceLiveContribution, useAudioAnalysisGetDeviceSettingsContribution, useAudioAnalysisResolveDeviceSettings, useAudioAnalyzerAnalyseChunk, useAudioAnalyzerAttachDevice, useAudioAnalyzerClassify, useAudioAnalyzerDetachDevice, useAudioAnalyzerDispose, useAudioAnalyzerIsReady, useAudioAnalyzerReprobeAudioEngine, useAudioCodecCanHandle, useAudioCodecCloseSession, useAudioCodecCreateDecodeSession, useAudioCodecCreateEncodeSession, useAudioCodecFlushEncode, useAudioCodecListActiveSessions, useAudioCodecListSupportedCodecs, useAudioCodecPullEncoded, useAudioCodecPullPcm, useAudioCodecPushEncodedFrame, useAudioCodecPushPcm, useAudioMetricsGetCurrentSnapshot, useAudioMetricsGetHistory, useAutomationControlDisable, useAutomationControlEnable, useAutomationControlGetStatus, useAutomationControlTrigger, useBackupCancel, useBackupCheckBackupKey, useBackupDelete, useBackupDeleteSchedule, useBackupDisableEncryption, useBackupGetEncryption, useBackupGetEntries, useBackupList, useBackupListArchives, useBackupListAvailableDatabases, useBackupListDestinations, useBackupListLocations, useBackupListRuns, useBackupListSchedules, useBackupPreviewSchedule, useBackupRestore, useBackupSetupEncryption, useBackupTrigger, useBackupUpsertDestinationPolicy, useBackupUpsertSchedule, useBatteryGetStatus, useBatteryWakeForStream, useBinaryGetStatus, useBrightnessGetStatus, useBrightnessSetBrightness, useBrokerAdd, useBrokerGet, useBrokerGetBrokerConfig, useBrokerGetSettings, useBrokerGetSettingsSchema, useBrokerGetState, useBrokerGetStatus, useBrokerList, useBrokerListProviders, useBrokerPublish, useBrokerRemove, useBrokerSetSettings, useBrokerSubscribe, useBrokerTestConnection, useBrokerTestSettings, useBrokerUnsubscribe, useButtonPress, useCalendarsGetPreferences, useCalendarsListCalendars, useCalendarsListEvents, useCalendarsSetCalendarPreference, useCameraCredentialsGetCredentials, useCameraCredentialsGetStatus, useCameraGridLayoutApplyDeviceSettingsPatch, useCameraGridLayoutGetDeviceLiveContribution, useCameraGridLayoutGetDeviceSettingsContribution, useCameraGridLayoutGetLayout, useCameraGridLayoutSaveLayout, useCameraPipelineConfigApplyDeviceSettingsPatch, useCameraPipelineConfigGetDeviceLiveContribution, useCameraPipelineConfigGetDeviceSettingsContribution, useCameraStreamsGetBrokerStreams, useCameraStreamsGetCameraStreams, useCameraStreamsGetProfileRtspEntries, useCameraStreamsGetRtspEntries, useCameraStreamsPickStream, useCapabilityPresence, useCarbonMonoxideGetStatus, useClimateControlGetStatus, useClimateControlSetFanMode, useClimateControlSetMode, useClimateControlSetPreset, useClimateControlSetSwingHorizontal, useClimateControlSetSwingVertical, useClimateControlSetTarget, useClimateControlSetTargetHumidity, useClimateControlSetTargetRange, useClusterNodes, useClusterTopology, useColorGetStatus, useColorSetColor, useConfirm, useConnectionTestDescribeTest, useConnectionTestTestSettings, useConnectivityGetStatus, useConsumablesGetStatus, useConsumablesReset, useContactGetStatus, useContainerChildren, useControlGetStatus, useControlSetValue, useCoreBlocksCompile, useCoreBlocksCreate, useCoreBlocksCreateComposition, useCoreBlocksDelete, useCoreBlocksGet, useCoreBlocksGetBlockSettings, useCoreBlocksGetComposition, useCoreBlocksGetCustomization, useCoreBlocksGetTypeDefs, useCoreBlocksList, useCoreBlocksListComposableCaps, useCoreBlocksListCompositions, useCoreBlocksMigrateRawSecrets, useCoreBlocksRestart, useCoreBlocksSetEnabled, useCoreBlocksUpdate, useCoreBlocksUpdateBlockSettings, useCoreBlocksUpdateComposition, useCoreBlocksValidateComposition, useCoverClose, useCoverGetStatus, useCoverOpen, useCoverSetPosition, useCoverSetTiltPosition, useCoverStop, useCustomFieldRenderer, useDataTableFilter, useDayNightGetOptions, useDayNightGetStatus, useDayNightSetSettings, useDebouncedString, useDecoderGetInfo, useDecoderReprobeHwaccel, useDetectionPipelineApplyDeviceSettingsPatch, useDetectionPipelineGetDeviceLiveContribution, useDetectionPipelineGetDeviceSettingsContribution, useDevShell, useDevice, useDeviceAdminLink, useDeviceAdminLinkGetAdminLink, useDeviceAdoptionAdopt, useDeviceAdoptionGetCandidate, useDeviceAdoptionGetStatus, useDeviceAdoptionListCandidateFilters, useDeviceAdoptionListCandidates, useDeviceAdoptionRefresh, useDeviceAdoptionRelease, useDeviceAdoptionResync, useDeviceAutotrack, useDeviceBattery, useDeviceCapSlice, useDeviceCapability, useDeviceCredentialsDeleteDeviceCredential, useDeviceCredentialsGetDeviceCaStatus, useDeviceCredentialsGetDeviceCertificateSettings, useDeviceCredentialsListDeviceCredentials, useDeviceCredentialsRenameDeviceCredential, useDeviceCredentialsRevokeDeviceCredential, useDeviceCredentialsSetDeviceCertificateSettings, useDeviceCredentialsStartEnrollment, useDeviceDetections, useDeviceDiscoveryAdoptDevice, useDeviceDiscoveryGetStatus, useDeviceDiscoveryListDiscovered, useDeviceDiscoveryRefreshDiscovery, useDeviceDiscoveryReleaseDevice, useDeviceExportApplyDeviceSettingsPatch, useDeviceExportExposeDevice, useDeviceExportGetDeviceLiveContribution, useDeviceExportGetDeviceSettingsContribution, useDeviceExportGetStatus, useDeviceExportListExposedDevices, useDeviceExportListSupportedDeviceKinds, useDeviceExportUnexposeDevice, useDeviceExtensionListCandidates, useDeviceExtensionListSlots, useDeviceExtensionListSources, useDeviceExtensionSetSlot, useDeviceHistoryDeclareSeries, useDeviceHistoryGetDeviceEnabled, useDeviceHistoryGetStatus, useDeviceHistoryListNodeSeries, useDeviceHistoryListSeries, useDeviceHistoryQuery, useDeviceHistoryQueryMany, useDeviceHistoryQueryNode, useDeviceHistoryRecord, useDeviceHistoryRecordBuckets, useDeviceHistorySetDeviceEnabled, useDeviceId, useDeviceListPageSize, useDeviceManagerAddLocation, useDeviceManagerAdoptDevice, useDeviceManagerAdoptionAdopt, useDeviceManagerAdoptionCancelJob, useDeviceManagerAdoptionListCandidateFilters, useDeviceManagerAdoptionListCandidates, useDeviceManagerAdoptionListJobs, useDeviceManagerAdoptionRefresh, useDeviceManagerAdoptionRelease, useDeviceManagerAdoptionResync, useDeviceManagerAdoptionStartJob, useDeviceManagerAllocateDeviceId, useDeviceManagerApplyInitialMeta, useDeviceManagerCleanupDeletedDevicesData, useDeviceManagerCreateDevice, useDeviceManagerDisable, useDeviceManagerDiscoverAllProviders, useDeviceManagerDiscoverDevices, useDeviceManagerDiscoverProvider, useDeviceManagerDiscoveryProviders, useDeviceManagerEnable, useDeviceManagerGetAllBindings, useDeviceManagerGetBindings, useDeviceManagerGetBindingsBatch, useDeviceManagerGetChildren, useDeviceManagerGetChildrenBatch, useDeviceManagerGetConfigSchema, useDeviceManagerGetCreationSchema, useDeviceManagerGetDevice, useDeviceManagerGetDeviceAggregate, useDeviceManagerGetDeviceLiveInfoAggregate, useDeviceManagerGetDeviceSettingsAggregate, useDeviceManagerGetDeviceStatusAggregate, useDeviceManagerGetDeviceStatusAggregateBatch, useDeviceManagerGetLinkedDevices, useDeviceManagerGetLinkedDevicesBatch, useDeviceManagerGetRoleDisplayDefaults, useDeviceManagerGetSettingsSchema, useDeviceManagerGetStreamProfileMap, useDeviceManagerGetStreamSources, useDeviceManagerGetWireableFields, useDeviceManagerGetWrapperActivation, useDeviceManagerListAll, useDeviceManagerListBindableCapsForDeviceType, useDeviceManagerListDeletedSince, useDeviceManagerListDeviceIdsByCaps, useDeviceManagerListLocationDescriptions, useDeviceManagerListLocationIcons, useDeviceManagerListLocations, useDeviceManagerListPersistedByAddon, useDeviceManagerListSiteZones, useDeviceManagerListWrappersForCap, useDeviceManagerLoadConfig, useDeviceManagerLoadMeta, useDeviceManagerLoadRuntimeState, useDeviceManagerMigrateDevice, useDeviceManagerPersistConfig, useDeviceManagerProbeStreams, useDeviceManagerProviderCreationType, useDeviceManagerProviderDiscoveryParamsSchema, useDeviceManagerRegisterDevice, useDeviceManagerRemove, useDeviceManagerRemoveByIntegration, useDeviceManagerRemoveDevice, useDeviceManagerRemoveLocation, useDeviceManagerRemoveSiteZone, useDeviceManagerRenameLocation, useDeviceManagerRenameSiteZone, useDeviceManagerRunDeviceAction, useDeviceManagerSetChildLayout, useDeviceManagerSetDisabled, useDeviceManagerSetDisplay, useDeviceManagerSetIntegrationId, useDeviceManagerSetLinkDeviceId, useDeviceManagerSetLocation, useDeviceManagerSetLocationDescription, useDeviceManagerSetLocationIcon, useDeviceManagerSetLocationZone, useDeviceManagerSetMetadata, useDeviceManagerSetName, useDeviceManagerSetPrimaryChildEntityId, useDeviceManagerSetRole, useDeviceManagerSetRoleDisplayDefaults, useDeviceManagerSetSiteZone, useDeviceManagerSetStreamProfileMap, useDeviceManagerSetType, useDeviceManagerSetWrapperActive, useDeviceManagerTestCreationField, useDeviceManagerTestField, useDeviceManagerUpdateConfig, useDeviceManagerUpdateDeviceField, useDeviceManagerUpdateDeviceFieldsBatch, useDeviceNetworkLink, useDeviceOpsGetConfigEntries, useDeviceOpsGetRawState, useDeviceOpsGetSettingsSchema, useDeviceOpsGetStreamSources, useDeviceOpsRemoveDevice, useDeviceOpsRunAction, useDeviceOpsSetConfig, useDeviceProviderAdoptDiscoveredDevice, useDeviceProviderCreateDevice, useDeviceProviderDiscoverDevices, useDeviceProviderGetChildCreationSchema, useDeviceProviderGetDevices, useDeviceProviderGetDiscoveryParamsSchema, useDeviceProviderGetManualCreationType, useDeviceProviderGetStatus, useDeviceProviderReloadDevice, useDeviceProviderStart, useDeviceProviderStop, useDeviceProviderSupportsDiscovery, useDeviceProviderSupportsManualCreation, useDeviceProviderTestCreationField, useDeviceProxy, useDeviceQuickDetailOpen, useDeviceSeriesList, useDeviceSnapshot, useDeviceSnapshotImage, useDeviceState, useDeviceStateClaimFields, useDeviceStateGetAllSnapshots, useDeviceStateGetCapSlice, useDeviceStateGetNativeCapSlice, useDeviceStateGetSnapshot, useDeviceStateListClaims, useDeviceStatePatchOwnedFields, useDeviceStateReleaseClaim, useDeviceStateSetCapSlice, useDeviceStateSlice, useDeviceStatusGetStatus, useDeviceWebrtc, useDevices, useDishwasherGetOptions, useDishwasherGetStatus, useDishwasherPause, useDishwasherResume, useDishwasherSetChildLock, useDishwasherSetExtraDry, useDishwasherSetHalfLoad, useDishwasherSetHygienePlus, useDishwasherSetIntensiveZone, useDishwasherSetOption, useDishwasherSetPower, useDishwasherSetProgram, useDishwasherSetVarioSpeedPlus, useDishwasherStart, useDishwasherStop, useDisplayIconResolver, useDoorbellEvents, useDoorbellGetStatus, useEnumSensorGetStatus, useEventEmitterGetStatus, useEventInvalidation, useEventStreamLatest, useEventStreamMap, useFaceGalleryAssignFace, useFaceGalleryAssignFaces, useFaceGalleryAuditIdentitySamples, useFaceGalleryCreateIdentity, useFaceGalleryDeleteFace, useFaceGalleryDeleteIdentity, useFaceGalleryGetFaceByTrack, useFaceGalleryGetFaceMedia, useFaceGalleryListIdentities, useFaceGalleryListIdentitySamples, useFaceGalleryListRecentFaces, useFaceGalleryRemoveSample, useFaceGalleryRenameIdentity, useFaceGallerySuggestFaceClusters, useFaceGalleryUnassignFace, useFaceGalleryUnassignFaces, useFanControlGetStatus, useFanControlSetDirection, useFanControlSetOscillating, useFanControlSetPercentage, useFanControlSetPreset, useFeatureProbeGetStatus, useFloodGetStatus, useGasGetStatus, useHeatingValveGetStatus, useHistoryPanels, useHistoryReader, useHistoryReads, useHumidifierGetStatus, useHumidifierSetMode, useHumidifierSetOn, useHumidifierSetTargetHumidity, useHumiditySensorGetStatus, useIconGlyphsVersion, useImageGetStatus, useImageSettingsGetOptions, useImageSettingsGetStatus, useImageSettingsSetSettings, useIntegrationsCreate, useIntegrationsDelete, useIntegrationsGet, useIntegrationsGetAvailableTypes, useIntegrationsGetByAddonId, useIntegrationsGetSettings, useIntegrationsList, useIntegrationsSetSettings, useIntegrationsTestConnection, useIntegrationsUpdate, useIntercomEndTalkSession, useIntercomGetStatus, useIntercomHandleAnswer, useIntercomPushTalkAudio, useIntercomStartSession, useIntercomStartTalkSession, useIntercomStopSession, useIsMidWidth, useIsMobile, useItemFormUrl, useKeychainAdminAcknowledgeReset, useKeychainAdminDeleteOrphans, useKeychainAdminDownloadRecoveryKey, useKeychainAdminImportSecret, useKeychainAdminListAudit, useKeychainAdminListSecrets, useKeychainAdminPurgeRetiredKeys, useKeychainAdminResetKeychain, useKeychainAdminResetReport, useKeychainAdminReveal, useKeychainAdminRotateMasterKey, useKeychainAdminSetSystemSecret, useKeychainAdminStatus, useLawnMowerControlDock, useLawnMowerControlGetStatus, useLawnMowerControlPause, useLawnMowerControlStartMowing, useLiveBuffer, useLiveEvent, useLlmCancel, useLlmDeleteModel, useLlmDeleteProfile, useLlmGenerate, useLlmGenerateVision, useLlmGetDefaults, useLlmGetRuntimeStatus, useLlmGetUsage, useLlmInstallModel, useLlmListModelCatalog, useLlmListModels, useLlmListNodeModels, useLlmListProfileKinds, useLlmListProfiles, useLlmListRuntimeNodes, useLlmMigrateRawSecrets, useLlmResolveModelRef, useLlmSetDefault, useLlmStartRuntime, useLlmStopRuntime, useLlmTestProfile, useLlmUpsertProfile, useLocalNetworkAddCustomAddress, useLocalNetworkDownloadCa, useLocalNetworkGetAddresses, useLocalNetworkGetAllowedAddresses, useLocalNetworkGetConnectionEndpoints, useLocalNetworkGetHubAddresses, useLocalNetworkGetNotificationEndpoint, useLocalNetworkGetPreferred, useLocalNetworkGetTlsStatus, useLocalNetworkGetViewerEndpoints, useLocalNetworkList, useLocalNetworkRegenerateCertificate, useLocalNetworkRemoveCustomAddress, useLocalNetworkResetAllowlistToBestMatch, useLocalNetworkRevertToGeneratedCertificate, useLocalNetworkSetAddressRole, useLocalNetworkSetAllowedAddresses, useLocalNetworkSetNotificationEndpoint, useLocalNetworkSetViewerEndpoints, useLocalNetworkUploadCertificate, useLocalNetworkUseStoredUploadedCertificate, useLockControlGetStatus, useLockControlLock, useLockControlOpen, useLockControlUnlock, useMediaPlayerGetStatus, useMediaPlayerNext, useMediaPlayerPause, useMediaPlayerPlay, useMediaPlayerPlayMedia, useMediaPlayerPrevious, useMediaPlayerSeek, useMediaPlayerSelectSource, useMediaPlayerSetMute, useMediaPlayerSetRepeat, useMediaPlayerSetShuffle, useMediaPlayerSetVolume, useMediaPlayerStop, useMeshNetworkGetStatus, useMeshNetworkJoin, useMeshNetworkLeave, useMeshNetworkListPeers, useMeshNetworkLogout, useMeshNetworkStartLogin, useMeshNetworkTestConnection, useMetricsProviderCollectSnapshot, useMetricsProviderGetAddonStats, useMetricsProviderGetCached, useMetricsProviderGetCpuTemperature, useMetricsProviderGetCurrent, useMetricsProviderGetDiskSpace, useMetricsProviderGetGpuInfo, useMetricsProviderGetLoadSeries, useMetricsProviderGetProcessStats, useMetricsProviderListAddonInstances, useMetricsProviderListNodeProcesses, useMotionDetectionAnalyze, useMotionDetectionApplyDeviceSettingsPatch, useMotionDetectionGetDeviceLiveContribution, useMotionDetectionGetDeviceSettingsContribution, useMotionDetectionRemoveCamera, useMotionDetectionReset, useMotionGetStatus, useMotionIsDetected, useMotionTriggerGetStatus, useMotionTriggerSetMotionTrigger, useMotionZonesGetOptions, useMotionZonesGetStatus, useMotionZonesSetZone, useMqttBrokerAddBroker, useMqttBrokerGetBrokerConfig, useMqttBrokerGetStatus, useMqttBrokerListBrokers, useMqttBrokerRemoveBroker, useMqttBrokerStartEmbeddedBroker, useMqttBrokerStopEmbeddedBroker, useMqttBrokerTestConnection, useNativeObjectDetectionGetOptions, useNativeObjectDetectionGetStatus, useNativeObjectDetectionSetEnabled, useNativeObjectDetectionSetSettings, useNavigation, useNavigationGetFeatures, useNavigationGetStatus, useNavigationGoToPoint, useNavigationListActions, useNavigationMove, useNavigationPlaySound, useNavigationRunAction, useNavigationSetLightLevel, useNavigationSetLightMode, useNavigationSetLightOn, useNavigationStop, useNetworkAccessGetEndpoint, useNetworkAccessGetStatus, useNetworkAccessListEndpoints, useNetworkAccessStart, useNetworkAccessStop, useNetworkLinkGetStatus, useNetworkQualityGetAllStats, useNetworkQualityGetDeviceStats, useNetworkQualityReportClientStats, useNodesClusterAddonStatus, useNodesDeployAddon, useNodesDirectory, useNodesExecuteQuery, useNodesGetCapUsageGraph, useNodesGetNodeAddons, useNodesRenameNode, useNodesRestartAddon, useNodesRestartNode, useNodesRestartProcess, useNodesSetProcessLogLevel, useNodesShutdownNode, useNodesTopology, useNodesUndeployAddon, useNotificationOutputDeleteTarget, useNotificationOutputDiscoverTargets, useNotificationOutputGetTarget, useNotificationOutputListTargetKinds, useNotificationOutputListTargets, useNotificationOutputProbeTargetConfig, useNotificationOutputSend, useNotificationOutputSetTargetEnabled, useNotificationOutputTestTarget, useNotificationOutputUpsertTarget, useNotificationRulesActivateAlarmProfile, useNotificationRulesCancelSnooze, useNotificationRulesCreateAlarmProfile, useNotificationRulesCreateRule, useNotificationRulesCreateSequence, useNotificationRulesCreateSnooze, useNotificationRulesDeleteAlarmProfile, useNotificationRulesDeleteRule, useNotificationRulesDeleteSequence, useNotificationRulesGetAlarmConfig, useNotificationRulesGetAlarmSensors, useNotificationRulesGetClipRetentionAsks, useNotificationRulesGetConditionCatalog, useNotificationRulesGetHistory, useNotificationRulesGetRule, useNotificationRulesGetRuleLastRuns, useNotificationRulesGetSequence, useNotificationRulesGetTemplateCatalog, useNotificationRulesListDeviceMutes, useNotificationRulesListRules, useNotificationRulesListSequences, useNotificationRulesListSnoozes, useNotificationRulesPreviewStateSummary, useNotificationRulesPreviewTemplate, useNotificationRulesRenameAlarmProfile, useNotificationRulesResolveArtifactUrl, useNotificationRulesSetAlarmConfig, useNotificationRulesSetDeviceMuted, useNotificationRulesSetRuleEnabled, useNotificationRulesTestRule, useNotificationRulesUpdateRule, useNotificationRulesUpdateSequence, useNotifierCancel, useNotifierGetStatus, useNotifierSend, useNumericSensorGetStatus, useOidcConnectionsConnectionFormSchema, useOidcConnectionsCreateConnection, useOidcConnectionsDeleteConnection, useOidcConnectionsListConnections, useOidcConnectionsUpdateConnection, useOptimisticSlice, useOptionalSystem, useOptionalWidgetRegistry, useOsdGetStatus, useOsdManagerClearSlotBinding, useOsdManagerCopyDeviceConfiguration, useOsdManagerDeleteTemplate, useOsdManagerGetConditionSupport, useOsdManagerGetDeviceOsd, useOsdManagerGetSourceCatalog, useOsdManagerListTemplates, useOsdManagerPreviewSlot, useOsdManagerRenderDevice, useOsdManagerSetSlotBinding, useOsdManagerSetSlotCameraEnabled, useOsdManagerUpsertTemplate, useOsdSetOverlay, usePTZ, usePersistedPageSize, usePetFeederCallPet, usePetFeederCancelFeed, usePetFeederFeed, usePetFeederGetStatus, usePetFeederMarkFoodReplenished, usePetFeederPlaySound, usePetFeederResetDesiccant, usePetFeederSetChildLock, usePetFeederSetFeedSound, usePetFeederSetIndicatorLight, usePetFeederSetVolume, usePickerSiteZones, usePipelineAnalyticsAddRetrainFrameFromRecording, usePipelineAnalyticsAddRetrainFramesToCollection, usePipelineAnalyticsApplyDeviceSettingsPatch, usePipelineAnalyticsBackfillRetrainOrigins, usePipelineAnalyticsCancelRelocateMedia, usePipelineAnalyticsCancelStorageMigrationMove, usePipelineAnalyticsClearTrackRelation, usePipelineAnalyticsClearTracks, usePipelineAnalyticsCompleteRetrainTrack, usePipelineAnalyticsCountRelocatableMedia, usePipelineAnalyticsCountUnstampedEventMedia, usePipelineAnalyticsCreateRetrainCollection, usePipelineAnalyticsDeleteDeviceEvents, usePipelineAnalyticsDeleteRetrainCollection, usePipelineAnalyticsDeleteTracks, usePipelineAnalyticsDeselectRetrainFrame, usePipelineAnalyticsGetActiveTracks, usePipelineAnalyticsGetAudioEvents, usePipelineAnalyticsGetDeviceLiveContribution, usePipelineAnalyticsGetDeviceSettingsContribution, usePipelineAnalyticsGetEventDensity, usePipelineAnalyticsGetEventDensityBatch, usePipelineAnalyticsGetEventMedia, usePipelineAnalyticsGetEventMediaFootprintByKind, usePipelineAnalyticsGetEventStoreFootprint, usePipelineAnalyticsGetKeyEvents, usePipelineAnalyticsGetKeyEventsBatch, usePipelineAnalyticsGetMediaReclaimStatus, usePipelineAnalyticsGetMotionEvents, usePipelineAnalyticsGetObjectEmbeddingRebuildStatus, usePipelineAnalyticsGetObjectEvents, usePipelineAnalyticsGetRetrainCorpusKpi, usePipelineAnalyticsGetRetrainExportUrl, usePipelineAnalyticsGetRetrainFrameImage, usePipelineAnalyticsGetSensorEvents, usePipelineAnalyticsGetStorageMigrationMoveStatus, usePipelineAnalyticsGetTrack, usePipelineAnalyticsGetTrackMedia, usePipelineAnalyticsGetTrainingExportSummary, usePipelineAnalyticsGetTrainingExportUrl, usePipelineAnalyticsListArchivedDebugNotes, usePipelineAnalyticsListBirthDecisions, usePipelineAnalyticsListEventKinds, usePipelineAnalyticsListEventKindsBatch, usePipelineAnalyticsListEventMedia, usePipelineAnalyticsListOpsLog, usePipelineAnalyticsListRecentTracks, usePipelineAnalyticsListRelocateMediaJobs, usePipelineAnalyticsListRetrainAnnotations, usePipelineAnalyticsListRetrainAssistProfiles, usePipelineAnalyticsListRetrainCollectionFrames, usePipelineAnalyticsListRetrainCollections, usePipelineAnalyticsListRetrainCorpusPage, usePipelineAnalyticsListRetrainFrames, usePipelineAnalyticsListRetrainStaging, usePipelineAnalyticsListTrackMedia, usePipelineAnalyticsListTrackRelations, usePipelineAnalyticsListTracks, usePipelineAnalyticsOfferRetrainArchive, usePipelineAnalyticsOwnersWithMedia, usePipelineAnalyticsPauseForStorageMigration, usePipelineAnalyticsProposeRetrainAnnotations, usePipelineAnalyticsProposeRetrainAnnotationsWithLlm, usePipelineAnalyticsPruneEvents, usePipelineAnalyticsPruneEventsBefore, usePipelineAnalyticsPruneTracksBefore, usePipelineAnalyticsReadLongTermSeries, usePipelineAnalyticsRebuildObjectEmbeddings, usePipelineAnalyticsReclaimDebugMedia, usePipelineAnalyticsReconcileFromDisk, usePipelineAnalyticsRefreshStorageLocationsForMigration, usePipelineAnalyticsRelocateMedia, usePipelineAnalyticsRemoveRetrainFrameFromCollection, usePipelineAnalyticsRenameRetrainCollection, usePipelineAnalyticsResolveRetrainTrainingCorpus, usePipelineAnalyticsRestageRetrainTrack, usePipelineAnalyticsResumeForStorageMigration, usePipelineAnalyticsSaveRetrainAnnotations, usePipelineAnalyticsSearchObjectEvents, usePipelineAnalyticsSelectRetrainFrames, usePipelineAnalyticsSetRetrainFrameComplete, usePipelineAnalyticsSetTrackFlags, usePipelineAnalyticsSetTrackPurity, usePipelineAnalyticsSetTrackRelation, usePipelineAnalyticsStartStorageMigrationMove, usePipelineAnalyticsWipeAllAnalytics, usePipelineAnalyticsWipeObjectEmbeddings, usePipelineAnalyticsWriteRetrainArchive, usePipelineExecutorCacheFrameInPool, usePipelineExecutorClearDeviceOverrides, usePipelineExecutorClearModelBoundDeviceOverrides, usePipelineExecutorDeleteModel, usePipelineExecutorDownloadModel, usePipelineExecutorGetAddonModels, usePipelineExecutorGetAudioCapabilities, usePipelineExecutorGetAvailableEngines, usePipelineExecutorGetCapabilities, usePipelineExecutorGetDefaultSteps, usePipelineExecutorGetDetectionConfigSchema, usePipelineExecutorGetEffectiveTuning, usePipelineExecutorGetEngineProvisioning, usePipelineExecutorGetGlobalPipelineConfig, usePipelineExecutorGetGlobalSteps, usePipelineExecutorGetInferenceDeviceHealth, usePipelineExecutorGetOrchestratorConfigSchema, usePipelineExecutorGetReferenceAudio, usePipelineExecutorGetReferenceAudioFiles, usePipelineExecutorGetReferenceImage, usePipelineExecutorGetSchema, usePipelineExecutorGetSelectedEngine, usePipelineExecutorGetVideoPipelineSteps, usePipelineExecutorInferCached, usePipelineExecutorKillEngine, usePipelineExecutorListLoadedEngines, usePipelineExecutorListReferenceImages, usePipelineExecutorRearmInferenceDevice, usePipelineExecutorRunAudioTest, usePipelineExecutorRunPipeline, usePipelineExecutorRunPipelineBatch, usePipelineExecutorSetVideoPipelineSteps, usePipelineExecutorSpinEngine, usePipelineExecutorUncacheFrame, usePipelineExecutorValidatePipeline, usePipelineOrchestratorAbortNodeBenchmark, usePipelineOrchestratorApplyAutoTune, usePipelineOrchestratorApplyDeviceSettingsPatch, usePipelineOrchestratorAssignAudio, usePipelineOrchestratorAssignPipeline, usePipelineOrchestratorDeleteNodeBenchmarkResult, usePipelineOrchestratorGetAgentLoad, usePipelineOrchestratorGetAgentSettings, usePipelineOrchestratorGetAudioAssignment, usePipelineOrchestratorGetAudioAssignments, usePipelineOrchestratorGetAudioNodeLoad, usePipelineOrchestratorGetCameraDetectionSource, usePipelineOrchestratorGetCameraMetrics, usePipelineOrchestratorGetCameraSettings, usePipelineOrchestratorGetCameraStatus, usePipelineOrchestratorGetCameraStatuses, usePipelineOrchestratorGetCameraStepOverrides, usePipelineOrchestratorGetCameraSwitches, usePipelineOrchestratorGetCapabilityBindings, usePipelineOrchestratorGetClusterModelChoice, usePipelineOrchestratorGetDeviceLiveContribution, usePipelineOrchestratorGetDeviceSettingsContribution, usePipelineOrchestratorGetEffectiveShares, usePipelineOrchestratorGetGlobalMetrics, usePipelineOrchestratorGetIngestOwner, usePipelineOrchestratorGetNodeBenchmarkCandidates, usePipelineOrchestratorGetNodeBenchmarkPreflight, usePipelineOrchestratorGetNodeBenchmarkStatus, usePipelineOrchestratorGetNodeInferenceDevices, usePipelineOrchestratorGetPipelineAssignment, usePipelineOrchestratorGetPipelineAssignments, usePipelineOrchestratorGetPipelineDevicePin, usePipelineOrchestratorGetPlacementStats, usePipelineOrchestratorGetReconcileFromDiskStatus, usePipelineOrchestratorListAgentSettings, usePipelineOrchestratorListNodeBenchmarkResults, usePipelineOrchestratorPauseForStorageMigration, usePipelineOrchestratorPlanAutoTune, usePipelineOrchestratorRebalance, usePipelineOrchestratorReconcileFromDisk, usePipelineOrchestratorRemoveAgentSettings, usePipelineOrchestratorRequestOccupancyLook, usePipelineOrchestratorResetNodePipelineDefaults, usePipelineOrchestratorResolvePipeline, usePipelineOrchestratorResumeForStorageMigration, usePipelineOrchestratorSetAgentCapabilities, usePipelineOrchestratorSetAgentDecodeLimits, usePipelineOrchestratorSetAgentInferenceDevices, usePipelineOrchestratorSetAgentMaxCameras, usePipelineOrchestratorSetAgentReachableHost, usePipelineOrchestratorSetCameraDetectionSource, usePipelineOrchestratorSetCameraPipelineForAgent, usePipelineOrchestratorSetCameraSensors, usePipelineOrchestratorSetCameraStepOverride, usePipelineOrchestratorSetCameraStepSettings, usePipelineOrchestratorSetCameraStepToggle, usePipelineOrchestratorSetCameraSwitch, usePipelineOrchestratorSetCapabilityBinding, usePipelineOrchestratorSetLoadBalancingShares, usePipelineOrchestratorSetPipelineDevicePin, usePipelineOrchestratorSetShareSource, usePipelineOrchestratorStartNodeBenchmark, usePipelineOrchestratorSuggestShares, usePipelineOrchestratorUnassignAudio, usePipelineOrchestratorUnassignPipeline, usePipelineRunnerAbortNodeBench, usePipelineRunnerAttachCamera, usePipelineRunnerDetachCamera, usePipelineRunnerGetAllCameraMetrics, usePipelineRunnerGetCameraMetrics, usePipelineRunnerGetLocalCameras, usePipelineRunnerGetLocalLoad, usePipelineRunnerGetLocalMetrics, usePipelineRunnerGetNativeCrop, usePipelineRunnerGetNodeBenchStatus, usePipelineRunnerGetParkedTrackFrame, usePipelineRunnerParkTrackFrame, usePipelineRunnerReleaseParkedTrackFrames, usePipelineRunnerReportMotion, usePipelineRunnerRequestOccupancyBurst, usePipelineRunnerRunDetailSubtree, usePipelineRunnerRunStatelessStep, usePipelineRunnerStartNodeBench, usePlateGalleryAssignPlate, usePlateGalleryAssignPlates, usePlateGalleryCorrectPlateText, usePlateGalleryCreateVehicle, usePlateGalleryDeletePlate, usePlateGalleryDeleteVehicle, usePlateGalleryGetPlateByTrack, usePlateGalleryGetPlateMedia, usePlateGalleryListPlates, usePlateGalleryListVehicleSamples, usePlateGalleryListVehicles, usePlateGalleryRemoveVehicleSample, usePlateGalleryRenameVehicle, usePlateGallerySearchPlates, usePlateGallerySuggestPlateClusters, usePlateGalleryUnassignPlate, usePlateGalleryUnassignPlates, usePlayerOverlayLayer, usePlayerOverlayLayers, usePlayerToolbarButton, usePlayerToolbarButtons, usePowerMeterGetStatus, usePresenceGetStatus, usePressureSensorGetStatus, usePrivacyMaskGetOptions, usePrivacyMaskGetStatus, usePrivacyMaskSetAudioEnabled, usePrivacyMaskSetMask, usePrompt, usePtzAutotrackGetSettings, usePtzAutotrackGetStatus, usePtzAutotrackSetEnabled, usePtzAutotrackSetSettings, usePtzCaptureHomeHere, usePtzContinuousMove, usePtzDeletePreset, usePtzGetHomePreset, usePtzGetHomeReturn, usePtzGetOptions, usePtzGetPosition, usePtzGetPresets, usePtzGetStatus, usePtzGoHome, usePtzGoToPreset, usePtzMove, usePtzSavePreset, usePtzSetAutofocus, usePtzSetHomePreset, usePtzSetHomeReturn, usePtzStop, useRebootReboot, useRecordEntriesDeleteEntry, useRecordEntriesListEntries, useRecordEntriesListEntryKinds, useRecordEntriesPutEntry, useRecordedPlayback, useRecordingArchiveApplyDeviceSettingsPatch, useRecordingArchiveCancelRelocateJob, useRecordingArchiveCancelStorageMigrationMove, useRecordingArchiveDeleteFootprint, useRecordingArchiveGetAvailabilityBatch, useRecordingArchiveGetDaysWithRecordingsBatch, useRecordingArchiveGetDeviceConfig, useRecordingArchiveGetDeviceLiveContribution, useRecordingArchiveGetDeviceSettingsContribution, useRecordingArchiveGetPlacement, useRecordingArchiveGetRelocateResidue, useRecordingArchiveGetStatus, useRecordingArchiveGetStorageMigrationMoveStatus, useRecordingArchiveGetStorageUsage, useRecordingArchiveListOpsLog, useRecordingArchiveListRelocateJobs, useRecordingArchiveLocateSegment, useRecordingArchivePauseForStorageMigration, useRecordingArchivePlanStorageRebalance, useRecordingArchivePruneFootage, useRecordingArchiveReadGopBytes, useRecordingArchiveReadSegmentBytes, useRecordingArchiveReadWindowBytes, useRecordingArchiveReconcileLedgerAgainstDisk, useRecordingArchiveRefreshStorageLocationsForMigration, useRecordingArchiveRelocateFootage, useRecordingArchiveRenderClip, useRecordingArchiveRenderGif, useRecordingArchiveRescanStorage, useRecordingArchiveResumeForStorageMigration, useRecordingArchiveSetDeviceConfig, useRecordingArchiveSetDevicePlacement, useRecordingArchiveStartStorageMigrationMove, useRecordingArchiveStartStorageRebalance, useRecordingExportCancelExport, useRecordingExportCreateExport, useRecordingExportDeleteExport, useRecordingExportGetDownloadUrl, useRecordingExportGetExport, useRecordingExportListExports, useRecordingExportReadExportBytes, useRecordingGetAvailability, useRecordingGetDaysWithRecordings, useRecordingGetPlayback, useRecordingGetPlaybackOptions, useRecordingListSources, useRecordingOnboardGetOptions, useRecordingOnboardGetStatus, useRecordingOnboardSetSettings, useRecordingSignalGetStatus, useRemoteComponent, useRoleDisplayDefaults, useSceneMonitorCaptureReference, useSceneMonitorCreateScene, useSceneMonitorDeleteReference, useSceneMonitorDeleteScene, useSceneMonitorGetStatus, useSceneMonitorListScenes, useSceneMonitorListScenesBatch, useSceneMonitorRecheckNow, useSceneMonitorResetScene, useSceneMonitorUpdateScene, useScriptRunnerGetStatus, useScriptRunnerRun, useScriptRunnerStop, useScrubController, useServerManagementApplyServerUpdate, useServerManagementCheckServerUpdate, useServerManagementGetServerPackageStatus, useServerManagementRestartServer, useServerManagementRollbackServerUpdate, useSettingsStoreAggregate, useSettingsStoreCount, useSettingsStoreDeclareCollection, useSettingsStoreDelete, useSettingsStoreDeleteWhere, useSettingsStoreGet, useSettingsStoreHistogram, useSettingsStoreInsert, useSettingsStoreInsertMany, useSettingsStoreIsEmpty, useSettingsStoreQuery, useSettingsStoreSet, useSettingsStoreTransact, useSettingsStoreUpdate, useSettingsStoreUpdateWhere, useSettingsStoreUpsert, useSettingsStoreUpsertMany, useSmokeGetStatus, useSnapshotApplyDeviceSettingsPatch, useSnapshotGetDebugState, useSnapshotGetDeviceLiveContribution, useSnapshotGetDeviceSettingsContribution, useSnapshotGetSnapshot, useSnapshotGetSnapshotLinks, useSnapshotGetSnapshotOverview, useSnapshotGetStatus, useSnapshotInvalidateCache, useStationaryOverlay, useStorageAbortUpload, useStorageBeginDownload, useStorageBeginUpload, useStorageDelete, useStorageDeleteLocation, useStorageEndDownload, useStorageExists, useStorageFinalizeUpload, useStorageGetAvailableSpace, useStorageList, useStorageListDrainProgress, useStorageListLocationDeclarations, useStorageListLocations, useStorageListProviders, useStorageMigrationCancel, useStorageMigrationCleanupCancel, useStorageMigrationCleanupStart, useStorageMigrationCleanupStatus, useStorageMigrationDrain, useStorageMigrationHistory, useStorageMigrationMoverClearance, useStorageMigrationMovers, useStorageMigrationPlan, useStorageMigrationResidue, useStorageMigrationStart, useStorageMigrationStatus, useStorageRead, useStorageReadChunk, useStorageResolve, useStorageTestConfig, useStorageTestLocation, useStorageUpsertLocation, useStorageWrite, useStorageWriteChunk, useStoredGroupBy, useStreamBrokerAcquireEgressTranscode, useStreamBrokerApplyDeviceSettingsPatch, useStreamBrokerAssignProfile, useStreamBrokerFetchEventMedia, useStreamBrokerForgetDeviceHardware, useStreamBrokerGetAllRtspEntries, useStreamBrokerGetBrokerStats, useStreamBrokerGetDeviceAudioMute, useStreamBrokerGetDeviceLiveContribution, useStreamBrokerGetDeviceSettingsContribution, useStreamBrokerGetLiveViewerCounts, useStreamBrokerGetPreBufferInfo, useStreamBrokerGetRtspEntry, useStreamBrokerGetRtspPort, useStreamBrokerGetStreamUrl, useStreamBrokerGetStreamWithCodec, useStreamBrokerIsRtspEnabled, useStreamBrokerKillClient, useStreamBrokerListAllCameraStreams, useStreamBrokerListAllProfileSlots, useStreamBrokerListClients, useStreamBrokerProbeStream, useStreamBrokerProduceEventMedia, useStreamBrokerPublishCameraStream, useStreamBrokerPullAudioChunks, useStreamBrokerRegenerateRtspToken, useStreamBrokerReleaseEgressTranscode, useStreamBrokerReleaseStreamWithCodec, useStreamBrokerRenderPreBufferClip, useStreamBrokerReportStreamSignal, useStreamBrokerRestartProfile, useStreamBrokerRetractCameraStream, useStreamBrokerSetDeviceAudioMute, useStreamBrokerSetPreBufferDuration, useStreamBrokerSetRtspEnabled, useStreamBrokerSubscribeAudioChunks, useStreamBrokerUnassignProfile, useStreamBrokerUnsubscribeAudioChunks, useStreamCatalogGetCatalog, useStreamParamsGetConfigSchema, useStreamParamsGetOptions, useStreamParamsGetStatus, useStreamParamsSetProfile, useStreamSignalsListSignals, useSwitchGetStatus, useSwitchSetState, useSystem, useSystemDetectSiteLocation, useSystemFeatureFlags, useSystemForceRetentionCleanup, useSystemGetFailureContributions, useSystemGetLoadContributions, useSystemGetLoggingSettings, useSystemGetRequestCensus, useSystemGetRetentionConfig, useSystemGetSiteLocation, useSystemHealth, useSystemInfo, useSystemMutation, useSystemNetworkAddresses, useSystemQuery, useSystemSetLoggingSettings, useSystemSetRetentionConfig, useSystemSetSiteLocation, useTamperGetStatus, useTemperatureSensorGetStatus, useTerminalSessionAdoptLegacyMonitor, useTerminalSessionClose, useTerminalSessionCreateInstance, useTerminalSessionDeleteInstance, useTerminalSessionListInstances, useTerminalSessionListLegacyCameras, useTerminalSessionListProfiles, useTerminalSessionListSessions, useTerminalSessionOpenSession, useTerminalSessionPullOutput, useTerminalSessionResize, useTerminalSessionSetInstanceEnabled, useTerminalSessionUpdateInstance, useTerminalSessionWriteInput, useThemeMode, useToastOnToast, useTrainingCanaryResult, useTrainingCancelJob, useTrainingDiscardResult, useTrainingGetJob, useTrainingGetJobResult, useTrainingListBaseModels, useTrainingListJobs, useTrainingPlanJob, useTrainingRegisterResult, useTrainingRollbackCanary, useTrainingStartJob, useTrainingStartJobBatch, useTurnProviderGetTurnServers, useUiText, useUpdateGetStatus, useUpdateInstallUpdate, useUrlQueryParam, useUserManagementConfirmTotp, useUserManagementCreateUser, useUserManagementDeleteUser, useUserManagementDisableTotp, useUserManagementFindUserByExternalIdentity, useUserManagementGetTotpStatus, useUserManagementListApiKeys, useUserManagementListExternalIdentities, useUserManagementListOauthSessions, useUserManagementListUsers, useUserManagementMigrateRawSecrets, useUserManagementOauthExchangeCode, useUserManagementOauthIssueCode, useUserManagementOauthRefresh, useUserManagementOauthVerifyAccessToken, useUserManagementResetPassword, useUserManagementRevokeApiKey, useUserManagementRevokeOauthSession, useUserManagementSetUserScopes, useUserManagementSetupTotp, useUserManagementUnlinkExternalIdentity, useUserManagementUpdateUser, useUserManagementValidateCredentials, useUserManagementVerifyTotp, useVacuumControlGetStatus, useVacuumControlLocate, useVacuumControlPause, useVacuumControlReturnToBase, useVacuumControlSetFanSpeed, useVacuumControlStart, useVacuumControlStop, useValveClose, useValveGetStatus, useValveOpen, useValveSetPosition, useValveStop, useVibrationGetStatus, useVideoclipsDialClipStream, useVideoclipsGetClipPlayback, useVideoclipsGetPlaybackOptions, useVideoclipsListClips, useVideoclipsListSources, useVideoclipsOfferClipBytes, useVideoclipsReadClipBytes, useVodPlayback, useWaterHeaterGetStatus, useWaterHeaterSetAway, useWaterHeaterSetOperationMode, useWaterHeaterSetTargetTemp, useWeatherGetStatus, useWebrtcSessionAddIceCandidate, useWebrtcSessionApplyDownlinkProbe, useWebrtcSessionCloseSession, useWebrtcSessionCreateSession, useWebrtcSessionGetIceCandidates, useWebrtcSessionGetSessionDebug, useWebrtcSessionGetSessionState, useWebrtcSessionHandleAnswer, useWebrtcSessionHandleOffer, useWebrtcSessionHasAdaptiveBitrate, useWebrtcSessionListStreams, useWidget, useWidgetMetadata, useWidgetRegistry, useZoneAnalyticsGetCameraHistory, useZoneAnalyticsGetCurrentSnapshot, useZoneAnalyticsGetCurrentSnapshotBatch, useZoneAnalyticsGetUnzonedHistory, useZoneAnalyticsGetZoneHistory, useZoneEditing, useZoneRulesListRules, useZoneRulesSetRules, useZonesAddZone, useZonesListZones, useZonesRemoveZone, useZonesUpdateZone, Route as ut, Users as v, vacuumStateMeta, validateScopes, valveStateMeta, variantLabel, Hammer as vn, ChartLine as vr, Puzzle as vt, Type as w, waterHeaterPhase, waterHeaterTint, weatherConditionMeta, weatherTint, withHiddenChart, withPanel, withResolvedParentDetail, withUseRootModel, withoutPanel, Funnel as wn, Bus as wr, writeStoredGroupBy, Plane as wt, Upload as x, GraduationCap as xn, Car as xr, Plug as xt, User as y, GripHorizontal as yn, Cat as yr, Power as yt, Sun as z, Download as zn, Bath as zr, Moon as zt };