@camstack/addon-post-analysis 1.2.655 → 1.2.657

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 (16) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{clip-model-registry-_b3KHYFm.mjs → clip-model-registry-OHEqXA-p.mjs} +2439 -1117
  3. package/dist/{clip-model-registry-C-en5vSD.js → clip-model-registry-zi2g3vrD.js} +2438 -1116
  4. package/dist/embedding-encoder/index.js +5 -5
  5. package/dist/embedding-encoder/index.mjs +4 -4
  6. package/dist/pipeline-analytics/_stub.js +2 -2
  7. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CF-Mts9j.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-B15uv0HK.mjs} +3 -3
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-1DyKMo-9.mjs +26 -0
  9. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BwWOFFOc.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-BSfz5s98.mjs → hostInit-BAuJ8QZH.mjs} +3 -3
  11. package/dist/pipeline-analytics/index.js +25 -7
  12. package/dist/pipeline-analytics/index.mjs +24 -6
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +10 -3
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-C7OJ7wek.mjs +0 -26
  16. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DXQUFp7P.mjs +0 -26
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- //#region ../types/dist/event-category-DmZDPDf_.mjs
2
+ //#region ../types/dist/event-category-CaURIwQA.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -715,6 +715,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
715
715
  EventCategory["NotificationRuleBroken"] = "notification.rule-broken";
716
716
  /** The clear of {@link NotificationRuleBroken}: the rule loads and names nothing deleted. */
717
717
  EventCategory["NotificationRuleRepaired"] = "notification.rule-repaired";
718
+ /** A migration failed or was blocked; the Alert Center re-reads the ledger. */
719
+ EventCategory["MigrationFailed"] = "migration.failed";
720
+ /** A migration applied or a failure cleared; the Alert Center re-reads the ledger. */
721
+ EventCategory["MigrationApplied"] = "migration.applied";
718
722
  EventCategory["DeviceUpdated"] = "device.updated";
719
723
  /**
720
724
  * Transport-level connectivity. Emitted by the device driver when
@@ -777,6 +781,378 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
777
781
  return EventCategory;
778
782
  }({});
779
783
  //#endregion
784
+ //#region ../types/dist/device-type-CEqjmUnR.mjs
785
+ /**
786
+ import { errMsg } from '@camstack/types'
787
+ * Extract a human-readable message from an unknown error value.
788
+ * Replaces the ubiquitous `errMsg(err)` pattern.
789
+ */
790
+ function errMsg(err) {
791
+ if (err instanceof Error) return err.message;
792
+ if (typeof err === "string") return err;
793
+ return String(err);
794
+ }
795
+ var DeviceType = /* @__PURE__ */ function(DeviceType) {
796
+ DeviceType["Camera"] = "camera";
797
+ DeviceType["Hub"] = "hub";
798
+ DeviceType["Light"] = "light";
799
+ DeviceType["Siren"] = "siren";
800
+ DeviceType["Switch"] = "switch";
801
+ DeviceType["Sensor"] = "sensor";
802
+ DeviceType["Thermostat"] = "thermostat";
803
+ /** Air-conditioner / heat-pump climate device (HVAC) — shares the
804
+ * `climate-control` cap surface with `Thermostat` but renders a
805
+ * dedicated AC-appropriate control UI (mode chips, fan speed,
806
+ * independent vertical/horizontal swing). Sources: native Gree, and
807
+ * reusable by other AC integrations. */
808
+ DeviceType["Climate"] = "climate";
809
+ DeviceType["Button"] = "button";
810
+ /** Generic stateless event emitter — carries a device's EXACT declared
811
+ * event vocabulary verbatim (no normalization). Installed with the
812
+ * `event-emitter` cap. Sources: HA `event.*` entities (structured) and
813
+ * HA bus events (e.g. `zha_event`, generic). */
814
+ DeviceType["EventEmitter"] = "event-emitter";
815
+ /** Firmware/software update entity — current vs available version,
816
+ * updatable flag, update state, and an install action. Installed with
817
+ * the `update` cap. Sources: Homematic firmware-update channels (and
818
+ * reusable by other providers, e.g. HA `update.*` entities). */
819
+ DeviceType["Update"] = "update";
820
+ DeviceType["Generic"] = "generic";
821
+ /** Generic notification delivery target (HA `notify.<service>`, future
822
+ * Telegram / Discord / ntfy / SMTP, …). One device per delivery
823
+ * endpoint; the `notifier` cap defines the send surface. */
824
+ DeviceType["Notifier"] = "notifier";
825
+ /** Pre-recorded action sequence with optional parameters
826
+ * (HA `script.*`). Runnable via `script-runner` cap. */
827
+ DeviceType["Script"] = "script";
828
+ /** Automation rule (HA `automation.*`) — enable/disable + manual
829
+ * trigger surface exposed via `automation-control` cap. */
830
+ DeviceType["Automation"] = "automation";
831
+ /** Door / smart lock device (HA `lock.*`). `lock-control` cap. */
832
+ DeviceType["Lock"] = "lock";
833
+ /** Window covering, blinds, garage door, valve, etc. (HA `cover.*`,
834
+ * `valve.*`). `cover` cap with sub-roles for variant. */
835
+ DeviceType["Cover"] = "cover";
836
+ /** Pipe / water / gas valve with open/close/stop and optional
837
+ * position (HA `valve.*`). `valve` cap — a cover-sibling actuator
838
+ * modelled on the same open/closed lifecycle. */
839
+ DeviceType["Valve"] = "valve";
840
+ /** Humidifier / dehumidifier with on/off + target humidity + mode
841
+ * (HA `humidifier.*`). `humidifier` cap — a climate-family actuator
842
+ * modelled on the same target / mode lifecycle. */
843
+ DeviceType["Humidifier"] = "humidifier";
844
+ /** Water heater / boiler with target temperature + operation mode +
845
+ * away mode (HA `water_heater.*`). `water-heater` cap — a
846
+ * climate-family actuator. */
847
+ DeviceType["WaterHeater"] = "water-heater";
848
+ /** Ceiling / standing / exhaust fan (HA `fan.*`). `fan-control` cap. */
849
+ DeviceType["Fan"] = "fan";
850
+ /** Audio / video playback endpoint (HA `media_player.*`). Disjoint from
851
+ * the camera surface — those use `Camera`. `media-player` cap. */
852
+ DeviceType["MediaPlayer"] = "media-player";
853
+ /** Security panel / alarm system (HA `alarm_control_panel.*`).
854
+ * `alarm-panel` cap. */
855
+ DeviceType["AlarmPanel"] = "alarm-panel";
856
+ /** Generic user-settable input (HA `number` / `input_number` / `select`
857
+ * / `input_select` / `text` / `input_text` / `input_datetime`).
858
+ * Sub-type via `DeviceRole`: NumericControl / SelectControl /
859
+ * TextControl / DateTimeControl. */
860
+ DeviceType["Control"] = "control";
861
+ /** Person / device-tracker presence (HA `person.*`, `device_tracker.*`).
862
+ * `presence` cap. */
863
+ DeviceType["Presence"] = "presence";
864
+ /** Weather provider (HA `weather.*`). Tier-3, low MVP priority.
865
+ * `weather` cap. */
866
+ DeviceType["Weather"] = "weather";
867
+ /** Robot vacuum (HA `vacuum.*`). Tier-3. `vacuum-control` cap. */
868
+ DeviceType["Vacuum"] = "vacuum";
869
+ /** Robotic lawn mower (HA `lawn_mower.*`). Tier-3.
870
+ * `lawn-mower-control` cap. */
871
+ DeviceType["LawnMower"] = "lawn-mower";
872
+ /** Physical HA device group — parent container for entity-children
873
+ * adopted from a single HA device entry. Not renderable as a
874
+ * standalone device; exists only to anchor child entities. */
875
+ DeviceType["Container"] = "container";
876
+ /** Single still-image entity (HA `image.*`). Read-only display of an
877
+ * `entity_picture` signed URL the browser loads directly. `image` cap. */
878
+ DeviceType["Image"] = "image";
879
+ /** Smart pet feeder — cloud-connected food dispenser with a bowl food
880
+ * level, battery, desiccant life, feeding state and manual-feed /
881
+ * call-pet / maintenance actions. Installed with the `pet-feeder` cap;
882
+ * dual-hopper models (D4S/D4SH) expose per-hopper portions. Sources:
883
+ * native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
884
+ * integrations sharing the same food/desiccant/hopper surface. */
885
+ DeviceType["PetFeeder"] = "pet-feeder";
886
+ /** Pet water fountain — tank level, water drunk, pump and its duty cycle.
887
+ * Installed with the `pet-fountain` cap. Reaches CamStack as loose entities
888
+ * of a container and is BUILT as a composed device (D967). */
889
+ DeviceType["PetFountain"] = "pet-fountain";
890
+ /** Built-in dishwasher — program lifecycle, door / remote-start gates,
891
+ * salt and rinse-aid levels. Installed with the `dishwasher` cap.
892
+ * Sources: native Bosch Home Connect, reusable by similar integrations.
893
+ * A plug-built dishwasher carries running-state and power instead. */
894
+ DeviceType["Dishwasher"] = "dishwasher";
895
+ /** Washing machine. A plug-built one is running while its power stays
896
+ * above a threshold. No program cap — that surface is the dishwasher's. */
897
+ DeviceType["Washer"] = "washer";
898
+ /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
899
+ DeviceType["Dryer"] = "dryer";
900
+ /** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
901
+ * other radio-to-IP bridges). It carries the box's own facts (firmware,
902
+ * address, connection state) and the actions the box really supports
903
+ * (`reboot`, `device-admin-link`). Products are NOT its children — they are
904
+ * adopted beside it — and its connection settings stay on the integration. */
905
+ DeviceType["Gateway"] = "gateway";
906
+ return DeviceType;
907
+ }({});
908
+ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
909
+ DeviceFeature["BatteryOperated"] = "battery-operated";
910
+ DeviceFeature["Rebootable"] = "rebootable";
911
+ /**
912
+ * Device supports an on-demand re-sync of its derived spec with its
913
+ * upstream source — drives the generic Re-sync button. The owning
914
+ * provider implements the action via the `device-adoption.resync` cap.
915
+ */
916
+ DeviceFeature["Resyncable"] = "resyncable";
917
+ DeviceFeature["NativeSnapshot"] = "native-snapshot";
918
+ DeviceFeature["DoorbellButton"] = "doorbell-button";
919
+ DeviceFeature["TwoWayAudio"] = "two-way-audio";
920
+ DeviceFeature["PanTiltZoom"] = "pan-tilt-zoom";
921
+ /**
922
+ * Camera supports the on-firmware autotrack subsystem (subject-
923
+ * following). Distinct from `PanTiltZoom` because not every PTZ
924
+ * camera ships autotrack — the admin UI uses this flag to gate
925
+ * the autotrack toggle / settings card without re-deriving from
926
+ * the cap registry. Mirrors `ptz-autotrack` cap registration:
927
+ * driver sets this feature when probe confirms the firmware
928
+ * surface, and registers the cap in the same code path.
929
+ */
930
+ DeviceFeature["PtzAutotrack"] = "ptz-autotrack";
931
+ /**
932
+ * Accessory exposes a "trigger on motion" toggle — the parent camera's
933
+ * motion detection automatically activates this device. Mirrors
934
+ * `motion-trigger` cap registration: drivers set this feature in the
935
+ * same code path that calls `ctx.registerNativeCap(motionTriggerCapability, ...)`.
936
+ *
937
+ * Used by admin UI (gate the in-hero `MotionTriggerToggle` against a
938
+ * fast scalar without binding fetch), notifier rules, and `listAll`
939
+ * filters that want "all devices with on-motion behaviour".
940
+ */
941
+ DeviceFeature["MotionTrigger"] = "motion-trigger";
942
+ /** Light supports rgb-triplet color via `color` cap. */
943
+ DeviceFeature["LightColorRgb"] = "light-color-rgb";
944
+ /** Light supports HSV color via `color` cap. */
945
+ DeviceFeature["LightColorHsv"] = "light-color-hsv";
946
+ /** Light supports color-temperature (mired) via `color` cap. */
947
+ DeviceFeature["LightColorMired"] = "light-color-mired";
948
+ /** Thermostat supports a `heat_cool` dual setpoint (targetLow +
949
+ * targetHigh). Gates the range slider UI. */
950
+ DeviceFeature["ClimateDualSetpoint"] = "climate-dual-setpoint";
951
+ /** Thermostat exposes target humidity and/or current humidity
952
+ * readings. Gates the humidity controls. */
953
+ DeviceFeature["ClimateHumidity"] = "climate-humidity";
954
+ /** Thermostat exposes a fan-mode selector. */
955
+ DeviceFeature["ClimateFanMode"] = "climate-fan-mode";
956
+ /** Thermostat exposes preset modes (eco / away / sleep / vendor). */
957
+ DeviceFeature["ClimatePreset"] = "climate-preset";
958
+ /** Thermostat exposes a vertical louver swing toggle. Gates the
959
+ * vertical-swing switch in the climate UI. Independent of horizontal. */
960
+ DeviceFeature["ClimateSwingVertical"] = "climate-swing-vertical";
961
+ /** Thermostat exposes a horizontal louver swing toggle. Gates the
962
+ * horizontal-swing switch in the climate UI. Independent of vertical. */
963
+ DeviceFeature["ClimateSwingHorizontal"] = "climate-swing-horizontal";
964
+ /** Cover exposes intermediate position control (0..100). Gates the
965
+ * position slider UI. */
966
+ DeviceFeature["CoverPositionable"] = "cover-positionable";
967
+ /** Cover exposes slat-tilt control. Gates the tilt slider UI. */
968
+ DeviceFeature["CoverTilt"] = "cover-tilt";
969
+ /** Valve exposes intermediate position control (0..100). Gates the
970
+ * position slider / drag surface UI. */
971
+ DeviceFeature["ValvePositionable"] = "valve-positionable";
972
+ /** Fan exposes a speed-percentage setter. Gates the speed slider UI. */
973
+ DeviceFeature["FanSpeed"] = "fan-speed";
974
+ /** Fan exposes a preset mode selector. */
975
+ DeviceFeature["FanPreset"] = "fan-preset";
976
+ /** Fan exposes blade direction (forward/reverse) — typical of
977
+ * ceiling fans. */
978
+ DeviceFeature["FanDirection"] = "fan-direction";
979
+ /** Fan exposes an oscillation toggle. */
980
+ DeviceFeature["FanOscillating"] = "fan-oscillating";
981
+ /** Lock requires a PIN code on lock/unlock. Gates the code-entry
982
+ * field on the UI lock-controls panel. */
983
+ DeviceFeature["LockPinRequired"] = "lock-pin-required";
984
+ /** Lock supports a latch-release ("open door") action distinct from
985
+ * unlock. Mirrors HA `LockEntityFeature.OPEN` (bit 1) in
986
+ * `supported_features`. Gates the Open Door button in the UI. */
987
+ DeviceFeature["LockOpen"] = "lock-open";
988
+ /** Media player exposes a seek-to-position surface. */
989
+ DeviceFeature["MediaPlayerSeek"] = "media-player-seek";
990
+ /** Media player exposes a volume-level setter. */
991
+ DeviceFeature["MediaPlayerVolume"] = "media-player-volume";
992
+ /** Media player exposes a mute toggle distinct from volume=0. */
993
+ DeviceFeature["MediaPlayerMute"] = "media-player-mute";
994
+ /** Media player exposes a shuffle toggle. */
995
+ DeviceFeature["MediaPlayerShuffle"] = "media-player-shuffle";
996
+ /** Media player exposes a repeat mode (off / all / one). */
997
+ DeviceFeature["MediaPlayerRepeat"] = "media-player-repeat";
998
+ /** Media player exposes a source / input selector. */
999
+ DeviceFeature["MediaPlayerSelectSource"] = "media-player-select-source";
1000
+ /** Media player exposes a play-arbitrary-media surface (URL / id). */
1001
+ DeviceFeature["MediaPlayerPlayMedia"] = "media-player-play-media";
1002
+ /** Media player exposes next-track. */
1003
+ DeviceFeature["MediaPlayerNext"] = "media-player-next";
1004
+ /** Media player exposes previous-track. */
1005
+ DeviceFeature["MediaPlayerPrevious"] = "media-player-previous";
1006
+ /** Media player exposes stop distinct from pause. */
1007
+ DeviceFeature["MediaPlayerStop"] = "media-player-stop";
1008
+ /** Alarm panel requires a PIN code on arm/disarm. */
1009
+ DeviceFeature["AlarmPinRequired"] = "alarm-pin-required";
1010
+ /** Presence device carries GPS coordinates (lat/lng/accuracy) in
1011
+ * addition to a textual location. */
1012
+ DeviceFeature["PresenceGps"] = "presence-gps";
1013
+ /** Notifier accepts an inline / URL image attachment. */
1014
+ DeviceFeature["NotifierImage"] = "notifier-image";
1015
+ /** Notifier accepts a priority hint (high/normal/low). */
1016
+ DeviceFeature["NotifierPriority"] = "notifier-priority";
1017
+ /** Notifier accepts a free-form `data` payload for platform-specific
1018
+ * fields. */
1019
+ DeviceFeature["NotifierData"] = "notifier-data";
1020
+ /** Notifier supports interactive action buttons / callbacks. */
1021
+ DeviceFeature["NotifierActions"] = "notifier-actions";
1022
+ /** Notifier supports per-call recipient targeting (multi-user). */
1023
+ DeviceFeature["NotifierRecipients"] = "notifier-recipients";
1024
+ /** Script runner accepts a variables map on each run invocation. */
1025
+ DeviceFeature["ScriptVariables"] = "script-variables";
1026
+ /** Automation `trigger` accepts a skipCondition flag — fires the
1027
+ * automation's actions while bypassing its condition block. */
1028
+ DeviceFeature["AutomationSkipCondition"] = "automation-skip-condition";
1029
+ /** Robot vacuum exposes a live cleaning map (image child). Gates the
1030
+ * map tile in the vacuum UI. */
1031
+ DeviceFeature["VacuumHasMap"] = "vacuum-has-map";
1032
+ /** Robot vacuum exposes AI obstacle-detection toggles. Gates the AI
1033
+ * switches group. */
1034
+ DeviceFeature["VacuumHasAi"] = "vacuum-has-ai";
1035
+ /** Robot mower exposes a live mowing map (SVG image child). Gates the
1036
+ * mower map tile. */
1037
+ DeviceFeature["MowerHasMap"] = "mower-has-map";
1038
+ /** Robot mower exposes targeted mowing (all-area / zones / edges /
1039
+ * spots) — gates the Mow action + map selectors. */
1040
+ DeviceFeature["MowerHasTargetedMowing"] = "mower-has-targeted-mowing";
1041
+ return DeviceFeature;
1042
+ }({});
1043
+ /**
1044
+ * Semantic role a device plays within its parent. Populated by driver
1045
+ * addons when creating accessory devices (Reolink siren/floodlight/
1046
+ * PIR/chime/autotrack/doorbell, ONVIF relay outputs, …). Used by the
1047
+ * admin UI to pick icons, labels, and widgets — a `Switch` with
1048
+ * `role: Floodlight` renders as a bulb with a brightness slider,
1049
+ * whereas a `Switch` with `role: Siren` renders as a klaxon.
1050
+ *
1051
+ * Undefined for top-level devices (cameras, NVRs, hubs). Persisted in
1052
+ * sqlite as a nullable TEXT column — old rows keep working unchanged.
1053
+ */
1054
+ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
1055
+ DeviceRole["Siren"] = "siren";
1056
+ DeviceRole["Floodlight"] = "floodlight";
1057
+ DeviceRole["Spotlight"] = "spotlight";
1058
+ DeviceRole["PirSensor"] = "pir-sensor";
1059
+ DeviceRole["Chime"] = "chime";
1060
+ DeviceRole["Autotrack"] = "autotrack";
1061
+ DeviceRole["Nightvision"] = "nightvision";
1062
+ DeviceRole["PrivacyMask"] = "privacy-mask";
1063
+ DeviceRole["Doorbell"] = "doorbell";
1064
+ /** Interior blind / shade / roller blind. HA `device_class: blind|shade`. */
1065
+ DeviceRole["CoverBlind"] = "cover-blind";
1066
+ /** Exterior roller shutter. HA `device_class: shutter`; Homematic BROLL. */
1067
+ DeviceRole["CoverShutter"] = "cover-shutter";
1068
+ /** Exterior awning. HA `device_class: awning`. */
1069
+ DeviceRole["CoverAwning"] = "cover-awning";
1070
+ /** Curtain / drape. HA `device_class: curtain`. */
1071
+ DeviceRole["CoverCurtain"] = "cover-curtain";
1072
+ /** Garage door. HA `device_class: garage`. A security actuator: exports gate it. */
1073
+ DeviceRole["CoverGarage"] = "cover-garage";
1074
+ /** Gate. HA `device_class: gate`. A security actuator: exports gate it. */
1075
+ DeviceRole["CoverGate"] = "cover-gate";
1076
+ /** Door. HA `device_class: door`. A security actuator: exports gate it. */
1077
+ DeviceRole["CoverDoor"] = "cover-door";
1078
+ /** Window. HA `device_class: window`. */
1079
+ DeviceRole["CoverWindow"] = "cover-window";
1080
+ /** Virtual HA toggle (input_boolean.*) — distinguishable from a
1081
+ * real Switch device for UI rendering / export adapters. */
1082
+ DeviceRole["BinaryHelper"] = "binary-helper";
1083
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
1084
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
1085
+ DeviceRole["Outlet"] = "outlet";
1086
+ /** Generic motion / occupancy / moving event source. Distinct from
1087
+ * the camera accessory PirSensor role: that one is a camera child;
1088
+ * this is a standalone HA / 3rd-party motion sensor. */
1089
+ DeviceRole["MotionSensor"] = "motion-sensor";
1090
+ DeviceRole["ContactSensor"] = "contact-sensor";
1091
+ DeviceRole["LeakSensor"] = "leak-sensor";
1092
+ DeviceRole["SmokeSensor"] = "smoke-sensor";
1093
+ DeviceRole["COSensor"] = "co-sensor";
1094
+ DeviceRole["GasSensor"] = "gas-sensor";
1095
+ DeviceRole["TamperSensor"] = "tamper-sensor";
1096
+ DeviceRole["VibrationSensor"] = "vibration-sensor";
1097
+ DeviceRole["ConnectivitySensor"] = "connectivity-sensor";
1098
+ DeviceRole["SoundSensor"] = "sound-sensor";
1099
+ /** Fallback for `binary_sensor` without a known `device_class`. */
1100
+ DeviceRole["BinarySensor"] = "binary-sensor";
1101
+ DeviceRole["TemperatureSensor"] = "temperature-sensor";
1102
+ DeviceRole["HumiditySensor"] = "humidity-sensor";
1103
+ DeviceRole["AmbientLightSensor"] = "ambient-light-sensor";
1104
+ DeviceRole["PressureSensor"] = "pressure-sensor";
1105
+ /** Wind speed or direction (weather-station `wind-sensor` cap). */
1106
+ DeviceRole["WindSensor"] = "wind-sensor";
1107
+ /** Rain accumulation or rate (weather-station `rain-sensor` cap). */
1108
+ DeviceRole["RainSensor"] = "rain-sensor";
1109
+ /** UV index (weather-station `uv-sensor` cap). */
1110
+ DeviceRole["UvSensor"] = "uv-sensor";
1111
+ /** Solar irradiance W/m² (weather-station `solar-radiation-sensor` cap).
1112
+ * Distinct from AmbientLightSensor (lux). */
1113
+ DeviceRole["SolarRadiationSensor"] = "solar-radiation-sensor";
1114
+ /** Soil moisture % (garden/weather `soil-moisture-sensor` cap). */
1115
+ DeviceRole["SoilMoistureSensor"] = "soil-moisture-sensor";
1116
+ DeviceRole["PowerSensor"] = "power-sensor";
1117
+ DeviceRole["EnergySensor"] = "energy-sensor";
1118
+ DeviceRole["VoltageSensor"] = "voltage-sensor";
1119
+ DeviceRole["CurrentSensor"] = "current-sensor";
1120
+ DeviceRole["AirQualitySensor"] = "air-quality-sensor";
1121
+ /** Battery level (numeric % via `sensor` OR low-bool via
1122
+ * `binary_sensor` — the cap distinguishes via the value type). */
1123
+ DeviceRole["BatterySensor"] = "battery-sensor";
1124
+ /** Fallback for `sensor` numeric without a known `device_class`. */
1125
+ DeviceRole["NumericSensor"] = "numeric-sensor";
1126
+ /** String / enum state (HA `sensor` with `state_class: enum` or
1127
+ * `attributes.options`). */
1128
+ DeviceRole["EnumSensor"] = "enum-sensor";
1129
+ /** Date / timestamp state (HA `sensor` with `device_class: timestamp`
1130
+ * or `date`). The slice carries the raw ISO string verbatim (hosted on
1131
+ * the `enum-sensor` cap); the UI renders it locale-formatted. */
1132
+ DeviceRole["DateTimeSensor"] = "datetime-sensor";
1133
+ /** Last-resort fallback when nothing else matches. */
1134
+ DeviceRole["GenericSensor"] = "generic-sensor";
1135
+ DeviceRole["NumericControl"] = "numeric-control";
1136
+ DeviceRole["SelectControl"] = "select-control";
1137
+ DeviceRole["TextControl"] = "text-control";
1138
+ DeviceRole["DateTimeControl"] = "datetime-control";
1139
+ /** Mobile push notifier (HA `notify.mobile_app_*`) — supports
1140
+ * rich features (image, priority, channel routing). */
1141
+ DeviceRole["MobilePushNotifier"] = "mobile-push-notifier";
1142
+ /** Chat / messaging service (HA `notify.telegram_*`,
1143
+ * `notify.discord_*`, etc.). */
1144
+ DeviceRole["MessagingNotifier"] = "messaging-notifier";
1145
+ /** Email-based delivery (HA `notify.smtp`, etc.). */
1146
+ DeviceRole["EmailNotifier"] = "email-notifier";
1147
+ /** Fallback when the notifier service name doesn't match a known
1148
+ * pattern. */
1149
+ DeviceRole["GenericNotifier"] = "generic-notifier";
1150
+ /** Emits a pulse train (infrared or RF) on request — the `ir-transmitter`
1151
+ * cap. A `DeviceType.Generic` device. */
1152
+ DeviceRole["IrTransmitter"] = "ir-transmitter";
1153
+ return DeviceRole;
1154
+ }({});
1155
+ //#endregion
780
1156
  //#region ../../node_modules/zod/v4/core/core.js
781
1157
  var _a$1;
782
1158
  function $constructor(name, initializer, params) {
@@ -5645,7 +6021,7 @@ var ZodIssueCode = {
5645
6021
  var ZodFirstPartyTypeKind;
5646
6022
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5647
6023
  //#endregion
5648
- //#region ../types/dist/sleep-CeyhrcOk.mjs
6024
+ //#region ../types/dist/sleep-CSYuxssh.mjs
5649
6025
  /**
5650
6026
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5651
6027
  * window to float samples (D455).
@@ -6161,6 +6537,102 @@ function emitReadiness(bus, params) {
6161
6537
  ts
6162
6538
  }));
6163
6539
  }
6540
+ var NOT_RUN = {
6541
+ declared: false,
6542
+ snapshot: null,
6543
+ applied: [],
6544
+ failed: [],
6545
+ blocked: [],
6546
+ skipped: [],
6547
+ blockOwner: []
6548
+ };
6549
+ /**
6550
+ * Offer `migrations` to the engine and run what it says to run, in its order. Never
6551
+ * throws: an unreachable or refusing engine is logged and reported `declared: false`,
6552
+ * because the hub (and this addon's boot) must not hang on a migration.
6553
+ */
6554
+ async function runAddonMigrations(host, migrations, logger) {
6555
+ const first = migrations[0];
6556
+ if (first === void 0) return NOT_RUN;
6557
+ const owner = first.meta.owner;
6558
+ const log = logger.withTags({ migrationOwner: owner });
6559
+ const byId = new Map(migrations.map((m) => [m.meta.id, m]));
6560
+ let declaration;
6561
+ try {
6562
+ declaration = await host.declareOwner({
6563
+ owner,
6564
+ metas: migrations.map((m) => m.meta)
6565
+ });
6566
+ } catch (err) {
6567
+ log.error("the migration engine refused or could not take this addon catalogue — none ran", { meta: { error: errMsg(err) } });
6568
+ return {
6569
+ ...NOT_RUN,
6570
+ blockOwner: migrations.filter((m) => m.meta.onFailure === "block-owner").map((m) => m.meta.id)
6571
+ };
6572
+ }
6573
+ const applied = [];
6574
+ const failed = [];
6575
+ const blocked = [];
6576
+ const skipped = [];
6577
+ const settle = (id, status) => {
6578
+ if (status === "applied") applied.push(id);
6579
+ else if (status === "failed") failed.push(id);
6580
+ else if (status === "blocked") blocked.push(id);
6581
+ else skipped.push(id);
6582
+ };
6583
+ for (const step of declaration.steps) {
6584
+ const bound = byId.get(step.id);
6585
+ if (bound === void 0 || step.action === "skip") {
6586
+ skipped.push(step.id);
6587
+ continue;
6588
+ }
6589
+ const tags = { migrationId: step.id };
6590
+ try {
6591
+ const gate = await host.startMigration({ id: step.id });
6592
+ if (!gate.proceed) {
6593
+ log.warn("migration not run", {
6594
+ tags,
6595
+ meta: {
6596
+ state: gate.state,
6597
+ reason: gate.reason
6598
+ }
6599
+ });
6600
+ settle(step.id, gate.state === "pending" ? "blocked" : gate.state);
6601
+ continue;
6602
+ }
6603
+ const result = await bound.execute();
6604
+ const outcome = await host.finishMigration({
6605
+ id: step.id,
6606
+ result
6607
+ });
6608
+ if (outcome.status === "applied") log.info("migration applied", {
6609
+ tags,
6610
+ meta: { rowsChanged: outcome.rowsChanged }
6611
+ });
6612
+ else log.error("migration failed", {
6613
+ tags,
6614
+ meta: { error: outcome.error }
6615
+ });
6616
+ settle(step.id, outcome.status);
6617
+ } catch (err) {
6618
+ log.error("migration could not be completed with the engine", {
6619
+ tags,
6620
+ meta: { error: errMsg(err) }
6621
+ });
6622
+ failed.push(step.id);
6623
+ }
6624
+ }
6625
+ const blockOwner = [...failed, ...blocked].filter((id) => byId.get(id)?.meta.onFailure === "block-owner");
6626
+ return {
6627
+ declared: true,
6628
+ snapshot: declaration.snapshot,
6629
+ applied,
6630
+ failed,
6631
+ blocked,
6632
+ skipped,
6633
+ blockOwner
6634
+ };
6635
+ }
6164
6636
  /**
6165
6637
  * Build a {@link DurableState} over a single store key. Transport-agnostic:
6166
6638
  * `read`/`write` are the addon-store or device-store accessors. The whole
@@ -7216,6 +7688,28 @@ var BaseAddon = class {
7216
7688
  return normalized;
7217
7689
  }
7218
7690
  /**
7691
+ * Run this addon's migration catalogue (D987 M3) through the hub's `migrations`
7692
+ * engine. Call it at the point of the boot where the data must already have the new
7693
+ * shape — `onInitialize`, or `onRestoreDevices` for a migration that needs the
7694
+ * restored devices — with the catalogue bound to the addon's ports
7695
+ * (`defineAddonMigrations(ADDON_MIGRATIONS, ports)`).
7696
+ *
7697
+ * An engine that cannot be reached or refuses the catalogue is logged and runs
7698
+ * nothing; the boot continues. A migration that FAILED (or was blocked by one)
7699
+ * and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
7700
+ * serve data whose shape it cannot trust; `tolerate` migrations never do.
7701
+ */
7702
+ async runMigrations(migrations) {
7703
+ const api = this.ctx.api.migrations;
7704
+ const report = await runAddonMigrations({
7705
+ declareOwner: (input) => api.declareOwner.mutate(input),
7706
+ startMigration: (input) => api.startMigration.mutate(input),
7707
+ finishMigration: (input) => api.finishMigration.mutate(input)
7708
+ }, migrations, this.ctx.logger);
7709
+ if (report.blockOwner.length > 0) throw new Error(`migration(s) ${report.blockOwner.join(", ")} did not apply and block this addon — fix or retry them (migrations.retry), then restart it`);
7710
+ return report;
7711
+ }
7712
+ /**
7219
7713
  * Called by the isolated-process runner AFTER `broker.start()`.
7220
7714
  * In-process addons never need this because the hub broker is already
7221
7715
  * running when `initialize()` fires. For forked children the broker
@@ -7348,6 +7842,26 @@ var BaseAddon = class {
7348
7842
  * alternative — skip it — turns a rename into a surface that quietly
7349
7843
  * empties, which looks exactly like an addon with nothing to configure.
7350
7844
  */
7845
+ /**
7846
+ * The form schema of ONE integration instance, or `null` to use {@link globalSettingsSchema}.
7847
+ *
7848
+ * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
7849
+ * needs its connection fields, the default integration the addon's own sections. It is the
7850
+ * SHAPE of the form only — the values live in `integrations.getSettings`, and the write path is
7851
+ * unchanged (`integrations.setSettings` for an account integration).
7852
+ */
7853
+ async integrationSettingsSchema(_instance) {
7854
+ return null;
7855
+ }
7856
+ /**
7857
+ * What the create wizard and the integration edit page render for one instance: the addon's
7858
+ * per-instance schema when it declares one, else its global schema (what they always showed).
7859
+ */
7860
+ async getIntegrationFormSchema(instance, nodeId) {
7861
+ const scoped = await this.integrationSettingsSchema(instance);
7862
+ if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
7863
+ return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
7864
+ }
7351
7865
  async getIntegrationSettings(nodeId) {
7352
7866
  const declared = this.integrationSettingSections();
7353
7867
  if (declared.length === 0) return null;
@@ -8451,407 +8965,6 @@ function defineRecordKinds(specs) {
8451
8965
  }
8452
8966
  return out;
8453
8967
  }
8454
- var READING_QUANTITIES = [
8455
- "temperature",
8456
- "pressure",
8457
- "speed",
8458
- "precipitation",
8459
- "precipitation-rate",
8460
- "length",
8461
- "area",
8462
- "illuminance",
8463
- "irradiance",
8464
- "power",
8465
- "energy",
8466
- "voltage",
8467
- "current",
8468
- "apparent-power",
8469
- "humidity",
8470
- "percentage",
8471
- "concentration-volume",
8472
- "concentration-mass",
8473
- "concentration-ppb",
8474
- "volume",
8475
- "duration",
8476
- "duration-ms",
8477
- "information",
8478
- "sound-level",
8479
- "signal-level",
8480
- "colour-temperature",
8481
- "angle",
8482
- "bitrate",
8483
- "rate",
8484
- "mass",
8485
- "index",
8486
- "ratio",
8487
- "count",
8488
- "none",
8489
- "device"
8490
- ];
8491
- var ReadingQuantitySchema = _enum(READING_QUANTITIES);
8492
- new Set(READING_QUANTITIES);
8493
- /**
8494
- * Stored unit symbol for a quantity. `none` means the number is dimensionless
8495
- * (or an index / ratio / count). `device` means the cap has no single unit.
8496
- */
8497
- var QUANTITY_BASE = {
8498
- temperature: "°C",
8499
- pressure: "hPa",
8500
- speed: "m/s",
8501
- precipitation: "mm",
8502
- "precipitation-rate": "mm/h",
8503
- length: "m",
8504
- area: "m²",
8505
- illuminance: "lx",
8506
- irradiance: "W/m²",
8507
- power: "W",
8508
- energy: "kWh",
8509
- voltage: "V",
8510
- current: "A",
8511
- "apparent-power": "VA",
8512
- humidity: "%",
8513
- percentage: "%",
8514
- "concentration-volume": "ppm",
8515
- "concentration-mass": "µg/m³",
8516
- "concentration-ppb": "ppb",
8517
- volume: "L",
8518
- duration: "s",
8519
- "duration-ms": "ms",
8520
- information: "B",
8521
- "sound-level": "dBFS",
8522
- "signal-level": "dBm",
8523
- "colour-temperature": "mired",
8524
- angle: "°",
8525
- bitrate: "kbps",
8526
- rate: "fps",
8527
- mass: "kg",
8528
- index: "none",
8529
- ratio: "none",
8530
- count: "none",
8531
- none: "none",
8532
- device: "device"
8533
- };
8534
- /** The stored unit, or `undefined` when the quantity has no unit symbol. */
8535
- function baseUnitOf(quantity) {
8536
- const symbol = QUANTITY_BASE[quantity];
8537
- if (symbol === "none" || symbol === "device") return void 0;
8538
- return symbol;
8539
- }
8540
- function readingMeta(quantity, extras) {
8541
- return {
8542
- quantity,
8543
- ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
8544
- ...extras?.label !== void 0 ? { label: extras.label } : {}
8545
- };
8546
- }
8547
- /** Shared Zod schemas used across streaming capabilities. */
8548
- var CamProfileSchema = _enum([
8549
- "high",
8550
- "mid",
8551
- "low"
8552
- ]);
8553
- /**
8554
- * Which rung a per-camera consumer listens on: an explicit profile, or `auto`.
8555
- *
8556
- * `auto` is a REAL value, not an absent one — it means "resolve it by the rule
8557
- * that consumer owns". It lives beside `CamProfile` because a rung is the only
8558
- * operator-facing way to name a stream: a cam-stream id is broker-internal and
8559
- * moves when a slot is reassigned, so a stored id silently stops meaning what
8560
- * the operator picked. Today's one user is the audio analyser, whose rule is
8561
- * "the lowest AVAILABLE native rung" (D537).
8562
- */
8563
- var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
8564
- var CamStreamKindSchema = _enum([
8565
- "pull-rtsp",
8566
- "pull-rtmp",
8567
- "pull-http",
8568
- "pull-flv",
8569
- "pull-rfc4571",
8570
- "push-annexb",
8571
- "derived"
8572
- ]);
8573
- var CamStreamResolutionSchema = object({
8574
- width: number().int().positive().meta(readingMeta("none")),
8575
- height: number().int().positive().meta(readingMeta("none"))
8576
- });
8577
- var CameraStreamSchema = object({
8578
- /** Stable, provider-assigned id unique within the (deviceId) scope. */
8579
- camStreamId: string().min(1),
8580
- deviceId: number().int().nonnegative(),
8581
- kind: CamStreamKindSchema,
8582
- /** Required for pull-* kinds. Ignored for push-annexb. */
8583
- url: string().optional(),
8584
- codec: string().optional(),
8585
- resolution: CamStreamResolutionSchema.optional(),
8586
- fps: number().positive().optional(),
8587
- /** Human label surfaced in the Admin UI "Camera Stream" dropdown. */
8588
- label: string().optional(),
8589
- /**
8590
- * Device-level features the publisher advertised (e.g. `battery-operated`).
8591
- * The broker, snapshot orchestrator, and prebuffer manager all consult
8592
- * this list to derive policy — relaxed stall watchdog for battery
8593
- * cams, prebuffer off by default, longer snapshot rate-limit, etc.
8594
- *
8595
- * Single source of truth replacing per-stream flags like the
8596
- * historical `allowStall`: if the publisher knows the camera is
8597
- * battery-powered, every downstream service derives the right policy
8598
- * from this list.
8599
- */
8600
- deviceFeatures: array(string()).optional(),
8601
- /**
8602
- * Whether this stream participates in the broker's automatic profile
8603
- * assignment (`computeInitialAssignment`). Defaults to `true`. Publishers
8604
- * use `false` when they want a stream to be SELECTABLE in the UI but not
8605
- * picked by default — e.g. Reolink publishes its native Baichuan streams
8606
- * as `autoEligible: true` (the recommended path) and its RTSP / RTMP
8607
- * mirrors as `autoEligible: false` (still pickable per slot, just not
8608
- * the auto choice). Manual `assignProfile` calls remain valid for
8609
- * non-eligible streams.
8610
- */
8611
- autoEligible: boolean().optional(),
8612
- /**
8613
- * The profile slot the PUBLISHER says this stream is: `low` for a stream
8614
- * whose only sensible role is the low-bandwidth one, `high` for a main
8615
- * stream. Consulted by the broker's automatic ranking
8616
- * (`rankInitialAssignment`) BEFORE the pixel-count heuristic, and only there:
8617
- * an operator's manual assignment still wins, and an absent hint is the
8618
- * pixel-ranked default every camera had before this field existed.
8619
- *
8620
- * Why it exists: a camera with ONE stream ranked to `mid` — the slot no
8621
- * storage class is written for on its own (`recordings` is high|mid,
8622
- * `recordingsLow` is low) and the one profile scrub does not read. The robot
8623
- * camera's single 720p relay is a `low` in every respect except the slot the
8624
- * heuristic put it in.
8625
- */
8626
- profileHint: CamProfileSchema.optional(),
8627
- /**
8628
- * Transport-specific opaque metadata. The broker passes it through to
8629
- * the source reader without inspecting it. Currently used by
8630
- * `pull-rfc4571` streams to carry the upstream SDP (so the reader can
8631
- * route RTP packets to the right depacketizer without an in-band
8632
- * DESCRIBE phase). Other kinds typically leave it undefined.
8633
- */
8634
- metadata: record(string(), unknown()).optional()
8635
- });
8636
- var ProfileSlotStatusSchema = _enum([
8637
- "unassigned",
8638
- "idle",
8639
- "connecting",
8640
- "streaming",
8641
- "error"
8642
- ]);
8643
- var ProfileSlotSchema = object({
8644
- deviceId: number().int().nonnegative(),
8645
- profile: CamProfileSchema,
8646
- /** Broker id the rest of the system addresses: `${deviceId}/${profile}`. */
8647
- brokerId: string(),
8648
- /** `null` when the profile is unassigned. */
8649
- sourceCamStreamId: string().nullable(),
8650
- status: ProfileSlotStatusSchema,
8651
- resolution: CamStreamResolutionSchema.optional(),
8652
- codec: string().optional(),
8653
- preBufferSec: number().nonnegative().optional(),
8654
- errorMessage: string().optional()
8655
- });
8656
- /**
8657
- * Zod schema for StreamSourceEntry — the canonical stream descriptor
8658
- * exposed by ICameraDevice.getStreamSources() and consumed by the broker.
8659
- */
8660
- var StreamSourceEntrySchema$1 = object({
8661
- id: string(),
8662
- label: string(),
8663
- protocol: _enum([
8664
- "rtsp",
8665
- "rtmp",
8666
- "annexb",
8667
- "http-mjpeg",
8668
- "webrtc",
8669
- "custom"
8670
- ]),
8671
- url: string().optional(),
8672
- resolution: object({
8673
- width: number(),
8674
- height: number()
8675
- }).readonly().optional(),
8676
- fps: number().optional(),
8677
- bitrate: number().optional(),
8678
- codec: string().optional(),
8679
- profileHint: CamProfileSchema.optional()
8680
- });
8681
- object({
8682
- type: string(),
8683
- url: string(),
8684
- videoCodec: string().optional(),
8685
- audioCodec: string().optional(),
8686
- metadata: record(string(), unknown()).readonly().optional()
8687
- });
8688
- /**
8689
- * Wire schema for `FrameHandle` — a zero-pixel, serialisable reference to a
8690
- * frame in a shared-memory ring (Phase 5 / D9). Mirrors the `FrameHandle` TS
8691
- * interface in `interfaces/frame-handle.ts` field-for-field so a cap method
8692
- * (the runner's native-crop and pipeline-executor inputs) can carry handles
8693
- * over tRPC / Moleculer.
8694
- *
8695
- * The `satisfies` assertion below pins this schema to the interface: if a
8696
- * field is added to / removed from `FrameHandle` without a matching schema
8697
- * edit, `z.infer<typeof FrameHandleSchema>` stops being assignable to
8698
- * `FrameHandle` and the build fails — no silent drift.
8699
- */
8700
- var FrameHandleSchema = object({
8701
- shmId: string(),
8702
- slot: number().int().nonnegative(),
8703
- seq: number().int().nonnegative(),
8704
- width: number().int().positive(),
8705
- height: number().int().positive(),
8706
- format: _enum([
8707
- "jpeg",
8708
- "rgb",
8709
- "bgr",
8710
- "yuv420",
8711
- "gray"
8712
- ]),
8713
- pts: number(),
8714
- byteLength: number().int().nonnegative(),
8715
- nodeId: string(),
8716
- slotCount: number().int().positive()
8717
- });
8718
- /**
8719
- * Wire schema for a decoded audio chunk (Phase 5 / D9). Mirrors the
8720
- * `DecodedAudioChunk` TS interface in `interfaces/stream-broker.ts`: PCM
8721
- * sample bytes plus the track parameters a consumer needs to interpret them.
8722
- *
8723
- * Audio chunks are tiny (a ~500ms AudioCodecSession window is a few KB of
8724
- * Float32 PCM), so unlike video frames they ship their bytes INLINE over
8725
- * tRPC / Moleculer — no shared-memory plane. `data` is typed `Uint8Array`
8726
- * (the wire-serialisable supertype of `Buffer`); a `Buffer` is assignable to
8727
- * it.
8728
- */
8729
- var AudioChunkFormatSchema = _enum(AUDIO_CHUNK_FORMATS);
8730
- var DecodedAudioChunkSchema = object({
8731
- data: _instanceof(Uint8Array),
8732
- sampleRate: number().int().positive(),
8733
- channels: number().int().positive(),
8734
- timestamp: number(),
8735
- /**
8736
- * Byte format of `data`. ABSENT MEANS `f32le` — today's bytes, byte for
8737
- * byte, for any peer that never heard of this field. A coded window
8738
- * (`pcmu` / `pcma`, one byte per sample) is only ever emitted to a
8739
- * subscription that DECLARED it accepts one, so absence can never mean
8740
- * "coded bytes a consumer will read as floats" (D455).
8741
- */
8742
- format: AudioChunkFormatSchema.optional()
8743
- });
8744
- /**
8745
- * Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). Audio ships
8746
- * its bytes inline: a consumer asks the broker for the decoded audio-chunk
8747
- * stream of one broker; the broker keeps a per-subscription bounded FIFO queue
8748
- * the consumer drains via `pullAudioChunks`.
8749
- */
8750
- var SubscribeAudioChunksInputSchema = object({
8751
- brokerId: string(),
8752
- /** Short caller-identity tag (`audio-analyzer`, …) for `listClients`. */
8753
- tag: string().optional(),
8754
- /**
8755
- * Byte formats this subscriber can READ, best first. The broker serves the
8756
- * chunk's own format when it is in this list and expands to `f32le`
8757
- * otherwise, so a subscriber is never handed bytes it cannot interpret.
8758
- *
8759
- * Absent (or without the source format) means `f32le` — the behaviour every
8760
- * subscriber had before D455, unchanged. This is the negotiation half of
8761
- * the source-bytes lever: it is what lets the broker and its consumers
8762
- * deploy one at a time across three nodes.
8763
- */
8764
- accept: array(AudioChunkFormatSchema).readonly().optional()
8765
- });
8766
- /** Result of `stream-broker.subscribeAudioChunks`. */
8767
- var SubscribeAudioChunksResultSchema = object({
8768
- /** Opaque id passed to `pullAudioChunks` / `unsubscribeAudioChunks`. */
8769
- subscriptionId: string() });
8770
- var BrokerStatsSchema = object({
8771
- status: _enum([
8772
- "idle",
8773
- "connecting",
8774
- "streaming",
8775
- "error",
8776
- "stopped"
8777
- ]),
8778
- inputFps: number(),
8779
- encodedSubscribers: number(),
8780
- uptimeMs: number(),
8781
- bitrateKbps: number(),
8782
- idrIntervalMs: number(),
8783
- codec: string().optional(),
8784
- totalBytes: number(),
8785
- packetCount: number(),
8786
- rtspClients: number(),
8787
- pipeClients: number(),
8788
- preBufferSec: number(),
8789
- preBufferMs: number(),
8790
- preBufferPackets: number(),
8791
- /**
8792
- * Detected audio track parameters from the RTSP DESCRIBE / SDP. `null`
8793
- * when the stream has no audio track or the broker is in cold start.
8794
- * `supported = false` means the codec was detected but the local
8795
- * decoder pipeline cannot produce PCM chunks (e.g. AAC without the
8796
- * AAC pipeline wired). Surfaced in the UI device-overview so operators
8797
- * can pre-pick the audio-analysis model that matches the codec.
8798
- */
8799
- audio: object({
8800
- codec: string(),
8801
- sampleRate: number(),
8802
- channels: number(),
8803
- supported: boolean()
8804
- }).nullable().optional(),
8805
- /**
8806
- * BROKER-SIDE AUDIO MUTE (D83). `true` = this broker is deliberately
8807
- * distributing none of the device's audio, on live or recording.
8808
- *
8809
- * Present so a silent camera can be told apart from a broken one on the
8810
- * stream panel itself, without cross-referencing the switch group: a
8811
- * broker holding an `audio` track descriptor while `audioMuted` is true is
8812
- * working exactly as asked. `audioMutedDropped` counts the audio units
8813
- * thrown away since the current dial — it is how you confirm from stats
8814
- * alone that the mute is on the packet path and not merely persisted.
8815
- */
8816
- audioMuted: boolean().optional(),
8817
- audioMutedDropped: number().optional()
8818
- });
8819
- /**
8820
- * Exporter-facing "profile restream" entry. Returned by
8821
- * `cameraStreams.getProfileRtspEntries` — one per ASSIGNED profile slot
8822
- * (high/mid/low). `brokerId` is the PROFILE-keyed broker id
8823
- * (`${deviceId}/${profile}`, e.g. `15/high`); its `url` is a broker
8824
- * RTSP restream that aliases the profile's assigned source broker, so
8825
- * every consumer dialling a profile converges on the same single pull.
8826
- * `codec`/`resolution` describe the assigned SOURCE camStream (for the
8827
- * `auto` resolution-closest pick). Distinct from `RtspRestreamEntry`
8828
- * (raw per-camStream, live-view only).
8829
- */
8830
- var ProfileRtspEntrySchema = object({
8831
- profile: CamProfileSchema,
8832
- /** Profile-keyed broker id, format `${deviceId}/${profile}` (e.g. `"15/high"`). */
8833
- brokerId: string(),
8834
- url: string(),
8835
- mutedUrl: string(),
8836
- enabled: boolean(),
8837
- codec: string().optional(),
8838
- resolution: CamStreamResolutionSchema.optional()
8839
- });
8840
- /**
8841
- * The model weight formats a catalog entry can ship. Schema-free on purpose:
8842
- * `types/models.ts` builds Zod schemas from it, but a runner-closure consumer
8843
- * (the model downloader in `@camstack/system`) needs only the list and must
8844
- * reach it through `@camstack/types/addon` without building the cap schema
8845
- * graph (D28).
8846
- */
8847
- var MODEL_FORMATS = [
8848
- "onnx",
8849
- "coreml",
8850
- "openvino",
8851
- "tflite",
8852
- "pt",
8853
- "gguf"
8854
- ];
8855
8968
  object({
8856
8969
  field: string().min(1),
8857
8970
  on: literal("advance")
@@ -8944,6 +9057,407 @@ function optionalMethod(input, output, options) {
8944
9057
  function event(data) {
8945
9058
  return { data };
8946
9059
  }
9060
+ var READING_QUANTITIES = [
9061
+ "temperature",
9062
+ "pressure",
9063
+ "speed",
9064
+ "precipitation",
9065
+ "precipitation-rate",
9066
+ "length",
9067
+ "area",
9068
+ "illuminance",
9069
+ "irradiance",
9070
+ "power",
9071
+ "energy",
9072
+ "voltage",
9073
+ "current",
9074
+ "apparent-power",
9075
+ "humidity",
9076
+ "percentage",
9077
+ "concentration-volume",
9078
+ "concentration-mass",
9079
+ "concentration-ppb",
9080
+ "volume",
9081
+ "duration",
9082
+ "duration-ms",
9083
+ "information",
9084
+ "sound-level",
9085
+ "signal-level",
9086
+ "colour-temperature",
9087
+ "angle",
9088
+ "bitrate",
9089
+ "rate",
9090
+ "mass",
9091
+ "index",
9092
+ "ratio",
9093
+ "count",
9094
+ "none",
9095
+ "device"
9096
+ ];
9097
+ var ReadingQuantitySchema = _enum(READING_QUANTITIES);
9098
+ new Set(READING_QUANTITIES);
9099
+ /**
9100
+ * Stored unit symbol for a quantity. `none` means the number is dimensionless
9101
+ * (or an index / ratio / count). `device` means the cap has no single unit.
9102
+ */
9103
+ var QUANTITY_BASE = {
9104
+ temperature: "°C",
9105
+ pressure: "hPa",
9106
+ speed: "m/s",
9107
+ precipitation: "mm",
9108
+ "precipitation-rate": "mm/h",
9109
+ length: "m",
9110
+ area: "m²",
9111
+ illuminance: "lx",
9112
+ irradiance: "W/m²",
9113
+ power: "W",
9114
+ energy: "kWh",
9115
+ voltage: "V",
9116
+ current: "A",
9117
+ "apparent-power": "VA",
9118
+ humidity: "%",
9119
+ percentage: "%",
9120
+ "concentration-volume": "ppm",
9121
+ "concentration-mass": "µg/m³",
9122
+ "concentration-ppb": "ppb",
9123
+ volume: "L",
9124
+ duration: "s",
9125
+ "duration-ms": "ms",
9126
+ information: "B",
9127
+ "sound-level": "dBFS",
9128
+ "signal-level": "dBm",
9129
+ "colour-temperature": "mired",
9130
+ angle: "°",
9131
+ bitrate: "kbps",
9132
+ rate: "fps",
9133
+ mass: "kg",
9134
+ index: "none",
9135
+ ratio: "none",
9136
+ count: "none",
9137
+ none: "none",
9138
+ device: "device"
9139
+ };
9140
+ /** The stored unit, or `undefined` when the quantity has no unit symbol. */
9141
+ function baseUnitOf(quantity) {
9142
+ const symbol = QUANTITY_BASE[quantity];
9143
+ if (symbol === "none" || symbol === "device") return void 0;
9144
+ return symbol;
9145
+ }
9146
+ function readingMeta(quantity, extras) {
9147
+ return {
9148
+ quantity,
9149
+ ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
9150
+ ...extras?.label !== void 0 ? { label: extras.label } : {}
9151
+ };
9152
+ }
9153
+ /** Shared Zod schemas used across streaming capabilities. */
9154
+ var CamProfileSchema = _enum([
9155
+ "high",
9156
+ "mid",
9157
+ "low"
9158
+ ]);
9159
+ /**
9160
+ * Which rung a per-camera consumer listens on: an explicit profile, or `auto`.
9161
+ *
9162
+ * `auto` is a REAL value, not an absent one — it means "resolve it by the rule
9163
+ * that consumer owns". It lives beside `CamProfile` because a rung is the only
9164
+ * operator-facing way to name a stream: a cam-stream id is broker-internal and
9165
+ * moves when a slot is reassigned, so a stored id silently stops meaning what
9166
+ * the operator picked. Today's one user is the audio analyser, whose rule is
9167
+ * "the lowest AVAILABLE native rung" (D537).
9168
+ */
9169
+ var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
9170
+ var CamStreamKindSchema = _enum([
9171
+ "pull-rtsp",
9172
+ "pull-rtmp",
9173
+ "pull-http",
9174
+ "pull-flv",
9175
+ "pull-rfc4571",
9176
+ "push-annexb",
9177
+ "derived"
9178
+ ]);
9179
+ var CamStreamResolutionSchema = object({
9180
+ width: number().int().positive().meta(readingMeta("none")),
9181
+ height: number().int().positive().meta(readingMeta("none"))
9182
+ });
9183
+ var CameraStreamSchema = object({
9184
+ /** Stable, provider-assigned id unique within the (deviceId) scope. */
9185
+ camStreamId: string().min(1),
9186
+ deviceId: number().int().nonnegative(),
9187
+ kind: CamStreamKindSchema,
9188
+ /** Required for pull-* kinds. Ignored for push-annexb. */
9189
+ url: string().optional(),
9190
+ codec: string().optional(),
9191
+ resolution: CamStreamResolutionSchema.optional(),
9192
+ fps: number().positive().optional(),
9193
+ /** Human label surfaced in the Admin UI "Camera Stream" dropdown. */
9194
+ label: string().optional(),
9195
+ /**
9196
+ * Device-level features the publisher advertised (e.g. `battery-operated`).
9197
+ * The broker, snapshot orchestrator, and prebuffer manager all consult
9198
+ * this list to derive policy — relaxed stall watchdog for battery
9199
+ * cams, prebuffer off by default, longer snapshot rate-limit, etc.
9200
+ *
9201
+ * Single source of truth replacing per-stream flags like the
9202
+ * historical `allowStall`: if the publisher knows the camera is
9203
+ * battery-powered, every downstream service derives the right policy
9204
+ * from this list.
9205
+ */
9206
+ deviceFeatures: array(string()).optional(),
9207
+ /**
9208
+ * Whether this stream participates in the broker's automatic profile
9209
+ * assignment (`computeInitialAssignment`). Defaults to `true`. Publishers
9210
+ * use `false` when they want a stream to be SELECTABLE in the UI but not
9211
+ * picked by default — e.g. Reolink publishes its native Baichuan streams
9212
+ * as `autoEligible: true` (the recommended path) and its RTSP / RTMP
9213
+ * mirrors as `autoEligible: false` (still pickable per slot, just not
9214
+ * the auto choice). Manual `assignProfile` calls remain valid for
9215
+ * non-eligible streams.
9216
+ */
9217
+ autoEligible: boolean().optional(),
9218
+ /**
9219
+ * The profile slot the PUBLISHER says this stream is: `low` for a stream
9220
+ * whose only sensible role is the low-bandwidth one, `high` for a main
9221
+ * stream. Consulted by the broker's automatic ranking
9222
+ * (`rankInitialAssignment`) BEFORE the pixel-count heuristic, and only there:
9223
+ * an operator's manual assignment still wins, and an absent hint is the
9224
+ * pixel-ranked default every camera had before this field existed.
9225
+ *
9226
+ * Why it exists: a camera with ONE stream ranked to `mid` — the slot no
9227
+ * storage class is written for on its own (`recordings` is high|mid,
9228
+ * `recordingsLow` is low) and the one profile scrub does not read. The robot
9229
+ * camera's single 720p relay is a `low` in every respect except the slot the
9230
+ * heuristic put it in.
9231
+ */
9232
+ profileHint: CamProfileSchema.optional(),
9233
+ /**
9234
+ * Transport-specific opaque metadata. The broker passes it through to
9235
+ * the source reader without inspecting it. Currently used by
9236
+ * `pull-rfc4571` streams to carry the upstream SDP (so the reader can
9237
+ * route RTP packets to the right depacketizer without an in-band
9238
+ * DESCRIBE phase). Other kinds typically leave it undefined.
9239
+ */
9240
+ metadata: record(string(), unknown()).optional()
9241
+ });
9242
+ var ProfileSlotStatusSchema = _enum([
9243
+ "unassigned",
9244
+ "idle",
9245
+ "connecting",
9246
+ "streaming",
9247
+ "error"
9248
+ ]);
9249
+ var ProfileSlotSchema = object({
9250
+ deviceId: number().int().nonnegative(),
9251
+ profile: CamProfileSchema,
9252
+ /** Broker id the rest of the system addresses: `${deviceId}/${profile}`. */
9253
+ brokerId: string(),
9254
+ /** `null` when the profile is unassigned. */
9255
+ sourceCamStreamId: string().nullable(),
9256
+ status: ProfileSlotStatusSchema,
9257
+ resolution: CamStreamResolutionSchema.optional(),
9258
+ codec: string().optional(),
9259
+ preBufferSec: number().nonnegative().optional(),
9260
+ errorMessage: string().optional()
9261
+ });
9262
+ /**
9263
+ * Zod schema for StreamSourceEntry — the canonical stream descriptor
9264
+ * exposed by ICameraDevice.getStreamSources() and consumed by the broker.
9265
+ */
9266
+ var StreamSourceEntrySchema$1 = object({
9267
+ id: string(),
9268
+ label: string(),
9269
+ protocol: _enum([
9270
+ "rtsp",
9271
+ "rtmp",
9272
+ "annexb",
9273
+ "http-mjpeg",
9274
+ "webrtc",
9275
+ "custom"
9276
+ ]),
9277
+ url: string().optional(),
9278
+ resolution: object({
9279
+ width: number(),
9280
+ height: number()
9281
+ }).readonly().optional(),
9282
+ fps: number().optional(),
9283
+ bitrate: number().optional(),
9284
+ codec: string().optional(),
9285
+ profileHint: CamProfileSchema.optional()
9286
+ });
9287
+ object({
9288
+ type: string(),
9289
+ url: string(),
9290
+ videoCodec: string().optional(),
9291
+ audioCodec: string().optional(),
9292
+ metadata: record(string(), unknown()).readonly().optional()
9293
+ });
9294
+ /**
9295
+ * Wire schema for `FrameHandle` — a zero-pixel, serialisable reference to a
9296
+ * frame in a shared-memory ring (Phase 5 / D9). Mirrors the `FrameHandle` TS
9297
+ * interface in `interfaces/frame-handle.ts` field-for-field so a cap method
9298
+ * (the runner's native-crop and pipeline-executor inputs) can carry handles
9299
+ * over tRPC / Moleculer.
9300
+ *
9301
+ * The `satisfies` assertion below pins this schema to the interface: if a
9302
+ * field is added to / removed from `FrameHandle` without a matching schema
9303
+ * edit, `z.infer<typeof FrameHandleSchema>` stops being assignable to
9304
+ * `FrameHandle` and the build fails — no silent drift.
9305
+ */
9306
+ var FrameHandleSchema = object({
9307
+ shmId: string(),
9308
+ slot: number().int().nonnegative(),
9309
+ seq: number().int().nonnegative(),
9310
+ width: number().int().positive(),
9311
+ height: number().int().positive(),
9312
+ format: _enum([
9313
+ "jpeg",
9314
+ "rgb",
9315
+ "bgr",
9316
+ "yuv420",
9317
+ "gray"
9318
+ ]),
9319
+ pts: number(),
9320
+ byteLength: number().int().nonnegative(),
9321
+ nodeId: string(),
9322
+ slotCount: number().int().positive()
9323
+ });
9324
+ /**
9325
+ * Wire schema for a decoded audio chunk (Phase 5 / D9). Mirrors the
9326
+ * `DecodedAudioChunk` TS interface in `interfaces/stream-broker.ts`: PCM
9327
+ * sample bytes plus the track parameters a consumer needs to interpret them.
9328
+ *
9329
+ * Audio chunks are tiny (a ~500ms AudioCodecSession window is a few KB of
9330
+ * Float32 PCM), so unlike video frames they ship their bytes INLINE over
9331
+ * tRPC / Moleculer — no shared-memory plane. `data` is typed `Uint8Array`
9332
+ * (the wire-serialisable supertype of `Buffer`); a `Buffer` is assignable to
9333
+ * it.
9334
+ */
9335
+ var AudioChunkFormatSchema = _enum(AUDIO_CHUNK_FORMATS);
9336
+ var DecodedAudioChunkSchema = object({
9337
+ data: _instanceof(Uint8Array),
9338
+ sampleRate: number().int().positive(),
9339
+ channels: number().int().positive(),
9340
+ timestamp: number(),
9341
+ /**
9342
+ * Byte format of `data`. ABSENT MEANS `f32le` — today's bytes, byte for
9343
+ * byte, for any peer that never heard of this field. A coded window
9344
+ * (`pcmu` / `pcma`, one byte per sample) is only ever emitted to a
9345
+ * subscription that DECLARED it accepts one, so absence can never mean
9346
+ * "coded bytes a consumer will read as floats" (D455).
9347
+ */
9348
+ format: AudioChunkFormatSchema.optional()
9349
+ });
9350
+ /**
9351
+ * Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). Audio ships
9352
+ * its bytes inline: a consumer asks the broker for the decoded audio-chunk
9353
+ * stream of one broker; the broker keeps a per-subscription bounded FIFO queue
9354
+ * the consumer drains via `pullAudioChunks`.
9355
+ */
9356
+ var SubscribeAudioChunksInputSchema = object({
9357
+ brokerId: string(),
9358
+ /** Short caller-identity tag (`audio-analyzer`, …) for `listClients`. */
9359
+ tag: string().optional(),
9360
+ /**
9361
+ * Byte formats this subscriber can READ, best first. The broker serves the
9362
+ * chunk's own format when it is in this list and expands to `f32le`
9363
+ * otherwise, so a subscriber is never handed bytes it cannot interpret.
9364
+ *
9365
+ * Absent (or without the source format) means `f32le` — the behaviour every
9366
+ * subscriber had before D455, unchanged. This is the negotiation half of
9367
+ * the source-bytes lever: it is what lets the broker and its consumers
9368
+ * deploy one at a time across three nodes.
9369
+ */
9370
+ accept: array(AudioChunkFormatSchema).readonly().optional()
9371
+ });
9372
+ /** Result of `stream-broker.subscribeAudioChunks`. */
9373
+ var SubscribeAudioChunksResultSchema = object({
9374
+ /** Opaque id passed to `pullAudioChunks` / `unsubscribeAudioChunks`. */
9375
+ subscriptionId: string() });
9376
+ var BrokerStatsSchema = object({
9377
+ status: _enum([
9378
+ "idle",
9379
+ "connecting",
9380
+ "streaming",
9381
+ "error",
9382
+ "stopped"
9383
+ ]),
9384
+ inputFps: number(),
9385
+ encodedSubscribers: number(),
9386
+ uptimeMs: number(),
9387
+ bitrateKbps: number(),
9388
+ idrIntervalMs: number(),
9389
+ codec: string().optional(),
9390
+ totalBytes: number(),
9391
+ packetCount: number(),
9392
+ rtspClients: number(),
9393
+ pipeClients: number(),
9394
+ preBufferSec: number(),
9395
+ preBufferMs: number(),
9396
+ preBufferPackets: number(),
9397
+ /**
9398
+ * Detected audio track parameters from the RTSP DESCRIBE / SDP. `null`
9399
+ * when the stream has no audio track or the broker is in cold start.
9400
+ * `supported = false` means the codec was detected but the local
9401
+ * decoder pipeline cannot produce PCM chunks (e.g. AAC without the
9402
+ * AAC pipeline wired). Surfaced in the UI device-overview so operators
9403
+ * can pre-pick the audio-analysis model that matches the codec.
9404
+ */
9405
+ audio: object({
9406
+ codec: string(),
9407
+ sampleRate: number(),
9408
+ channels: number(),
9409
+ supported: boolean()
9410
+ }).nullable().optional(),
9411
+ /**
9412
+ * BROKER-SIDE AUDIO MUTE (D83). `true` = this broker is deliberately
9413
+ * distributing none of the device's audio, on live or recording.
9414
+ *
9415
+ * Present so a silent camera can be told apart from a broken one on the
9416
+ * stream panel itself, without cross-referencing the switch group: a
9417
+ * broker holding an `audio` track descriptor while `audioMuted` is true is
9418
+ * working exactly as asked. `audioMutedDropped` counts the audio units
9419
+ * thrown away since the current dial — it is how you confirm from stats
9420
+ * alone that the mute is on the packet path and not merely persisted.
9421
+ */
9422
+ audioMuted: boolean().optional(),
9423
+ audioMutedDropped: number().optional()
9424
+ });
9425
+ /**
9426
+ * Exporter-facing "profile restream" entry. Returned by
9427
+ * `cameraStreams.getProfileRtspEntries` — one per ASSIGNED profile slot
9428
+ * (high/mid/low). `brokerId` is the PROFILE-keyed broker id
9429
+ * (`${deviceId}/${profile}`, e.g. `15/high`); its `url` is a broker
9430
+ * RTSP restream that aliases the profile's assigned source broker, so
9431
+ * every consumer dialling a profile converges on the same single pull.
9432
+ * `codec`/`resolution` describe the assigned SOURCE camStream (for the
9433
+ * `auto` resolution-closest pick). Distinct from `RtspRestreamEntry`
9434
+ * (raw per-camStream, live-view only).
9435
+ */
9436
+ var ProfileRtspEntrySchema = object({
9437
+ profile: CamProfileSchema,
9438
+ /** Profile-keyed broker id, format `${deviceId}/${profile}` (e.g. `"15/high"`). */
9439
+ brokerId: string(),
9440
+ url: string(),
9441
+ mutedUrl: string(),
9442
+ enabled: boolean(),
9443
+ codec: string().optional(),
9444
+ resolution: CamStreamResolutionSchema.optional()
9445
+ });
9446
+ /**
9447
+ * The model weight formats a catalog entry can ship. Schema-free on purpose:
9448
+ * `types/models.ts` builds Zod schemas from it, but a runner-closure consumer
9449
+ * (the model downloader in `@camstack/server/system`) needs only the list and must
9450
+ * reach it through `@camstack/types/addon` without building the cap schema
9451
+ * graph (D28).
9452
+ */
9453
+ var MODEL_FORMATS = [
9454
+ "onnx",
9455
+ "coreml",
9456
+ "openvino",
9457
+ "tflite",
9458
+ "pt",
9459
+ "gguf"
9460
+ ];
8947
9461
  /**
8948
9462
  * Per-call node pinning for `ctx.api` capability calls.
8949
9463
  *
@@ -9267,6 +9781,7 @@ var CAP_NAMES = Object.freeze([
9267
9781
  "mesh-network",
9268
9782
  "meter",
9269
9783
  "metrics-provider",
9784
+ "migrations",
9270
9785
  "model-convert",
9271
9786
  "model-distributor",
9272
9787
  "motion",
@@ -9299,6 +9814,7 @@ var CAP_NAMES = Object.freeze([
9299
9814
  "plate-gallery",
9300
9815
  "platform-probe",
9301
9816
  "power-meter",
9817
+ "power-source",
9302
9818
  "presence",
9303
9819
  "pressure-sensor",
9304
9820
  "privacy-mask",
@@ -9316,6 +9832,7 @@ var CAP_NAMES = Object.freeze([
9316
9832
  "remote-layout-components",
9317
9833
  "running-state",
9318
9834
  "scene-monitor",
9835
+ "schedulers",
9319
9836
  "script-runner",
9320
9837
  "server-management",
9321
9838
  "settings-store",
@@ -10261,7 +10778,8 @@ Object.freeze({
10261
10778
  "unlocked",
10262
10779
  "locking",
10263
10780
  "unlocking",
10264
- "jammed"
10781
+ "jammed",
10782
+ "door-open"
10265
10783
  ]
10266
10784
  }],
10267
10785
  "media-player": [
@@ -10648,6 +11166,13 @@ Object.freeze({
10648
11166
  "valueType": "number"
10649
11167
  }
10650
11168
  ],
11169
+ "power-source": [{
11170
+ "path": "switchOn",
11171
+ "valueType": "boolean"
11172
+ }, {
11173
+ "path": "offSeconds",
11174
+ "valueType": "number"
11175
+ }],
10651
11176
  "presence": [
10652
11177
  {
10653
11178
  "path": "state",
@@ -11161,7 +11686,7 @@ Object.freeze({
11161
11686
  * can answer "may a rule actuate this?" without importing the schema barrel
11162
11687
  * (~144MB RSS per runner, D28).
11163
11688
  *
11164
- * Coverage: 97 device-scoped capabilities.
11689
+ * Coverage: 98 device-scoped capabilities.
11165
11690
  */
11166
11691
  var DEVICE_SCOPED_CAPS = new Set([
11167
11692
  "accessories",
@@ -11230,6 +11755,7 @@ var DEVICE_SCOPED_CAPS = new Set([
11230
11755
  "pet-fountain",
11231
11756
  "pipeline-analytics",
11232
11757
  "power-meter",
11758
+ "power-source",
11233
11759
  "presence",
11234
11760
  "pressure-sensor",
11235
11761
  "privacy-mask",
@@ -11306,379 +11832,7 @@ function sleep(ms) {
11306
11832
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11307
11833
  }
11308
11834
  //#endregion
11309
- //#region ../types/dist/err-msg-BD5buxvT.mjs
11310
- var DeviceType = /* @__PURE__ */ function(DeviceType) {
11311
- DeviceType["Camera"] = "camera";
11312
- DeviceType["Hub"] = "hub";
11313
- DeviceType["Light"] = "light";
11314
- DeviceType["Siren"] = "siren";
11315
- DeviceType["Switch"] = "switch";
11316
- DeviceType["Sensor"] = "sensor";
11317
- DeviceType["Thermostat"] = "thermostat";
11318
- /** Air-conditioner / heat-pump climate device (HVAC) — shares the
11319
- * `climate-control` cap surface with `Thermostat` but renders a
11320
- * dedicated AC-appropriate control UI (mode chips, fan speed,
11321
- * independent vertical/horizontal swing). Sources: native Gree, and
11322
- * reusable by other AC integrations. */
11323
- DeviceType["Climate"] = "climate";
11324
- DeviceType["Button"] = "button";
11325
- /** Generic stateless event emitter — carries a device's EXACT declared
11326
- * event vocabulary verbatim (no normalization). Installed with the
11327
- * `event-emitter` cap. Sources: HA `event.*` entities (structured) and
11328
- * HA bus events (e.g. `zha_event`, generic). */
11329
- DeviceType["EventEmitter"] = "event-emitter";
11330
- /** Firmware/software update entity — current vs available version,
11331
- * updatable flag, update state, and an install action. Installed with
11332
- * the `update` cap. Sources: Homematic firmware-update channels (and
11333
- * reusable by other providers, e.g. HA `update.*` entities). */
11334
- DeviceType["Update"] = "update";
11335
- DeviceType["Generic"] = "generic";
11336
- /** Generic notification delivery target (HA `notify.<service>`, future
11337
- * Telegram / Discord / ntfy / SMTP, …). One device per delivery
11338
- * endpoint; the `notifier` cap defines the send surface. */
11339
- DeviceType["Notifier"] = "notifier";
11340
- /** Pre-recorded action sequence with optional parameters
11341
- * (HA `script.*`). Runnable via `script-runner` cap. */
11342
- DeviceType["Script"] = "script";
11343
- /** Automation rule (HA `automation.*`) — enable/disable + manual
11344
- * trigger surface exposed via `automation-control` cap. */
11345
- DeviceType["Automation"] = "automation";
11346
- /** Door / smart lock device (HA `lock.*`). `lock-control` cap. */
11347
- DeviceType["Lock"] = "lock";
11348
- /** Window covering, blinds, garage door, valve, etc. (HA `cover.*`,
11349
- * `valve.*`). `cover` cap with sub-roles for variant. */
11350
- DeviceType["Cover"] = "cover";
11351
- /** Pipe / water / gas valve with open/close/stop and optional
11352
- * position (HA `valve.*`). `valve` cap — a cover-sibling actuator
11353
- * modelled on the same open/closed lifecycle. */
11354
- DeviceType["Valve"] = "valve";
11355
- /** Humidifier / dehumidifier with on/off + target humidity + mode
11356
- * (HA `humidifier.*`). `humidifier` cap — a climate-family actuator
11357
- * modelled on the same target / mode lifecycle. */
11358
- DeviceType["Humidifier"] = "humidifier";
11359
- /** Water heater / boiler with target temperature + operation mode +
11360
- * away mode (HA `water_heater.*`). `water-heater` cap — a
11361
- * climate-family actuator. */
11362
- DeviceType["WaterHeater"] = "water-heater";
11363
- /** Ceiling / standing / exhaust fan (HA `fan.*`). `fan-control` cap. */
11364
- DeviceType["Fan"] = "fan";
11365
- /** Audio / video playback endpoint (HA `media_player.*`). Disjoint from
11366
- * the camera surface — those use `Camera`. `media-player` cap. */
11367
- DeviceType["MediaPlayer"] = "media-player";
11368
- /** Security panel / alarm system (HA `alarm_control_panel.*`).
11369
- * `alarm-panel` cap. */
11370
- DeviceType["AlarmPanel"] = "alarm-panel";
11371
- /** Generic user-settable input (HA `number` / `input_number` / `select`
11372
- * / `input_select` / `text` / `input_text` / `input_datetime`).
11373
- * Sub-type via `DeviceRole`: NumericControl / SelectControl /
11374
- * TextControl / DateTimeControl. */
11375
- DeviceType["Control"] = "control";
11376
- /** Person / device-tracker presence (HA `person.*`, `device_tracker.*`).
11377
- * `presence` cap. */
11378
- DeviceType["Presence"] = "presence";
11379
- /** Weather provider (HA `weather.*`). Tier-3, low MVP priority.
11380
- * `weather` cap. */
11381
- DeviceType["Weather"] = "weather";
11382
- /** Robot vacuum (HA `vacuum.*`). Tier-3. `vacuum-control` cap. */
11383
- DeviceType["Vacuum"] = "vacuum";
11384
- /** Robotic lawn mower (HA `lawn_mower.*`). Tier-3.
11385
- * `lawn-mower-control` cap. */
11386
- DeviceType["LawnMower"] = "lawn-mower";
11387
- /** Physical HA device group — parent container for entity-children
11388
- * adopted from a single HA device entry. Not renderable as a
11389
- * standalone device; exists only to anchor child entities. */
11390
- DeviceType["Container"] = "container";
11391
- /** Single still-image entity (HA `image.*`). Read-only display of an
11392
- * `entity_picture` signed URL the browser loads directly. `image` cap. */
11393
- DeviceType["Image"] = "image";
11394
- /** Smart pet feeder — cloud-connected food dispenser with a bowl food
11395
- * level, battery, desiccant life, feeding state and manual-feed /
11396
- * call-pet / maintenance actions. Installed with the `pet-feeder` cap;
11397
- * dual-hopper models (D4S/D4SH) expose per-hopper portions. Sources:
11398
- * native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
11399
- * integrations sharing the same food/desiccant/hopper surface. */
11400
- DeviceType["PetFeeder"] = "pet-feeder";
11401
- /** Pet water fountain — tank level, water drunk, pump and its duty cycle.
11402
- * Installed with the `pet-fountain` cap. Reaches CamStack as loose entities
11403
- * of a container and is BUILT as a composed device (D967). */
11404
- DeviceType["PetFountain"] = "pet-fountain";
11405
- /** Built-in dishwasher — program lifecycle, door / remote-start gates,
11406
- * salt and rinse-aid levels. Installed with the `dishwasher` cap.
11407
- * Sources: native Bosch Home Connect, reusable by similar integrations.
11408
- * A plug-built dishwasher carries running-state and power instead. */
11409
- DeviceType["Dishwasher"] = "dishwasher";
11410
- /** Washing machine. A plug-built one is running while its power stays
11411
- * above a threshold. No program cap — that surface is the dishwasher's. */
11412
- DeviceType["Washer"] = "washer";
11413
- /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
11414
- DeviceType["Dryer"] = "dryer";
11415
- /** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
11416
- * other radio-to-IP bridges). It carries the box's own facts (firmware,
11417
- * address, connection state) and the actions the box really supports
11418
- * (`reboot`, `device-admin-link`). Products are NOT its children — they are
11419
- * adopted beside it — and its connection settings stay on the integration. */
11420
- DeviceType["Gateway"] = "gateway";
11421
- return DeviceType;
11422
- }({});
11423
- var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
11424
- DeviceFeature["BatteryOperated"] = "battery-operated";
11425
- DeviceFeature["Rebootable"] = "rebootable";
11426
- /**
11427
- * Device supports an on-demand re-sync of its derived spec with its
11428
- * upstream source — drives the generic Re-sync button. The owning
11429
- * provider implements the action via the `device-adoption.resync` cap.
11430
- */
11431
- DeviceFeature["Resyncable"] = "resyncable";
11432
- DeviceFeature["NativeSnapshot"] = "native-snapshot";
11433
- DeviceFeature["DoorbellButton"] = "doorbell-button";
11434
- DeviceFeature["TwoWayAudio"] = "two-way-audio";
11435
- DeviceFeature["PanTiltZoom"] = "pan-tilt-zoom";
11436
- /**
11437
- * Camera supports the on-firmware autotrack subsystem (subject-
11438
- * following). Distinct from `PanTiltZoom` because not every PTZ
11439
- * camera ships autotrack — the admin UI uses this flag to gate
11440
- * the autotrack toggle / settings card without re-deriving from
11441
- * the cap registry. Mirrors `ptz-autotrack` cap registration:
11442
- * driver sets this feature when probe confirms the firmware
11443
- * surface, and registers the cap in the same code path.
11444
- */
11445
- DeviceFeature["PtzAutotrack"] = "ptz-autotrack";
11446
- /**
11447
- * Accessory exposes a "trigger on motion" toggle — the parent camera's
11448
- * motion detection automatically activates this device. Mirrors
11449
- * `motion-trigger` cap registration: drivers set this feature in the
11450
- * same code path that calls `ctx.registerNativeCap(motionTriggerCapability, ...)`.
11451
- *
11452
- * Used by admin UI (gate the in-hero `MotionTriggerToggle` against a
11453
- * fast scalar without binding fetch), notifier rules, and `listAll`
11454
- * filters that want "all devices with on-motion behaviour".
11455
- */
11456
- DeviceFeature["MotionTrigger"] = "motion-trigger";
11457
- /** Light supports rgb-triplet color via `color` cap. */
11458
- DeviceFeature["LightColorRgb"] = "light-color-rgb";
11459
- /** Light supports HSV color via `color` cap. */
11460
- DeviceFeature["LightColorHsv"] = "light-color-hsv";
11461
- /** Light supports color-temperature (mired) via `color` cap. */
11462
- DeviceFeature["LightColorMired"] = "light-color-mired";
11463
- /** Thermostat supports a `heat_cool` dual setpoint (targetLow +
11464
- * targetHigh). Gates the range slider UI. */
11465
- DeviceFeature["ClimateDualSetpoint"] = "climate-dual-setpoint";
11466
- /** Thermostat exposes target humidity and/or current humidity
11467
- * readings. Gates the humidity controls. */
11468
- DeviceFeature["ClimateHumidity"] = "climate-humidity";
11469
- /** Thermostat exposes a fan-mode selector. */
11470
- DeviceFeature["ClimateFanMode"] = "climate-fan-mode";
11471
- /** Thermostat exposes preset modes (eco / away / sleep / vendor). */
11472
- DeviceFeature["ClimatePreset"] = "climate-preset";
11473
- /** Thermostat exposes a vertical louver swing toggle. Gates the
11474
- * vertical-swing switch in the climate UI. Independent of horizontal. */
11475
- DeviceFeature["ClimateSwingVertical"] = "climate-swing-vertical";
11476
- /** Thermostat exposes a horizontal louver swing toggle. Gates the
11477
- * horizontal-swing switch in the climate UI. Independent of vertical. */
11478
- DeviceFeature["ClimateSwingHorizontal"] = "climate-swing-horizontal";
11479
- /** Cover exposes intermediate position control (0..100). Gates the
11480
- * position slider UI. */
11481
- DeviceFeature["CoverPositionable"] = "cover-positionable";
11482
- /** Cover exposes slat-tilt control. Gates the tilt slider UI. */
11483
- DeviceFeature["CoverTilt"] = "cover-tilt";
11484
- /** Valve exposes intermediate position control (0..100). Gates the
11485
- * position slider / drag surface UI. */
11486
- DeviceFeature["ValvePositionable"] = "valve-positionable";
11487
- /** Fan exposes a speed-percentage setter. Gates the speed slider UI. */
11488
- DeviceFeature["FanSpeed"] = "fan-speed";
11489
- /** Fan exposes a preset mode selector. */
11490
- DeviceFeature["FanPreset"] = "fan-preset";
11491
- /** Fan exposes blade direction (forward/reverse) — typical of
11492
- * ceiling fans. */
11493
- DeviceFeature["FanDirection"] = "fan-direction";
11494
- /** Fan exposes an oscillation toggle. */
11495
- DeviceFeature["FanOscillating"] = "fan-oscillating";
11496
- /** Lock requires a PIN code on lock/unlock. Gates the code-entry
11497
- * field on the UI lock-controls panel. */
11498
- DeviceFeature["LockPinRequired"] = "lock-pin-required";
11499
- /** Lock supports a latch-release ("open door") action distinct from
11500
- * unlock. Mirrors HA `LockEntityFeature.OPEN` (bit 1) in
11501
- * `supported_features`. Gates the Open Door button in the UI. */
11502
- DeviceFeature["LockOpen"] = "lock-open";
11503
- /** Media player exposes a seek-to-position surface. */
11504
- DeviceFeature["MediaPlayerSeek"] = "media-player-seek";
11505
- /** Media player exposes a volume-level setter. */
11506
- DeviceFeature["MediaPlayerVolume"] = "media-player-volume";
11507
- /** Media player exposes a mute toggle distinct from volume=0. */
11508
- DeviceFeature["MediaPlayerMute"] = "media-player-mute";
11509
- /** Media player exposes a shuffle toggle. */
11510
- DeviceFeature["MediaPlayerShuffle"] = "media-player-shuffle";
11511
- /** Media player exposes a repeat mode (off / all / one). */
11512
- DeviceFeature["MediaPlayerRepeat"] = "media-player-repeat";
11513
- /** Media player exposes a source / input selector. */
11514
- DeviceFeature["MediaPlayerSelectSource"] = "media-player-select-source";
11515
- /** Media player exposes a play-arbitrary-media surface (URL / id). */
11516
- DeviceFeature["MediaPlayerPlayMedia"] = "media-player-play-media";
11517
- /** Media player exposes next-track. */
11518
- DeviceFeature["MediaPlayerNext"] = "media-player-next";
11519
- /** Media player exposes previous-track. */
11520
- DeviceFeature["MediaPlayerPrevious"] = "media-player-previous";
11521
- /** Media player exposes stop distinct from pause. */
11522
- DeviceFeature["MediaPlayerStop"] = "media-player-stop";
11523
- /** Alarm panel requires a PIN code on arm/disarm. */
11524
- DeviceFeature["AlarmPinRequired"] = "alarm-pin-required";
11525
- /** Presence device carries GPS coordinates (lat/lng/accuracy) in
11526
- * addition to a textual location. */
11527
- DeviceFeature["PresenceGps"] = "presence-gps";
11528
- /** Notifier accepts an inline / URL image attachment. */
11529
- DeviceFeature["NotifierImage"] = "notifier-image";
11530
- /** Notifier accepts a priority hint (high/normal/low). */
11531
- DeviceFeature["NotifierPriority"] = "notifier-priority";
11532
- /** Notifier accepts a free-form `data` payload for platform-specific
11533
- * fields. */
11534
- DeviceFeature["NotifierData"] = "notifier-data";
11535
- /** Notifier supports interactive action buttons / callbacks. */
11536
- DeviceFeature["NotifierActions"] = "notifier-actions";
11537
- /** Notifier supports per-call recipient targeting (multi-user). */
11538
- DeviceFeature["NotifierRecipients"] = "notifier-recipients";
11539
- /** Script runner accepts a variables map on each run invocation. */
11540
- DeviceFeature["ScriptVariables"] = "script-variables";
11541
- /** Automation `trigger` accepts a skipCondition flag — fires the
11542
- * automation's actions while bypassing its condition block. */
11543
- DeviceFeature["AutomationSkipCondition"] = "automation-skip-condition";
11544
- /** Robot vacuum exposes a live cleaning map (image child). Gates the
11545
- * map tile in the vacuum UI. */
11546
- DeviceFeature["VacuumHasMap"] = "vacuum-has-map";
11547
- /** Robot vacuum exposes AI obstacle-detection toggles. Gates the AI
11548
- * switches group. */
11549
- DeviceFeature["VacuumHasAi"] = "vacuum-has-ai";
11550
- /** Robot mower exposes a live mowing map (SVG image child). Gates the
11551
- * mower map tile. */
11552
- DeviceFeature["MowerHasMap"] = "mower-has-map";
11553
- /** Robot mower exposes targeted mowing (all-area / zones / edges /
11554
- * spots) — gates the Mow action + map selectors. */
11555
- DeviceFeature["MowerHasTargetedMowing"] = "mower-has-targeted-mowing";
11556
- return DeviceFeature;
11557
- }({});
11558
- /**
11559
- * Semantic role a device plays within its parent. Populated by driver
11560
- * addons when creating accessory devices (Reolink siren/floodlight/
11561
- * PIR/chime/autotrack/doorbell, ONVIF relay outputs, …). Used by the
11562
- * admin UI to pick icons, labels, and widgets — a `Switch` with
11563
- * `role: Floodlight` renders as a bulb with a brightness slider,
11564
- * whereas a `Switch` with `role: Siren` renders as a klaxon.
11565
- *
11566
- * Undefined for top-level devices (cameras, NVRs, hubs). Persisted in
11567
- * sqlite as a nullable TEXT column — old rows keep working unchanged.
11568
- */
11569
- var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
11570
- DeviceRole["Siren"] = "siren";
11571
- DeviceRole["Floodlight"] = "floodlight";
11572
- DeviceRole["Spotlight"] = "spotlight";
11573
- DeviceRole["PirSensor"] = "pir-sensor";
11574
- DeviceRole["Chime"] = "chime";
11575
- DeviceRole["Autotrack"] = "autotrack";
11576
- DeviceRole["Nightvision"] = "nightvision";
11577
- DeviceRole["PrivacyMask"] = "privacy-mask";
11578
- DeviceRole["Doorbell"] = "doorbell";
11579
- /** Interior blind / shade / roller blind. HA `device_class: blind|shade`. */
11580
- DeviceRole["CoverBlind"] = "cover-blind";
11581
- /** Exterior roller shutter. HA `device_class: shutter`; Homematic BROLL. */
11582
- DeviceRole["CoverShutter"] = "cover-shutter";
11583
- /** Exterior awning. HA `device_class: awning`. */
11584
- DeviceRole["CoverAwning"] = "cover-awning";
11585
- /** Curtain / drape. HA `device_class: curtain`. */
11586
- DeviceRole["CoverCurtain"] = "cover-curtain";
11587
- /** Garage door. HA `device_class: garage`. A security actuator: exports gate it. */
11588
- DeviceRole["CoverGarage"] = "cover-garage";
11589
- /** Gate. HA `device_class: gate`. A security actuator: exports gate it. */
11590
- DeviceRole["CoverGate"] = "cover-gate";
11591
- /** Door. HA `device_class: door`. A security actuator: exports gate it. */
11592
- DeviceRole["CoverDoor"] = "cover-door";
11593
- /** Window. HA `device_class: window`. */
11594
- DeviceRole["CoverWindow"] = "cover-window";
11595
- /** Virtual HA toggle (input_boolean.*) — distinguishable from a
11596
- * real Switch device for UI rendering / export adapters. */
11597
- DeviceRole["BinaryHelper"] = "binary-helper";
11598
- /** A smart plug: a `switch` whose source says it is an outlet (HA
11599
- * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
11600
- DeviceRole["Outlet"] = "outlet";
11601
- /** Generic motion / occupancy / moving event source. Distinct from
11602
- * the camera accessory PirSensor role: that one is a camera child;
11603
- * this is a standalone HA / 3rd-party motion sensor. */
11604
- DeviceRole["MotionSensor"] = "motion-sensor";
11605
- DeviceRole["ContactSensor"] = "contact-sensor";
11606
- DeviceRole["LeakSensor"] = "leak-sensor";
11607
- DeviceRole["SmokeSensor"] = "smoke-sensor";
11608
- DeviceRole["COSensor"] = "co-sensor";
11609
- DeviceRole["GasSensor"] = "gas-sensor";
11610
- DeviceRole["TamperSensor"] = "tamper-sensor";
11611
- DeviceRole["VibrationSensor"] = "vibration-sensor";
11612
- DeviceRole["ConnectivitySensor"] = "connectivity-sensor";
11613
- DeviceRole["SoundSensor"] = "sound-sensor";
11614
- /** Fallback for `binary_sensor` without a known `device_class`. */
11615
- DeviceRole["BinarySensor"] = "binary-sensor";
11616
- DeviceRole["TemperatureSensor"] = "temperature-sensor";
11617
- DeviceRole["HumiditySensor"] = "humidity-sensor";
11618
- DeviceRole["AmbientLightSensor"] = "ambient-light-sensor";
11619
- DeviceRole["PressureSensor"] = "pressure-sensor";
11620
- /** Wind speed or direction (weather-station `wind-sensor` cap). */
11621
- DeviceRole["WindSensor"] = "wind-sensor";
11622
- /** Rain accumulation or rate (weather-station `rain-sensor` cap). */
11623
- DeviceRole["RainSensor"] = "rain-sensor";
11624
- /** UV index (weather-station `uv-sensor` cap). */
11625
- DeviceRole["UvSensor"] = "uv-sensor";
11626
- /** Solar irradiance W/m² (weather-station `solar-radiation-sensor` cap).
11627
- * Distinct from AmbientLightSensor (lux). */
11628
- DeviceRole["SolarRadiationSensor"] = "solar-radiation-sensor";
11629
- /** Soil moisture % (garden/weather `soil-moisture-sensor` cap). */
11630
- DeviceRole["SoilMoistureSensor"] = "soil-moisture-sensor";
11631
- DeviceRole["PowerSensor"] = "power-sensor";
11632
- DeviceRole["EnergySensor"] = "energy-sensor";
11633
- DeviceRole["VoltageSensor"] = "voltage-sensor";
11634
- DeviceRole["CurrentSensor"] = "current-sensor";
11635
- DeviceRole["AirQualitySensor"] = "air-quality-sensor";
11636
- /** Battery level (numeric % via `sensor` OR low-bool via
11637
- * `binary_sensor` — the cap distinguishes via the value type). */
11638
- DeviceRole["BatterySensor"] = "battery-sensor";
11639
- /** Fallback for `sensor` numeric without a known `device_class`. */
11640
- DeviceRole["NumericSensor"] = "numeric-sensor";
11641
- /** String / enum state (HA `sensor` with `state_class: enum` or
11642
- * `attributes.options`). */
11643
- DeviceRole["EnumSensor"] = "enum-sensor";
11644
- /** Date / timestamp state (HA `sensor` with `device_class: timestamp`
11645
- * or `date`). The slice carries the raw ISO string verbatim (hosted on
11646
- * the `enum-sensor` cap); the UI renders it locale-formatted. */
11647
- DeviceRole["DateTimeSensor"] = "datetime-sensor";
11648
- /** Last-resort fallback when nothing else matches. */
11649
- DeviceRole["GenericSensor"] = "generic-sensor";
11650
- DeviceRole["NumericControl"] = "numeric-control";
11651
- DeviceRole["SelectControl"] = "select-control";
11652
- DeviceRole["TextControl"] = "text-control";
11653
- DeviceRole["DateTimeControl"] = "datetime-control";
11654
- /** Mobile push notifier (HA `notify.mobile_app_*`) — supports
11655
- * rich features (image, priority, channel routing). */
11656
- DeviceRole["MobilePushNotifier"] = "mobile-push-notifier";
11657
- /** Chat / messaging service (HA `notify.telegram_*`,
11658
- * `notify.discord_*`, etc.). */
11659
- DeviceRole["MessagingNotifier"] = "messaging-notifier";
11660
- /** Email-based delivery (HA `notify.smtp`, etc.). */
11661
- DeviceRole["EmailNotifier"] = "email-notifier";
11662
- /** Fallback when the notifier service name doesn't match a known
11663
- * pattern. */
11664
- DeviceRole["GenericNotifier"] = "generic-notifier";
11665
- /** Emits a pulse train (infrared or RF) on request — the `ir-transmitter`
11666
- * cap. A `DeviceType.Generic` device. */
11667
- DeviceRole["IrTransmitter"] = "ir-transmitter";
11668
- return DeviceRole;
11669
- }({});
11670
- /**
11671
- import { errMsg } from '@camstack/types'
11672
- * Extract a human-readable message from an unknown error value.
11673
- * Replaces the ubiquitous `errMsg(err)` pattern.
11674
- */
11675
- function errMsg(err) {
11676
- if (err instanceof Error) return err.message;
11677
- if (typeof err === "string") return err;
11678
- return String(err);
11679
- }
11680
- //#endregion
11681
- //#region ../types/dist/composition-C7WdThmP.mjs
11835
+ //#region ../types/dist/composition-BBzVf2xd.mjs
11682
11836
  /**
11683
11837
  * Deterministic SHA-256 hash of an arbitrary serialisable value. The
11684
11838
  * canonical form sorts object keys alphabetically at every depth so two
@@ -13072,6 +13226,90 @@ var EVENT_OWNER_TYPE_SET = new Set(EVENT_OWNER_TYPES);
13072
13226
  function isEventOwnerType(ownerType) {
13073
13227
  return EVENT_OWNER_TYPE_SET.has(ownerType);
13074
13228
  }
13229
+ /**
13230
+ * What a published package needs from the rest of the install (D989).
13231
+ *
13232
+ * One published framework package (`@camstack/server`) carries the kernel, the
13233
+ * builtins and the capability contracts, so "which addon runs on which
13234
+ * framework" is a question of version, and the answer is DECLARED by the
13235
+ * package that knows it:
13236
+ *
13237
+ * - an addon says the oldest server it runs on (`requires.server: ">=X"`);
13238
+ * - the server says the oldest addon it works with
13239
+ * (`requires.addons: { "@camstack/addon-x": ">=Y" }`) and the oldest shell
13240
+ * around it (`requires.wrapper: { desktop, image }`);
13241
+ * - a wrapper (the desktop app, the container starter) says which server
13242
+ * range it can host, and reports its own kind and version.
13243
+ *
13244
+ * The publish workflow STAMPS these after the version bump
13245
+ * (`scripts/ci/stamp-requires.mjs`); nobody types a number by hand.
13246
+ *
13247
+ * A requirement that cannot be checked is NOT met: a shell that reports no
13248
+ * version counts as below the minimum, and an unreadable running version is
13249
+ * named in the result (a failed read is never a pass).
13250
+ */
13251
+ /** `>=MAJOR.MINOR.PATCH[-prerelease]` — the only range shape a requirement uses. */
13252
+ var MinVersionSchema = string().regex(/^>=\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/, "a minimum is written \">=X.Y.Z\"");
13253
+ object({
13254
+ /** The oldest `@camstack/server` this package runs on. */
13255
+ server: MinVersionSchema.optional(),
13256
+ /** The oldest installed version of each named addon package. */
13257
+ addons: record(string(), MinVersionSchema).optional(),
13258
+ /** The oldest shell around the server, per shell kind. */
13259
+ wrapper: object({
13260
+ desktop: MinVersionSchema.optional(),
13261
+ image: MinVersionSchema.optional()
13262
+ }).optional()
13263
+ });
13264
+ /** One unmet requirement; each names the versions on both sides. */
13265
+ var UnmetRequirementSchema = discriminatedUnion("kind", [
13266
+ object({
13267
+ kind: literal("server-too-old"),
13268
+ required: string(),
13269
+ /** `null` — the running server version could not be read. */
13270
+ running: string().nullable()
13271
+ }),
13272
+ object({
13273
+ kind: literal("addon-too-old"),
13274
+ packageName: string(),
13275
+ required: string(),
13276
+ /** `null` — the addon is not installed (or its version is unreadable). */
13277
+ running: string().nullable()
13278
+ }),
13279
+ object({
13280
+ kind: literal("wrapper-too-old"),
13281
+ wrapper: _enum(["desktop", "image"]),
13282
+ required: string(),
13283
+ /** `null` — the wrapper reported no version. */
13284
+ running: string().nullable()
13285
+ })
13286
+ ]);
13287
+ var RequiredAddonUpdateSchema = object({
13288
+ packageName: string(),
13289
+ /** The minimum the target server declares. */
13290
+ required: string(),
13291
+ /** What this node has; `null` when the addon is not installed. */
13292
+ installed: string().nullable()
13293
+ });
13294
+ /**
13295
+ * Which integration instance a settings form is for. An addon that serves several instances
13296
+ * (`instanceMode: multiple`) can show a different form per instance — an NVR integration
13297
+ * needs its connection, the default integration needs the addon's own sections.
13298
+ *
13299
+ * - `integrationId` is `null` in the create wizard: nothing exists yet, the operator is
13300
+ * setting up a NEW instance.
13301
+ * - `mode` is the wizard mode of the integration type (`getAvailableTypes`).
13302
+ * - `candidateStableId` is the discovery candidate a wizard was seeded from, if any.
13303
+ */
13304
+ var IntegrationInstanceRefSchema = object({
13305
+ integrationId: string().nullable(),
13306
+ mode: _enum([
13307
+ "standalone",
13308
+ "account",
13309
+ "broker"
13310
+ ]),
13311
+ candidateStableId: string().optional()
13312
+ });
13075
13313
  var EncodeProfileSchema = object({
13076
13314
  video: object({
13077
13315
  codec: _enum([
@@ -13639,6 +13877,613 @@ var CameraSwitchGroupSchema = object({
13639
13877
  */
13640
13878
  var DETECTION_PIPELINE_CAP_NAME = "detection-pipeline";
13641
13879
  /**
13880
+ * Query filter for settings-store collections.
13881
+ */
13882
+ var QueryFilterSchema = object({
13883
+ where: record(string(), unknown()).optional(),
13884
+ whereIn: record(string(), array(unknown())).optional(),
13885
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
13886
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
13887
+ whereNot: record(string(), unknown()).optional(),
13888
+ /** NULL-safe exclusion of a SET — `whereNot` for more than one value. An
13889
+ * empty list excludes nothing. See `QueryFilter.whereNotIn`. */
13890
+ whereNotIn: record(string(), array(unknown())).optional(),
13891
+ orderBy: object({
13892
+ field: string(),
13893
+ direction: _enum(["asc", "desc"])
13894
+ }).optional(),
13895
+ limit: number().optional(),
13896
+ offset: number().optional()
13897
+ });
13898
+ /**
13899
+ * The predicate half of a filter, for BULK MUTATIONS.
13900
+ *
13901
+ * Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
13902
+ * meaning for a statement that rewrites a set, and accepting them would invite
13903
+ * a caller to believe `limit` bounds the damage. Every field is optional here
13904
+ * only so the shape stays composable — the implementation REJECTS a filter
13905
+ * that compiles to no predicate, because that is the whole collection.
13906
+ */
13907
+ var MutationFilterSchema = object({
13908
+ where: record(string(), unknown()).optional(),
13909
+ whereIn: record(string(), array(unknown())).optional(),
13910
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
13911
+ whereNot: record(string(), unknown()).optional(),
13912
+ whereNotIn: record(string(), array(unknown())).optional()
13913
+ });
13914
+ /**
13915
+ * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
13916
+ *
13917
+ * `as` names the slot in the result, so the SAME column may be asked twice with
13918
+ * two operations (`MIN(startMs)` and `MAX(startMs)` in one round trip) — which
13919
+ * a `Record<column, op>` shape could not express.
13920
+ */
13921
+ var AggregateFieldSchema = object({
13922
+ /** Result key. */
13923
+ as: string().min(1),
13924
+ /** Column to aggregate. Must be a real column of a declared collection. */
13925
+ field: string().min(1),
13926
+ op: _enum([
13927
+ "sum",
13928
+ "min",
13929
+ "max"
13930
+ ])
13931
+ });
13932
+ /**
13933
+ * `COUNT(*)` plus one number per requested field.
13934
+ *
13935
+ * `null` means NO ROW MATCHED, never zero: a `SUM` over an empty set and a sum
13936
+ * that really is 0 are different facts, and an accounting caller that renders
13937
+ * "0 bytes, oldest = 0" for "nothing here" reports a lie about a disk.
13938
+ */
13939
+ var AggregateResultSchema = object({
13940
+ count: number().int(),
13941
+ values: record(string(), number().nullable())
13942
+ });
13943
+ /** A single stored record: `{ id, data }`. */
13944
+ var SettingsRecordSchema = object({
13945
+ id: string(),
13946
+ data: record(string(), unknown())
13947
+ });
13948
+ /**
13949
+ * One record of a BULK insert — {@link SettingsRecordSchema} with the id made
13950
+ * optional.
13951
+ *
13952
+ * A separate schema rather than loosening the shared one: every other method
13953
+ * on this cap addresses a row BY its id, and making that field optional
13954
+ * everywhere would turn a forgotten key into a silently generated one on
13955
+ * `update` and `delete` as well.
13956
+ */
13957
+ var BulkRecordSchema = object({
13958
+ id: string().optional(),
13959
+ data: record(string(), unknown())
13960
+ });
13961
+ /**
13962
+ * The database FILES one `sqlite-settings` engine process owns (D851).
13963
+ * Closed on purpose: a file is a thing the engine opens, backs up and gives a
13964
+ * serving thread, so a new one is a code change, never a string a caller
13965
+ * invents. `settings` is `camstack.db` and the default for every collection
13966
+ * that names no file.
13967
+ */
13968
+ var DataStoreDatabaseSchema = _enum([
13969
+ "settings",
13970
+ "events",
13971
+ "history"
13972
+ ]);
13973
+ DataStoreDatabaseSchema.options;
13974
+ /**
13975
+ * One write inside a {@link settingsStoreCapability.methods.transact} call.
13976
+ * The three that a "replace this set, bump that revision" write needs, plus
13977
+ * the batched upsert a rollup writer needs.
13978
+ */
13979
+ var TransactOpSchema = discriminatedUnion("op", [
13980
+ object({
13981
+ op: literal("deleteWhere"),
13982
+ collection: string(),
13983
+ filter: MutationFilterSchema
13984
+ }),
13985
+ object({
13986
+ op: literal("insertMany"),
13987
+ collection: string(),
13988
+ records: array(BulkRecordSchema).readonly()
13989
+ }),
13990
+ object({
13991
+ op: literal("update"),
13992
+ collection: string(),
13993
+ id: string(),
13994
+ data: record(string(), unknown())
13995
+ }),
13996
+ object({
13997
+ op: literal("upsertMany"),
13998
+ collection: string(),
13999
+ conflict: array(string()).readonly(),
14000
+ records: array(BulkRecordSchema).readonly()
14001
+ })
14002
+ ]);
14003
+ /**
14004
+ * The precondition of a transaction: the row `key` of `collection` must hold
14005
+ * `equals` in `column`, read INSIDE the transaction.
14006
+ */
14007
+ var TransactGuardSchema = object({
14008
+ collection: string(),
14009
+ key: string(),
14010
+ column: string(),
14011
+ equals: unknown()
14012
+ });
14013
+ /**
14014
+ * Column declaration for a structured (SQL-backed) collection.
14015
+ *
14016
+ * Logical types — the backend translates each to the matching SQLite
14017
+ * storage class and handles per-type marshaling:
14018
+ * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
14019
+ * - `JSON` — TEXT under the hood; serialised on write, parsed on read
14020
+ * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
14021
+ */
14022
+ var CollectionColumnSchema = object({
14023
+ name: string(),
14024
+ type: _enum([
14025
+ "TEXT",
14026
+ "INTEGER",
14027
+ "REAL",
14028
+ "JSON",
14029
+ "BOOLEAN"
14030
+ ]),
14031
+ primaryKey: boolean().optional(),
14032
+ notNull: boolean().optional(),
14033
+ unique: boolean().optional(),
14034
+ /**
14035
+ * Column DEFAULT. Required for B2: the four `ensureTable` consumers declare
14036
+ * columns like `enabled INTEGER NOT NULL DEFAULT 1`, and without this the
14037
+ * collection surface simply cannot express their existing tables — which is
14038
+ * why they were still on the uncapped `table*` API.
14039
+ */
14040
+ defaultValue: union([
14041
+ string(),
14042
+ number(),
14043
+ boolean()
14044
+ ]).optional()
14045
+ });
14046
+ var CollectionIndexSchema = object({
14047
+ name: string(),
14048
+ columns: array(string()).readonly(),
14049
+ unique: boolean().optional()
14050
+ });
14051
+ /**
14052
+ * The input of `declareCollection` — ONE schema for the door and for the
14053
+ * engine behind it, so the two surfaces cannot drift (the dispatcher forwards
14054
+ * the payload untouched).
14055
+ *
14056
+ * - `database` — the file the collection lives in (D851). Absent means
14057
+ * `settings`. A declaration naming a file other than the one the table is
14058
+ * already in is REFUSED by the engine, never moved.
14059
+ * - `primaryKey` — a COMPOSITE key, as column names. A single name is the same
14060
+ * as `primaryKey: true` on that column. A composite-keyed collection has no
14061
+ * `id`: it is written with `insertMany` / `upsertMany` / `transact` and read
14062
+ * with `query` / `count` / `aggregate` / `deleteWhere`; the calls that
14063
+ * address one row by id are refused.
14064
+ * - `withoutRowid` — emit `WITHOUT ROWID`. Requires a primary key.
14065
+ */
14066
+ var DeclareCollectionInputSchema = object({
14067
+ namespace: string().optional(),
14068
+ collection: string(),
14069
+ columns: array(CollectionColumnSchema).readonly(),
14070
+ indexes: array(CollectionIndexSchema).readonly().optional(),
14071
+ database: DataStoreDatabaseSchema.optional(),
14072
+ primaryKey: array(string()).readonly().optional(),
14073
+ withoutRowid: boolean().optional()
14074
+ });
14075
+ method(object({
14076
+ namespace: string().optional(),
14077
+ collection: string(),
14078
+ key: string()
14079
+ }), unknown(), { auth: "admin" }), method(object({
14080
+ namespace: string().optional(),
14081
+ collection: string(),
14082
+ key: string(),
14083
+ value: unknown()
14084
+ }), _void(), {
14085
+ kind: "mutation",
14086
+ auth: "admin"
14087
+ }), method(object({
14088
+ namespace: string().optional(),
14089
+ collection: string(),
14090
+ filter: QueryFilterSchema.optional(),
14091
+ columns: array(string()).readonly().optional()
14092
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
14093
+ namespace: string().optional(),
14094
+ collection: string(),
14095
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
14096
+ * index on the collection. Never empty. */
14097
+ conflict: array(string()).readonly(),
14098
+ record: BulkRecordSchema
14099
+ }), object({
14100
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
14101
+ * row's own id on an update. */
14102
+ id: union([string(), number()]) }), {
14103
+ kind: "mutation",
14104
+ auth: "admin"
14105
+ }), method(object({
14106
+ namespace: string().optional(),
14107
+ collection: string(),
14108
+ conflict: array(string()).readonly(),
14109
+ records: array(BulkRecordSchema).readonly()
14110
+ }), object({ upserted: number().int() }), {
14111
+ kind: "mutation",
14112
+ auth: "admin"
14113
+ }), method(object({
14114
+ namespace: string().optional(),
14115
+ collection: string(),
14116
+ record: object({
14117
+ id: string().optional(),
14118
+ data: record(string(), unknown())
14119
+ })
14120
+ }), object({
14121
+ /**
14122
+ * The id the row ACTUALLY got (D473): the one supplied, the UUID minted
14123
+ * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
14124
+ * primary key — which is the only place an auto key is knowable.
14125
+ */
14126
+ id: union([string(), number()]) }), {
14127
+ kind: "mutation",
14128
+ auth: "admin"
14129
+ }), method(object({
14130
+ namespace: string().optional(),
14131
+ collection: string(),
14132
+ records: array(BulkRecordSchema).readonly()
14133
+ }), object({ inserted: number().int() }), {
14134
+ kind: "mutation",
14135
+ auth: "admin"
14136
+ }), method(object({
14137
+ namespace: string().optional(),
14138
+ guard: TransactGuardSchema.optional(),
14139
+ ops: array(TransactOpSchema).readonly()
14140
+ }), object({
14141
+ applied: boolean(),
14142
+ current: unknown().optional()
14143
+ }), {
14144
+ kind: "mutation",
14145
+ auth: "admin"
14146
+ }), method(object({
14147
+ namespace: string().optional(),
14148
+ collection: string(),
14149
+ id: string(),
14150
+ data: record(string(), unknown())
14151
+ }), _void(), {
14152
+ kind: "mutation",
14153
+ auth: "admin"
14154
+ }), method(object({
14155
+ namespace: string().optional(),
14156
+ collection: string(),
14157
+ key: string()
14158
+ }), _void(), {
14159
+ kind: "mutation",
14160
+ auth: "admin"
14161
+ }), method(object({
14162
+ namespace: string().optional(),
14163
+ collection: string(),
14164
+ filter: MutationFilterSchema
14165
+ }), object({ deleted: number().int() }), {
14166
+ kind: "mutation",
14167
+ auth: "admin"
14168
+ }), method(object({
14169
+ namespace: string().optional(),
14170
+ collection: string(),
14171
+ filter: MutationFilterSchema,
14172
+ data: record(string(), unknown())
14173
+ }), object({ updated: number().int() }), {
14174
+ kind: "mutation",
14175
+ auth: "admin"
14176
+ }), method(object({
14177
+ namespace: string().optional(),
14178
+ collection: string(),
14179
+ filter: QueryFilterSchema.optional()
14180
+ }), number(), { auth: "admin" }), method(object({
14181
+ namespace: string().optional(),
14182
+ collection: string(),
14183
+ fields: array(AggregateFieldSchema).readonly(),
14184
+ filter: QueryFilterSchema.optional()
14185
+ }), AggregateResultSchema, { auth: "admin" }), method(object({
14186
+ namespace: string().optional(),
14187
+ collection: string(),
14188
+ field: string(),
14189
+ bucketSize: number().int().positive(),
14190
+ origin: number().int(),
14191
+ filter: QueryFilterSchema.optional()
14192
+ }), array(object({
14193
+ bucket: number().int(),
14194
+ count: number().int()
14195
+ })).readonly(), { auth: "admin" }), method(object({
14196
+ namespace: string().optional(),
14197
+ collection: string()
14198
+ }), boolean(), { auth: "admin" }), method(DeclareCollectionInputSchema, _void(), {
14199
+ kind: "mutation",
14200
+ auth: "admin"
14201
+ });
14202
+ var MigrationIdSchema = string().regex(/^[a-z0-9-]+\/\d{4}-\d{2}-\d{2}-[a-z0-9-]+$/, "a migration id is \"<owner>/<yyyy-mm-dd>-<slug>\"");
14203
+ var MigrationLevelSchema = _enum([
14204
+ "hub-store",
14205
+ "addon-state",
14206
+ "node-config",
14207
+ "viewer",
14208
+ "addon-internal"
14209
+ ]);
14210
+ var MigrationStoreSchema = _enum([
14211
+ "settings",
14212
+ "events",
14213
+ "history",
14214
+ "node-local",
14215
+ "addon-files"
14216
+ ]);
14217
+ var MigrationScopeSchema = _enum(["cluster", "per-node"]);
14218
+ /** What depends on a migration while it is pending: `block-owner` is the default. */
14219
+ var MigrationOnFailureSchema = _enum(["block-owner", "tolerate"]);
14220
+ /**
14221
+ * `sql` is a hub-only body over the owner's connection. `plan` returns edits the
14222
+ * engine applies inside the migration's own transaction (hub-owned: computed in
14223
+ * that transaction; addon-owned: computed by the owner and shipped to the engine).
14224
+ * `procedure` is addon-owned work that is not SQL (keychain, integration registry,
14225
+ * device tombstones); the ledger records its outcome and it must be idempotent.
14226
+ */
14227
+ var MigrationBodyKindSchema = _enum([
14228
+ "sql",
14229
+ "plan",
14230
+ "procedure"
14231
+ ]);
14232
+ var MigrationMetaSchema = object({
14233
+ id: MigrationIdSchema,
14234
+ /** `hub`, an addon id, `node` or `viewer`. */
14235
+ owner: string().regex(/^[a-z0-9-]+$/),
14236
+ level: MigrationLevelSchema,
14237
+ /** Strictly increasing per owner: the order within an owner. */
14238
+ seq: number().int().positive(),
14239
+ /** Ids of any owner: the order across owners. */
14240
+ dependsOn: array(MigrationIdSchema),
14241
+ store: MigrationStoreSchema,
14242
+ scope: MigrationScopeSchema,
14243
+ onFailure: MigrationOnFailureSchema,
14244
+ bodyKind: MigrationBodyKindSchema,
14245
+ description: string().min(1),
14246
+ /** The marked field the migration's spec must assert survived. */
14247
+ fixtureMark: string().min(1)
14248
+ }).refine((m) => m.id.startsWith(`${m.owner}/`), {
14249
+ message: "the id must start with \"<owner>/\"",
14250
+ path: ["id"]
14251
+ });
14252
+ /** A cycle in the dependency graph, named by the ids on it. */
14253
+ var MigrationCycleError = class extends Error {
14254
+ ids;
14255
+ constructor(ids) {
14256
+ super(`migration dependsOn cycle: ${ids.join(" -> ")}`);
14257
+ this.ids = ids;
14258
+ this.name = "MigrationCycleError";
14259
+ }
14260
+ };
14261
+ /**
14262
+ * Topological order over `dependsOn` plus the implicit edge "a lower `seq` of an
14263
+ * owner runs first". Ties are broken by `(owner, seq)`. Throws
14264
+ * {@link MigrationCycleError} on a cycle. A `dependsOn` id that is not in the list
14265
+ * is ignored here — the catalogue schema is what refuses it.
14266
+ */
14267
+ function orderMigrations(items) {
14268
+ const byId = new Map(items.map((item) => [item.id, item]));
14269
+ const deps = /* @__PURE__ */ new Map();
14270
+ const ownerSorted = /* @__PURE__ */ new Map();
14271
+ for (const item of items) {
14272
+ const list = ownerSorted.get(item.owner) ?? [];
14273
+ list.push(item);
14274
+ ownerSorted.set(item.owner, list);
14275
+ }
14276
+ for (const item of items) deps.set(item.id, new Set(item.dependsOn.filter((id) => byId.has(id))));
14277
+ for (const list of ownerSorted.values()) {
14278
+ list.sort((a, b) => a.seq - b.seq);
14279
+ for (let i = 1; i < list.length; i++) {
14280
+ const previous = list[i - 1];
14281
+ const current = list[i];
14282
+ if (previous !== void 0 && current !== void 0) deps.get(current.id)?.add(previous.id);
14283
+ }
14284
+ }
14285
+ const tie = (a, b) => a.owner < b.owner ? -1 : a.owner > b.owner ? 1 : a.seq - b.seq;
14286
+ const done = /* @__PURE__ */ new Set();
14287
+ const ordered = [];
14288
+ let remaining = [...items];
14289
+ while (remaining.length > 0) {
14290
+ const ready = remaining.filter((item) => [...deps.get(item.id) ?? []].every((dep) => done.has(dep)));
14291
+ if (ready.length === 0) throw new MigrationCycleError(remaining.map((item) => item.id).sort());
14292
+ ready.sort(tie);
14293
+ const next = ready[0];
14294
+ if (next === void 0) break;
14295
+ ordered.push(next);
14296
+ done.add(next.id);
14297
+ remaining = remaining.filter((item) => item.id !== next.id);
14298
+ }
14299
+ return ordered;
14300
+ }
14301
+ /** Every way a list of migrations (in declaration order) is not a catalogue. */
14302
+ function catalogueIssues(items) {
14303
+ const issues = [];
14304
+ const seen = /* @__PURE__ */ new Set();
14305
+ const known = new Set(items.map((item) => item.id));
14306
+ const lastSeq = /* @__PURE__ */ new Map();
14307
+ const seqUsed = /* @__PURE__ */ new Map();
14308
+ for (const item of items) {
14309
+ if (seen.has(item.id)) issues.push({
14310
+ code: "duplicate-id",
14311
+ ids: [item.id],
14312
+ message: `duplicate id ${item.id}`
14313
+ });
14314
+ seen.add(item.id);
14315
+ const used = seqUsed.get(item.owner) ?? /* @__PURE__ */ new Set();
14316
+ if (used.has(item.seq)) issues.push({
14317
+ code: "duplicate-seq",
14318
+ ids: [item.id],
14319
+ message: `owner ${item.owner} has two migrations with seq ${item.seq}`
14320
+ });
14321
+ else if (item.seq < (lastSeq.get(item.owner) ?? 0)) issues.push({
14322
+ code: "seq-order",
14323
+ ids: [item.id],
14324
+ message: `seq ${item.seq} of ${item.id} does not increase in declaration order (owner ${item.owner})`
14325
+ });
14326
+ used.add(item.seq);
14327
+ seqUsed.set(item.owner, used);
14328
+ lastSeq.set(item.owner, Math.max(item.seq, lastSeq.get(item.owner) ?? 0));
14329
+ for (const dep of item.dependsOn) if (!known.has(dep)) issues.push({
14330
+ code: "unknown-dependency",
14331
+ ids: [item.id, dep],
14332
+ message: `${item.id} dependsOn unknown id ${dep}`
14333
+ });
14334
+ }
14335
+ try {
14336
+ orderMigrations(items);
14337
+ } catch (err) {
14338
+ if (err instanceof MigrationCycleError) issues.push({
14339
+ code: "cycle",
14340
+ ids: err.ids,
14341
+ message: err.message
14342
+ });
14343
+ else throw err;
14344
+ }
14345
+ return issues;
14346
+ }
14347
+ array(MigrationMetaSchema).superRefine((items, ctx) => {
14348
+ for (const issue of catalogueIssues(items)) ctx.addIssue({
14349
+ code: "custom",
14350
+ message: `${issue.code}: ${issue.message}`
14351
+ });
14352
+ });
14353
+ /**
14354
+ * The canonical JSON (`preconditionOf`) of the value a plan read; the apply step
14355
+ * re-reads, and a plan whose read no longer matches is refused (the migration is
14356
+ * `failed`, nothing it edited is kept).
14357
+ */
14358
+ var PreconditionSchema = string().min(1).optional();
14359
+ var MigrationPlanSchema = object({ edits: array(discriminatedUnion("op", [
14360
+ object({
14361
+ op: literal("setKeys"),
14362
+ scope: string().min(1),
14363
+ values: record(string(), unknown()),
14364
+ precondition: PreconditionSchema
14365
+ }),
14366
+ object({
14367
+ op: literal("deleteKeys"),
14368
+ scope: string().min(1),
14369
+ keys: array(string().min(1)).min(1),
14370
+ precondition: PreconditionSchema
14371
+ }),
14372
+ object({
14373
+ op: literal("putRow"),
14374
+ table: string().min(1),
14375
+ key: record(string(), unknown()),
14376
+ row: record(string(), unknown()),
14377
+ precondition: PreconditionSchema
14378
+ }),
14379
+ object({
14380
+ op: literal("deleteRow"),
14381
+ table: string().min(1),
14382
+ key: record(string(), unknown()),
14383
+ precondition: PreconditionSchema
14384
+ }),
14385
+ object({
14386
+ op: literal("upsertRecord"),
14387
+ kind: string().min(1),
14388
+ recordKey: string().min(1),
14389
+ value: unknown(),
14390
+ precondition: PreconditionSchema
14391
+ }),
14392
+ object({
14393
+ op: literal("deleteRecord"),
14394
+ kind: string().min(1),
14395
+ recordKey: string().min(1),
14396
+ precondition: PreconditionSchema
14397
+ })
14398
+ ])) });
14399
+ /**
14400
+ * What a `procedure` body reports. `already-done` means the rerun observed the
14401
+ * work finished (a crash between the work and the ledger row, or a manual fix)
14402
+ * and did none of it again; the ledger records `applied` either way.
14403
+ */
14404
+ var MigrationProcedureResultSchema = object({
14405
+ outcome: _enum(["done", "already-done"]),
14406
+ summary: record(string(), unknown())
14407
+ });
14408
+ /** What an owner hands the engine when one of its migrations ran. */
14409
+ var MigrationOwnerResultSchema = discriminatedUnion("kind", [
14410
+ object({
14411
+ kind: literal("plan"),
14412
+ plan: MigrationPlanSchema
14413
+ }),
14414
+ object({
14415
+ kind: literal("procedure"),
14416
+ result: MigrationProcedureResultSchema
14417
+ }),
14418
+ object({
14419
+ kind: literal("failed"),
14420
+ error: string().min(1)
14421
+ })
14422
+ ]);
14423
+ /**
14424
+ * `held`: a restore of a pre-migration snapshot undid this id; it does not rerun
14425
+ * until the operator retries or a new code version ships.
14426
+ * `skipped-newer`: the database knows an id this code does not.
14427
+ */
14428
+ var MigrationStatusSchema = _enum([
14429
+ "applied",
14430
+ "failed",
14431
+ "blocked",
14432
+ "running",
14433
+ "held",
14434
+ "skipped-newer"
14435
+ ]);
14436
+ var MigrationLedgerEntrySchema = object({
14437
+ id: string(),
14438
+ database: DataStoreDatabaseSchema,
14439
+ owner: string(),
14440
+ level: MigrationLevelSchema,
14441
+ seq: number().int().nonnegative(),
14442
+ status: MigrationStatusSchema,
14443
+ attempts: number().int().nonnegative(),
14444
+ appliedAt: number().nullable(),
14445
+ codeVersion: string().nullable(),
14446
+ /** What the apply changed: rows per table, as the engine counted them. */
14447
+ planSummary: record(string(), unknown()).nullable(),
14448
+ progress: record(string(), unknown()).nullable(),
14449
+ error: string().nullable(),
14450
+ /** Why it is `blocked` or `held` (`dependency-failed`, `snapshot-refused`, …). */
14451
+ reason: string().nullable(),
14452
+ nodeId: string().nullable(),
14453
+ bootId: string().nullable()
14454
+ });
14455
+ /** A catalogued migration that is not applied, with where it stands. */
14456
+ var MigrationPendingEntrySchema = object({
14457
+ meta: MigrationMetaSchema,
14458
+ database: DataStoreDatabaseSchema,
14459
+ state: _enum([
14460
+ "pending",
14461
+ "failed",
14462
+ "blocked",
14463
+ "held"
14464
+ ]),
14465
+ reason: string().nullable()
14466
+ });
14467
+ /** Rows per table before and after a dry run, and what it would have changed. */
14468
+ var MigrationDryRunResultSchema = object({
14469
+ id: string(),
14470
+ ok: boolean(),
14471
+ error: string().nullable(),
14472
+ rowsChanged: number().int().nonnegative(),
14473
+ tables: array(object({
14474
+ table: string(),
14475
+ before: number().int().nonnegative(),
14476
+ after: number().int().nonnegative()
14477
+ }))
14478
+ });
14479
+ /** One of an owner's migrations, as the engine tells the owner to treat it this boot. */
14480
+ var MigrationOwnerStepSchema = object({
14481
+ id: MigrationIdSchema,
14482
+ action: _enum(["run", "skip"]),
14483
+ /** The ledger state the engine found: `pending` has no row yet. */
14484
+ state: union([literal("pending"), MigrationStatusSchema])
14485
+ });
14486
+ /**
13642
14487
  * The trainer contract — what the hub hands the cloud trainer and what it
13643
14488
  * gets back (cloud training, D805+).
13644
14489
  *
@@ -17674,6 +18519,41 @@ var ACTUATOR_CAP_VOCABULARY = {
17674
18519
  icon: "droplets"
17675
18520
  }]
17676
18521
  },
18522
+ "power-source": {
18523
+ scope: "device",
18524
+ noun: t("Power source", "Alimentazione"),
18525
+ actions: [
18526
+ {
18527
+ id: "powerSource.on",
18528
+ method: "powerOn",
18529
+ label: t("Power on", "Dai corrente"),
18530
+ phrase: t("Power on {device}", "Dai corrente a {device}"),
18531
+ icon: "power"
18532
+ },
18533
+ {
18534
+ id: "powerSource.off",
18535
+ method: "powerOff",
18536
+ label: t("Power off", "Togli corrente"),
18537
+ phrase: t("Cut the power of {device}", "Togli la corrente a {device}"),
18538
+ icon: "power-off"
18539
+ },
18540
+ {
18541
+ id: "powerSource.forceRestart",
18542
+ method: "forceRestart",
18543
+ label: t("Force restart", "Riavvio forzato"),
18544
+ phrase: t("Force restart {device} by cutting its power", "Riavvia {device} togliendo la corrente"),
18545
+ icon: "refresh-cw"
18546
+ }
18547
+ ],
18548
+ state: [{
18549
+ kind: "boolean",
18550
+ field: "switchOn",
18551
+ label: t("Power switch on", "Presa accesa"),
18552
+ whenTrue: t("When the power of {device} comes on", "Quando arriva la corrente a {device}"),
18553
+ whenFalse: t("When the power of {device} is cut", "Quando manca la corrente a {device}"),
18554
+ icon: "power"
18555
+ }]
18556
+ },
17677
18557
  "lock-control": {
17678
18558
  scope: "device",
17679
18559
  noun: t("Lock", "Serratura"),
@@ -17755,6 +18635,11 @@ var ACTUATOR_CAP_VOCABULARY = {
17755
18635
  value: "jammed",
17756
18636
  label: t("Jammed", "Inceppata"),
17757
18637
  phrase: t("When {device} jams", "Quando {device} si inceppa")
18638
+ },
18639
+ {
18640
+ value: "door-open",
18641
+ label: t("Door open", "Porta aperta"),
18642
+ phrase: t("When the door of {device} opens", "Quando la porta di {device} si apre")
17758
18643
  }
17759
18644
  ],
17760
18645
  icon: "lock"
@@ -18144,7 +19029,7 @@ var ACTUATOR_CAP_VOCABULARY = {
18144
19029
  method: "setDirection",
18145
19030
  label: t("Spin forward", "Rotazione avanti"),
18146
19031
  phrase: t("Spin {device} forward", "Fai girare {device} in avanti"),
18147
- icon: "rotate-cw",
19032
+ icon: "refresh-cw",
18148
19033
  effect: {
18149
19034
  field: "direction",
18150
19035
  value: "forward"
@@ -21222,6 +22107,7 @@ var SYSTEM_CAP_VOCABULARY = {
21222
22107
  "login-method": system(IDENTITY),
21223
22108
  "mesh-network": system(NETWORK),
21224
22109
  "metrics-provider": system(CLUSTER),
22110
+ migrations: system(INTERNAL),
21225
22111
  "model-convert": system(ML),
21226
22112
  "model-distributor": system(ML),
21227
22113
  "mqtt-broker": system(NETWORK),
@@ -21245,6 +22131,7 @@ var SYSTEM_CAP_VOCABULARY = {
21245
22131
  records: system(INTERNAL),
21246
22132
  remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
21247
22133
  "remote-layout-components": system("the palette of the remote template editor — read-only data, never actuated"),
22134
+ schedulers: system("time schedulers — authored on their own page, not device actions"),
21248
22135
  "server-management": system(CLUSTER),
21249
22136
  "settings-store": system(INTERNAL),
21250
22137
  "smtp-provider": system(INTERNAL),
@@ -42368,6 +43255,122 @@ var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
42368
43255
  StationaryPresentLeafSchema,
42369
43256
  ExpressionLeafSchema
42370
43257
  ]));
43258
+ var EVENT_KINDS = new Set([
43259
+ "device-becomes",
43260
+ "device-event",
43261
+ "schedule",
43262
+ "manual",
43263
+ "interval",
43264
+ "detection",
43265
+ "stationary"
43266
+ ]);
43267
+ function isEventLeaf(node) {
43268
+ return EVENT_KINDS.has(node.kind);
43269
+ }
43270
+ function isLeaf(node) {
43271
+ return node.kind !== "all" && node.kind !== "any" && node.kind !== "not";
43272
+ }
43273
+ /** Depth of a tree, root container = 1. A bare leaf is depth 1. */
43274
+ function whenDepth(node) {
43275
+ if (isLeaf(node)) return 1;
43276
+ if (node.kind === "not") return 1 + whenDepth(node.child);
43277
+ let deepest = 0;
43278
+ for (const child of node.children) deepest = Math.max(deepest, whenDepth(child));
43279
+ return 1 + deepest;
43280
+ }
43281
+ /** Leaves only — containers do not count toward the leaf budget. */
43282
+ function countWhenLeaves(node) {
43283
+ if (isLeaf(node)) return 1;
43284
+ if (node.kind === "not") return countWhenLeaves(node.child);
43285
+ let total = 0;
43286
+ for (const child of node.children) total += countWhenLeaves(child);
43287
+ return total;
43288
+ }
43289
+ /** Every leaf with its path (`when`, `when.0`, `when.0.1`, `when.0` under a `not`). */
43290
+ function forEachLeaf(node, visit, path = "when") {
43291
+ if (isLeaf(node)) {
43292
+ visit(node, path);
43293
+ return;
43294
+ }
43295
+ if (node.kind === "not") {
43296
+ forEachLeaf(node.child, visit, `${path}.0`);
43297
+ return;
43298
+ }
43299
+ node.children.forEach((child, i) => {
43300
+ forEachLeaf(child, visit, `${path}.${i}`);
43301
+ });
43302
+ }
43303
+ function hasEventLeaves(node) {
43304
+ let found = false;
43305
+ forEachLeaf(node, (leaf) => {
43306
+ if (isEventLeaf(leaf)) found = true;
43307
+ });
43308
+ return found;
43309
+ }
43310
+ function hasEmptyContainer(node) {
43311
+ if (isLeaf(node)) return false;
43312
+ if (node.kind === "not") return hasEmptyContainer(node.child);
43313
+ if (node.children.length === 0) return true;
43314
+ return node.children.some(hasEmptyContainer);
43315
+ }
43316
+ var LEAF_WORDS = {
43317
+ "device-becomes": "a device value that becomes",
43318
+ "device-event": "a device event",
43319
+ schedule: "a schedule",
43320
+ manual: "a manual run",
43321
+ interval: "an interval",
43322
+ detection: "a detection",
43323
+ stationary: "a parked object appearing or leaving"
43324
+ };
43325
+ function say(kind) {
43326
+ return LEAF_WORDS[kind] ?? kind;
43327
+ }
43328
+ /**
43329
+ * The cheapest set of event leaves that makes `node` true, with negation pushed
43330
+ * down to the leaves first (De Morgan), so `not(any(A, B))` and `all(not A,
43331
+ * not B)` are the same shape to the check. A negated event is its own error: an
43332
+ * event is an instant, "the event did not occur" is true at every other moment.
43333
+ */
43334
+ function eventNeed(node, path, negated) {
43335
+ if (isLeaf(node)) {
43336
+ if (!isEventLeaf(node)) return { paths: [] };
43337
+ if (negated) return { error: `"not" is applied to ${say(node.kind)} (${path}) — an event is an instant, so "it did not happen" is true at every other moment and can never start a run. Negate a state ("is") instead, or remove the "not"` };
43338
+ return { paths: [{
43339
+ path,
43340
+ kind: node.kind
43341
+ }] };
43342
+ }
43343
+ if (node.kind === "not") return eventNeed(node.child, `${path}.0`, !negated);
43344
+ const conjunctive = node.kind === "all" !== negated;
43345
+ const parts = [];
43346
+ for (let i = 0; i < node.children.length; i += 1) {
43347
+ const child = node.children[i];
43348
+ if (child === void 0) continue;
43349
+ const need = eventNeed(child, `${path}.${i}`, negated);
43350
+ if ("error" in need) return need;
43351
+ parts.push(need);
43352
+ }
43353
+ if (conjunctive) return { paths: parts.flatMap((p) => p.paths) };
43354
+ let best;
43355
+ for (const part of parts) if (best === void 0 || part.paths.length < best.paths.length) best = part;
43356
+ return best ?? { paths: [] };
43357
+ }
43358
+ /**
43359
+ * Author-time bounds and sense check of a `when` tree. Returns `null` when it
43360
+ * is legal, else a message the editor shows verbatim.
43361
+ */
43362
+ function validateWhenTree(when) {
43363
+ if (hasEmptyContainer(when)) return "a group is empty — group existing rows instead of leaving a container to fill";
43364
+ const depth = whenDepth(when);
43365
+ if (depth > 4) return `the rules nest ${depth} levels deep — the limit is 4. "not (any of …)" is the same as "all of (not …)", which is one level shallower`;
43366
+ const leaves = countWhenLeaves(when);
43367
+ if (leaves > 32) return `the rules use ${leaves} rows — the limit is 32`;
43368
+ const need = eventNeed(when, "when", false);
43369
+ if ("error" in need) return need.error;
43370
+ const [first, second] = need.paths;
43371
+ if (first !== void 0 && second !== void 0) return `${say(first.kind)} (${first.path}) and ${say(second.kind)} (${second.path}) would both have to happen at the same instant, and an event happens at one moment — this can never run. Use "any of" to start on either, or turn one of them into a state ("is") that must already hold`;
43372
+ return null;
43373
+ }
42371
43374
  /**
42372
43375
  * The recipe shape before the single `when` tree: a flat list of triggers
42373
43376
  * (OR'd, each an instant) and an optional condition tree. Stored recipes —
@@ -42723,6 +43726,187 @@ function convertLegacyRecipe(legacy) {
42723
43726
  var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
42724
43727
  new Set(Object.values(COCO_TO_MACRO.mapping));
42725
43728
  /**
43729
+ * Schedulers (D983) — an entity that performs actions over the day.
43730
+ *
43731
+ * A scheduler has profiles; a profile has weekdays and a timeline of POINTS.
43732
+ * A point is an `edge` (run once at its time), a `hold` (from this point until
43733
+ * the next point of the profile, keep the steps' effects true) or a `release`
43734
+ * (end the current hold without acting). The scheduler AND each profile carry
43735
+ * a manual `enabled` flag and an optional `activeWhen`: a `when` STATE tree
43736
+ * (facts only — an event leaf is an instant and cannot be "in force").
43737
+ *
43738
+ * This file is the data half: the schemas, and the author-time rules that
43739
+ * decide whether a scheduler can be saved at all (a hold needs an action that
43740
+ * leads to a state; a sun point needs the site location). It holds no runtime.
43741
+ */
43742
+ /** A `when` tree that is a STATE: no event leaf, and legal as a tree. */
43743
+ var SchedulerStateTreeSchema = AutomationWhenSchema.superRefine((when, ctx) => {
43744
+ if (hasEventLeaves(when)) {
43745
+ ctx.addIssue({
43746
+ code: "custom",
43747
+ message: "a scheduler condition is a state — an event (\"becomes\", \"detects\", \"at a time\") is an instant and cannot be in force"
43748
+ });
43749
+ return;
43750
+ }
43751
+ const problem = validateWhenTree(when);
43752
+ if (problem !== null) ctx.addIssue({
43753
+ code: "custom",
43754
+ message: problem
43755
+ });
43756
+ });
43757
+ /** A point's moment: a clock time or a sun event. A one-shot date is not a position on a day. */
43758
+ var SchedulerTimeSpecSchema = TimeSpecSchema.refine((spec) => spec.kind !== "date", { message: "a scheduler point is a clock time or a sun event — a one-shot date has no place on a timeline" });
43759
+ var SchedulerPointModeSchema = _enum([
43760
+ "edge",
43761
+ "hold",
43762
+ "release"
43763
+ ]);
43764
+ /** What a hold does when someone changes the device by hand. */
43765
+ var HoldOverrideSchema = discriminatedUnion("mode", [object({ mode: literal("respect") }), object({
43766
+ mode: literal("reassert"),
43767
+ /** How long the manual state is left alone before the hold puts its own back. */
43768
+ graceMs: number().int().min(0).max(24 * 36e5)
43769
+ })]);
43770
+ /** A missed edge up to this late still runs. */
43771
+ var DEFAULT_MAX_LATE_MS = 10 * 6e4;
43772
+ var SchedulerPointSchema = object({
43773
+ id: string().min(1).max(64),
43774
+ at: SchedulerTimeSpecSchema,
43775
+ mode: SchedulerPointModeSchema,
43776
+ /** The actions of an `edge` or `hold`; a `release` has none. */
43777
+ steps: array(ActionStepSchema).max(20).default([]),
43778
+ /** Checked when the point runs (an edge) or is applied (a hold). */
43779
+ conditions: SchedulerStateTreeSchema.optional(),
43780
+ /** Hold only; absent means `respect`. */
43781
+ override: HoldOverrideSchema.optional(),
43782
+ /** Hold only: re-applications per window; absent means {@link DEFAULT_MAX_HOLD_ATTEMPTS}. */
43783
+ maxHoldAttempts: number().int().min(1).max(20).optional()
43784
+ });
43785
+ var SchedulerProfileSchema = object({
43786
+ id: string().min(1).max(64),
43787
+ name: string().min(1).max(120),
43788
+ enabled: boolean().default(true),
43789
+ activeWhen: SchedulerStateTreeSchema.optional(),
43790
+ days: array(WeekdayKeySchema).min(1).max(7),
43791
+ points: array(SchedulerPointSchema).max(50)
43792
+ });
43793
+ /** What an operator writes. */
43794
+ var SchedulerInputSchema = object({
43795
+ name: string().min(1).max(120),
43796
+ enabled: boolean().default(true),
43797
+ activeWhen: SchedulerStateTreeSchema.optional(),
43798
+ /** Higher wins when two holds want the same device in different states. */
43799
+ priority: number().int().min(0).max(100).default(0),
43800
+ maxLateMs: number().int().min(1e3).max(24 * 36e5).default(DEFAULT_MAX_LATE_MS),
43801
+ profiles: array(SchedulerProfileSchema).max(20)
43802
+ });
43803
+ var SchedulerSchema = SchedulerInputSchema.extend({
43804
+ id: string().min(1),
43805
+ createdAt: number(),
43806
+ updatedAt: number()
43807
+ });
43808
+ /** Every outcome a scheduler reports to the runs ledger (`AutomationRunTrace.scheduler`). */
43809
+ var SchedulerOutcomeSchema = _enum([
43810
+ "done",
43811
+ "missed",
43812
+ "inactive",
43813
+ "condition-false",
43814
+ "failed",
43815
+ "held",
43816
+ "gave-up",
43817
+ "overridden",
43818
+ "suspended-manual"
43819
+ ]);
43820
+ _enum([
43821
+ "not-found",
43822
+ "invalid-scheduler",
43823
+ "invalid-date"
43824
+ ]);
43825
+ var LOCAL_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
43826
+ /** One point of the day, placed. `at` is `null` when the sun never crosses that day. */
43827
+ var SchedulerPreviewPointSchema = object({
43828
+ pointId: string(),
43829
+ mode: SchedulerPointModeSchema,
43830
+ at: number().nullable(),
43831
+ /** A hold: when its window ends (the next point, else the end of the day). */
43832
+ until: number().nullable(),
43833
+ /** The point's steps are held on a different scheduler's claim at this time — see `conflicts`. */
43834
+ stepCount: number().int()
43835
+ });
43836
+ var SchedulerPreviewSchema = object({
43837
+ schedulerId: string(),
43838
+ /** The site-local day previewed. */
43839
+ date: string().regex(LOCAL_DATE_PATTERN),
43840
+ timeZone: string(),
43841
+ /** Why no profile is in force, or `null` when one is. */
43842
+ idleReason: string().nullable(),
43843
+ /** The profile in force that day: first in list order whose rules hold. */
43844
+ profileId: string().nullable(),
43845
+ points: array(SchedulerPreviewPointSchema)
43846
+ });
43847
+ /** Two holds that want the same device in different states at the same time. */
43848
+ var SchedulerConflictSchema = object({
43849
+ deviceId: number().int(),
43850
+ cap: string(),
43851
+ field: string(),
43852
+ mine: object({
43853
+ profileId: string(),
43854
+ pointId: string(),
43855
+ value: union([
43856
+ string(),
43857
+ number(),
43858
+ boolean()
43859
+ ]),
43860
+ priority: number().int()
43861
+ }),
43862
+ other: object({
43863
+ schedulerId: string(),
43864
+ schedulerName: string(),
43865
+ profileId: string(),
43866
+ pointId: string(),
43867
+ value: union([
43868
+ string(),
43869
+ number(),
43870
+ boolean()
43871
+ ]),
43872
+ priority: number().int()
43873
+ }),
43874
+ /** The overlapping stretch, as site-local minutes of the day. */
43875
+ fromMinute: number().int(),
43876
+ toMinute: number().int(),
43877
+ /** `mine` | `other` | `tie` — who holds when it happens (a tie: the one that started first). */
43878
+ winner: _enum([
43879
+ "mine",
43880
+ "other",
43881
+ "tie"
43882
+ ])
43883
+ });
43884
+ var HoldTargetStatusSchema = _enum([
43885
+ "pending",
43886
+ "held",
43887
+ "suspended-manual",
43888
+ "gave-up",
43889
+ "overridden"
43890
+ ]);
43891
+ var SchedulerStatusSchema = object({
43892
+ schedulerId: string(),
43893
+ profileId: string().nullable(),
43894
+ /** The hold in force right now, with where each target stands. */
43895
+ hold: object({
43896
+ pointId: string(),
43897
+ until: number(),
43898
+ /** `true` while the scheduler or profile `activeWhen` is false. */
43899
+ suspendedInactive: boolean(),
43900
+ targets: array(object({
43901
+ deviceId: number().int(),
43902
+ cap: string(),
43903
+ field: string(),
43904
+ status: HoldTargetStatusSchema,
43905
+ attempts: number().int()
43906
+ }))
43907
+ }).nullable()
43908
+ });
43909
+ /**
42726
43910
  * Run traces — what an automation (or, later, an Action run) DID, node by node,
42727
43911
  * so the editor can paint every block green or red with its reason.
42728
43912
  *
@@ -42858,7 +44042,13 @@ var AutomationRunTraceSchema = object({
42858
44042
  /** How many identical consecutive skips this trace stands for (>= 1). */
42859
44043
  repeat: number().int().min(1),
42860
44044
  /** When the latest of those repeats happened. */
42861
- lastAt: number()
44045
+ lastAt: number(),
44046
+ /**
44047
+ * A scheduler point's own outcome (D983). `outcome` carries the closest
44048
+ * automation outcome so a reader that does not know schedulers still reads
44049
+ * the trace; this field says exactly what happened.
44050
+ */
44051
+ scheduler: SchedulerOutcomeSchema.optional()
42862
44052
  });
42863
44053
  /**
42864
44054
  * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
@@ -43483,7 +44673,7 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
43483
44673
  * rebuilds without manual reload.
43484
44674
  *
43485
44675
  * The hub-local builtin `addon-pages-aggregator` (see
43486
- * `@camstack/system/builtins/addon-pages-aggregator`) registers the
44676
+ * `@camstack/server/system/builtins/addon-pages-aggregator`) registers the
43487
44677
  * provider. Splitting the public aggregator from the raw collection
43488
44678
  * keeps both ends in codegen — there's no hand-written
43489
44679
  * `addon-pages.router.ts` wrapper anymore.
@@ -43609,6 +44799,10 @@ method(object({
43609
44799
  }), method(object({
43610
44800
  addonId: string(),
43611
44801
  nodeId: string().optional()
44802
+ }), SettingsSchemaWithValuesSchema.nullable()), method(object({
44803
+ addonId: string(),
44804
+ nodeId: string().optional(),
44805
+ instance: IntegrationInstanceRefSchema
43612
44806
  }), SettingsSchemaWithValuesSchema.nullable()), method(object({
43613
44807
  addonId: string(),
43614
44808
  nodeId: string().optional()
@@ -43798,7 +44992,7 @@ listWidgets: method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "ad
43798
44992
  * manual reload — same scheme used by `addon-pages`.
43799
44993
  *
43800
44994
  * The hub-local builtin `addon-widgets-aggregator` (see
43801
- * `@camstack/system/builtins/addon-widgets-aggregator`) registers the
44995
+ * `@camstack/server/system/builtins/addon-widgets-aggregator`) registers the
43802
44996
  * provider. Splitting the public aggregator from the raw collection
43803
44997
  * keeps both ends in codegen — there's no hand-written wrapper.
43804
44998
  */
@@ -44159,329 +45353,6 @@ method(object({
44159
45353
  password: string()
44160
45354
  }), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
44161
45355
  /**
44162
- * Query filter for settings-store collections.
44163
- */
44164
- var QueryFilterSchema = object({
44165
- where: record(string(), unknown()).optional(),
44166
- whereIn: record(string(), array(unknown())).optional(),
44167
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
44168
- /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
44169
- whereNot: record(string(), unknown()).optional(),
44170
- /** NULL-safe exclusion of a SET — `whereNot` for more than one value. An
44171
- * empty list excludes nothing. See `QueryFilter.whereNotIn`. */
44172
- whereNotIn: record(string(), array(unknown())).optional(),
44173
- orderBy: object({
44174
- field: string(),
44175
- direction: _enum(["asc", "desc"])
44176
- }).optional(),
44177
- limit: number().optional(),
44178
- offset: number().optional()
44179
- });
44180
- /**
44181
- * The predicate half of a filter, for BULK MUTATIONS.
44182
- *
44183
- * Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
44184
- * meaning for a statement that rewrites a set, and accepting them would invite
44185
- * a caller to believe `limit` bounds the damage. Every field is optional here
44186
- * only so the shape stays composable — the implementation REJECTS a filter
44187
- * that compiles to no predicate, because that is the whole collection.
44188
- */
44189
- var MutationFilterSchema = object({
44190
- where: record(string(), unknown()).optional(),
44191
- whereIn: record(string(), array(unknown())).optional(),
44192
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
44193
- whereNot: record(string(), unknown()).optional(),
44194
- whereNotIn: record(string(), array(unknown())).optional()
44195
- });
44196
- /**
44197
- * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
44198
- *
44199
- * `as` names the slot in the result, so the SAME column may be asked twice with
44200
- * two operations (`MIN(startMs)` and `MAX(startMs)` in one round trip) — which
44201
- * a `Record<column, op>` shape could not express.
44202
- */
44203
- var AggregateFieldSchema = object({
44204
- /** Result key. */
44205
- as: string().min(1),
44206
- /** Column to aggregate. Must be a real column of a declared collection. */
44207
- field: string().min(1),
44208
- op: _enum([
44209
- "sum",
44210
- "min",
44211
- "max"
44212
- ])
44213
- });
44214
- /**
44215
- * `COUNT(*)` plus one number per requested field.
44216
- *
44217
- * `null` means NO ROW MATCHED, never zero: a `SUM` over an empty set and a sum
44218
- * that really is 0 are different facts, and an accounting caller that renders
44219
- * "0 bytes, oldest = 0" for "nothing here" reports a lie about a disk.
44220
- */
44221
- var AggregateResultSchema = object({
44222
- count: number().int(),
44223
- values: record(string(), number().nullable())
44224
- });
44225
- /** A single stored record: `{ id, data }`. */
44226
- var SettingsRecordSchema = object({
44227
- id: string(),
44228
- data: record(string(), unknown())
44229
- });
44230
- /**
44231
- * One record of a BULK insert — {@link SettingsRecordSchema} with the id made
44232
- * optional.
44233
- *
44234
- * A separate schema rather than loosening the shared one: every other method
44235
- * on this cap addresses a row BY its id, and making that field optional
44236
- * everywhere would turn a forgotten key into a silently generated one on
44237
- * `update` and `delete` as well.
44238
- */
44239
- var BulkRecordSchema = object({
44240
- id: string().optional(),
44241
- data: record(string(), unknown())
44242
- });
44243
- /**
44244
- * The database FILES one `sqlite-settings` engine process owns (D851).
44245
- * Closed on purpose: a file is a thing the engine opens, backs up and gives a
44246
- * serving thread, so a new one is a code change, never a string a caller
44247
- * invents. `settings` is `camstack.db` and the default for every collection
44248
- * that names no file.
44249
- */
44250
- var DataStoreDatabaseSchema = _enum([
44251
- "settings",
44252
- "events",
44253
- "history"
44254
- ]);
44255
- DataStoreDatabaseSchema.options;
44256
- /**
44257
- * One write inside a {@link settingsStoreCapability.methods.transact} call.
44258
- * The three that a "replace this set, bump that revision" write needs, plus
44259
- * the batched upsert a rollup writer needs.
44260
- */
44261
- var TransactOpSchema = discriminatedUnion("op", [
44262
- object({
44263
- op: literal("deleteWhere"),
44264
- collection: string(),
44265
- filter: MutationFilterSchema
44266
- }),
44267
- object({
44268
- op: literal("insertMany"),
44269
- collection: string(),
44270
- records: array(BulkRecordSchema).readonly()
44271
- }),
44272
- object({
44273
- op: literal("update"),
44274
- collection: string(),
44275
- id: string(),
44276
- data: record(string(), unknown())
44277
- }),
44278
- object({
44279
- op: literal("upsertMany"),
44280
- collection: string(),
44281
- conflict: array(string()).readonly(),
44282
- records: array(BulkRecordSchema).readonly()
44283
- })
44284
- ]);
44285
- /**
44286
- * The precondition of a transaction: the row `key` of `collection` must hold
44287
- * `equals` in `column`, read INSIDE the transaction.
44288
- */
44289
- var TransactGuardSchema = object({
44290
- collection: string(),
44291
- key: string(),
44292
- column: string(),
44293
- equals: unknown()
44294
- });
44295
- /**
44296
- * Column declaration for a structured (SQL-backed) collection.
44297
- *
44298
- * Logical types — the backend translates each to the matching SQLite
44299
- * storage class and handles per-type marshaling:
44300
- * - `TEXT` / `INTEGER` / `REAL` — native SQLite types, pass-through
44301
- * - `JSON` — TEXT under the hood; serialised on write, parsed on read
44302
- * - `BOOLEAN` — INTEGER 0/1 under the hood; coerced both directions
44303
- */
44304
- var CollectionColumnSchema = object({
44305
- name: string(),
44306
- type: _enum([
44307
- "TEXT",
44308
- "INTEGER",
44309
- "REAL",
44310
- "JSON",
44311
- "BOOLEAN"
44312
- ]),
44313
- primaryKey: boolean().optional(),
44314
- notNull: boolean().optional(),
44315
- unique: boolean().optional(),
44316
- /**
44317
- * Column DEFAULT. Required for B2: the four `ensureTable` consumers declare
44318
- * columns like `enabled INTEGER NOT NULL DEFAULT 1`, and without this the
44319
- * collection surface simply cannot express their existing tables — which is
44320
- * why they were still on the uncapped `table*` API.
44321
- */
44322
- defaultValue: union([
44323
- string(),
44324
- number(),
44325
- boolean()
44326
- ]).optional()
44327
- });
44328
- var CollectionIndexSchema = object({
44329
- name: string(),
44330
- columns: array(string()).readonly(),
44331
- unique: boolean().optional()
44332
- });
44333
- /**
44334
- * The input of `declareCollection` — ONE schema for the door and for the
44335
- * engine behind it, so the two surfaces cannot drift (the dispatcher forwards
44336
- * the payload untouched).
44337
- *
44338
- * - `database` — the file the collection lives in (D851). Absent means
44339
- * `settings`. A declaration naming a file other than the one the table is
44340
- * already in is REFUSED by the engine, never moved.
44341
- * - `primaryKey` — a COMPOSITE key, as column names. A single name is the same
44342
- * as `primaryKey: true` on that column. A composite-keyed collection has no
44343
- * `id`: it is written with `insertMany` / `upsertMany` / `transact` and read
44344
- * with `query` / `count` / `aggregate` / `deleteWhere`; the calls that
44345
- * address one row by id are refused.
44346
- * - `withoutRowid` — emit `WITHOUT ROWID`. Requires a primary key.
44347
- */
44348
- var DeclareCollectionInputSchema = object({
44349
- namespace: string().optional(),
44350
- collection: string(),
44351
- columns: array(CollectionColumnSchema).readonly(),
44352
- indexes: array(CollectionIndexSchema).readonly().optional(),
44353
- database: DataStoreDatabaseSchema.optional(),
44354
- primaryKey: array(string()).readonly().optional(),
44355
- withoutRowid: boolean().optional()
44356
- });
44357
- method(object({
44358
- namespace: string().optional(),
44359
- collection: string(),
44360
- key: string()
44361
- }), unknown(), { auth: "admin" }), method(object({
44362
- namespace: string().optional(),
44363
- collection: string(),
44364
- key: string(),
44365
- value: unknown()
44366
- }), _void(), {
44367
- kind: "mutation",
44368
- auth: "admin"
44369
- }), method(object({
44370
- namespace: string().optional(),
44371
- collection: string(),
44372
- filter: QueryFilterSchema.optional(),
44373
- columns: array(string()).readonly().optional()
44374
- }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
44375
- namespace: string().optional(),
44376
- collection: string(),
44377
- /** Declared columns identifying the slot; must be covered by a UNIQUE
44378
- * index on the collection. Never empty. */
44379
- conflict: array(string()).readonly(),
44380
- record: BulkRecordSchema
44381
- }), object({
44382
- /** The id the row HAS: assigned by SQLite on an insert, or the existing
44383
- * row's own id on an update. */
44384
- id: union([string(), number()]) }), {
44385
- kind: "mutation",
44386
- auth: "admin"
44387
- }), method(object({
44388
- namespace: string().optional(),
44389
- collection: string(),
44390
- conflict: array(string()).readonly(),
44391
- records: array(BulkRecordSchema).readonly()
44392
- }), object({ upserted: number().int() }), {
44393
- kind: "mutation",
44394
- auth: "admin"
44395
- }), method(object({
44396
- namespace: string().optional(),
44397
- collection: string(),
44398
- record: object({
44399
- id: string().optional(),
44400
- data: record(string(), unknown())
44401
- })
44402
- }), object({
44403
- /**
44404
- * The id the row ACTUALLY got (D473): the one supplied, the UUID minted
44405
- * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
44406
- * primary key — which is the only place an auto key is knowable.
44407
- */
44408
- id: union([string(), number()]) }), {
44409
- kind: "mutation",
44410
- auth: "admin"
44411
- }), method(object({
44412
- namespace: string().optional(),
44413
- collection: string(),
44414
- records: array(BulkRecordSchema).readonly()
44415
- }), object({ inserted: number().int() }), {
44416
- kind: "mutation",
44417
- auth: "admin"
44418
- }), method(object({
44419
- namespace: string().optional(),
44420
- guard: TransactGuardSchema.optional(),
44421
- ops: array(TransactOpSchema).readonly()
44422
- }), object({
44423
- applied: boolean(),
44424
- current: unknown().optional()
44425
- }), {
44426
- kind: "mutation",
44427
- auth: "admin"
44428
- }), method(object({
44429
- namespace: string().optional(),
44430
- collection: string(),
44431
- id: string(),
44432
- data: record(string(), unknown())
44433
- }), _void(), {
44434
- kind: "mutation",
44435
- auth: "admin"
44436
- }), method(object({
44437
- namespace: string().optional(),
44438
- collection: string(),
44439
- key: string()
44440
- }), _void(), {
44441
- kind: "mutation",
44442
- auth: "admin"
44443
- }), method(object({
44444
- namespace: string().optional(),
44445
- collection: string(),
44446
- filter: MutationFilterSchema
44447
- }), object({ deleted: number().int() }), {
44448
- kind: "mutation",
44449
- auth: "admin"
44450
- }), method(object({
44451
- namespace: string().optional(),
44452
- collection: string(),
44453
- filter: MutationFilterSchema,
44454
- data: record(string(), unknown())
44455
- }), object({ updated: number().int() }), {
44456
- kind: "mutation",
44457
- auth: "admin"
44458
- }), method(object({
44459
- namespace: string().optional(),
44460
- collection: string(),
44461
- filter: QueryFilterSchema.optional()
44462
- }), number(), { auth: "admin" }), method(object({
44463
- namespace: string().optional(),
44464
- collection: string(),
44465
- fields: array(AggregateFieldSchema).readonly(),
44466
- filter: QueryFilterSchema.optional()
44467
- }), AggregateResultSchema, { auth: "admin" }), method(object({
44468
- namespace: string().optional(),
44469
- collection: string(),
44470
- field: string(),
44471
- bucketSize: number().int().positive(),
44472
- origin: number().int(),
44473
- filter: QueryFilterSchema.optional()
44474
- }), array(object({
44475
- bucket: number().int(),
44476
- count: number().int()
44477
- })).readonly(), { auth: "admin" }), method(object({
44478
- namespace: string().optional(),
44479
- collection: string()
44480
- }), boolean(), { auth: "admin" }), method(DeclareCollectionInputSchema, _void(), {
44481
- kind: "mutation",
44482
- auth: "admin"
44483
- });
44484
- /**
44485
45356
  * How long one snapshot may take end to end: the pinned copy of the whole
44486
45357
  * database plus `PRAGMA integrity_check` over the copy, on the hub's slowest
44487
45358
  * disk. ~800 MB at a pessimistic 10 MB/s is under 3 minutes; the bound is
@@ -46399,6 +47270,7 @@ method(object({
46399
47270
  kind: "mutation",
46400
47271
  auth: "admin"
46401
47272
  }), method(object({ blockIds: array(string()) }), object({ states: array(CompositionBlockStateSchema) }), { auth: "admin" });
47273
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
46402
47274
  var NUMBER = {
46403
47275
  kind: "fixed",
46404
47276
  result: ["number"]
@@ -47416,6 +48288,57 @@ id: union([string(), number()]) }), {
47416
48288
  kind: "mutation",
47417
48289
  auth: "admin"
47418
48290
  });
48291
+ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { auth: "admin" }), method(object({ owner: string().optional() }), object({ pending: array(MigrationPendingEntrySchema) }), { auth: "admin" }), method(object({ id: string().min(1) }), MigrationDryRunResultSchema, { auth: "admin" }), method(object({
48292
+ id: string().min(1),
48293
+ error: string().min(1)
48294
+ }), MigrationLedgerEntrySchema, {
48295
+ kind: "mutation",
48296
+ auth: "admin"
48297
+ }), method(object({ id: string().min(1) }), object({ entries: array(MigrationLedgerEntrySchema) }), {
48298
+ kind: "mutation",
48299
+ auth: "admin"
48300
+ }), method(object({
48301
+ database: DataStoreDatabaseSchema,
48302
+ snapshotId: string().min(1)
48303
+ }), object({
48304
+ scheduled: boolean(),
48305
+ held: array(string()),
48306
+ restartRequired: boolean()
48307
+ }), {
48308
+ kind: "mutation",
48309
+ auth: "admin"
48310
+ }), method(object({
48311
+ owner: string().min(1),
48312
+ metas: array(MigrationMetaSchema)
48313
+ }), object({
48314
+ bootId: string(),
48315
+ snapshot: _enum([
48316
+ "none",
48317
+ "written",
48318
+ "refused"
48319
+ ]),
48320
+ steps: array(MigrationOwnerStepSchema)
48321
+ }), {
48322
+ kind: "mutation",
48323
+ auth: "admin"
48324
+ }), method(object({ id: string().min(1) }), object({
48325
+ proceed: boolean(),
48326
+ state: union([literal("pending"), MigrationStatusSchema]),
48327
+ reason: string().nullable()
48328
+ }), {
48329
+ kind: "mutation",
48330
+ auth: "admin"
48331
+ }), method(object({
48332
+ id: string().min(1),
48333
+ result: MigrationOwnerResultSchema
48334
+ }), object({
48335
+ status: MigrationStatusSchema,
48336
+ error: string().nullable(),
48337
+ rowsChanged: number().int().nonnegative()
48338
+ }), {
48339
+ kind: "mutation",
48340
+ auth: "admin"
48341
+ });
47419
48342
  /**
47420
48343
  * records - an addon's own facts and documents (D881): ledgers, audit rows,
47421
48344
  * config documents, mirrors. The third storage API next to `ctx.settings`
@@ -64082,7 +65005,18 @@ var ServerUpdateActionResultSchema = object({
64082
65005
  * store (D877), present when the pre-flight refused the update
64083
65006
  * (`reason: 'addon-would-refuse'`). Optional for version skew.
64084
65007
  */
64085
- blockers: array(UpdateBlockerSchema).optional()
65008
+ blockers: array(UpdateBlockerSchema).optional(),
65009
+ /**
65010
+ * The minimums the target server declares and this node does not meet
65011
+ * (D989), present when the update was refused with
65012
+ * `reason: 'requirements-unmet'`. Each names the versions on both sides.
65013
+ */
65014
+ unmet: array(UnmetRequirementSchema).optional(),
65015
+ /**
65016
+ * The addons this node has below the minimum the staged server declares —
65017
+ * the ones to update WITH the server. Present on an accepted update.
65018
+ */
65019
+ requiredAddonUpdates: array(RequiredAddonUpdateSchema).optional()
64086
65020
  });
64087
65021
  var LaunchOverridesSchema = object({
64088
65022
  /** The persisted heap ceiling in MB, or `null` when none is set. */
@@ -66660,7 +67594,9 @@ var PackageUpdateSchema = object({
66660
67594
  latestVersion: string(),
66661
67595
  category: _enum(["addon", "core"]),
66662
67596
  requiresRestart: boolean(),
66663
- isSystem: boolean()
67597
+ isSystem: boolean(),
67598
+ /** Held: what the newer version needs and this node does not meet (D989). */
67599
+ blockedBy: array(UnmetRequirementSchema).readonly().optional()
66664
67600
  });
66665
67601
  var PackageVersionInfoSchema = object({
66666
67602
  version: string(),
@@ -67220,6 +68156,43 @@ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
67220
68156
  auth: "admin"
67221
68157
  });
67222
68158
  /**
68159
+ * schedulers — timelines of edge and hold points that perform actions over the
68160
+ * day (D983). Provided by the core-blocks builtin on the hub, beside `actions`:
68161
+ * the same runs ledger (owner `scheduler:<id>`), the same target resolver, the
68162
+ * same step runner.
68163
+ *
68164
+ * The hub runs ONE timer for the next point across every scheduler. A save is
68165
+ * refused with its reason (`[invalid-scheduler] …`) when a hold has a step that
68166
+ * leads to no state, or a sun point has no site location to read.
68167
+ */
68168
+ var SchedulerIdInput = object({ schedulerId: string().min(1) });
68169
+ method(object({}), array(SchedulerSchema), { auth: "admin" }), method(SchedulerIdInput, object({ scheduler: SchedulerSchema.nullable() }), { auth: "admin" }), method(object({ scheduler: SchedulerInputSchema }), object({ scheduler: SchedulerSchema }), {
68170
+ kind: "mutation",
68171
+ auth: "admin"
68172
+ }), method(object({
68173
+ schedulerId: string().min(1),
68174
+ scheduler: SchedulerInputSchema
68175
+ }), object({ scheduler: SchedulerSchema }), {
68176
+ kind: "mutation",
68177
+ auth: "admin"
68178
+ }), method(SchedulerIdInput, _void(), {
68179
+ kind: "mutation",
68180
+ auth: "admin"
68181
+ }), method(object({
68182
+ schedulerId: string().min(1),
68183
+ profileId: string().min(1).optional(),
68184
+ enabled: boolean()
68185
+ }), object({ scheduler: SchedulerSchema }), {
68186
+ kind: "mutation",
68187
+ auth: "admin"
68188
+ }), method(object({
68189
+ schedulerId: string().min(1),
68190
+ date: string().regex(LOCAL_DATE_PATTERN).optional()
68191
+ }), SchedulerPreviewSchema, { auth: "admin" }), method(object({
68192
+ scheduler: SchedulerInputSchema,
68193
+ schedulerId: string().min(1).optional()
68194
+ }), object({ conflicts: array(SchedulerConflictSchema) }), { auth: "admin" }), method(object({}), array(SchedulerStatusSchema), { auth: "admin" }), method(SchedulerIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" });
68195
+ /**
67223
68196
  * actions — the ONE surface for runnable actions, for the UI and for
67224
68197
  * automations. Provided by the core-blocks builtin on the hub.
67225
68198
  *
@@ -68079,19 +69052,19 @@ var RemoteSetConfigResultSchema = discriminatedUnion("status", [object({ status:
68079
69052
  reason: _enum(["unknown-template", "not-a-media-player"]),
68080
69053
  detail: string()
68081
69054
  })]);
68082
- var deviceInput = object({ deviceId: number().int().nonnegative() });
69055
+ var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
68083
69056
  DeviceType.MediaPlayer;
68084
- method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
69057
+ method(deviceInput$1, RemoteConfigSchema, { auth: "admin" }), method(deviceInput$1.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
68085
69058
  kind: "mutation",
68086
69059
  auth: "admin"
68087
- }), method(deviceInput, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput.extend({
69060
+ }), method(deviceInput$1, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput$1.extend({
68088
69061
  role: RemoteRoleSchema,
68089
69062
  /** Sends in a row (volume steps). */
68090
69063
  repeat: number().int().min(1).max(50).default(1)
68091
69064
  }), RemoteSendResultSchema, {
68092
69065
  kind: "mutation",
68093
69066
  auth: "protected"
68094
- }), method(deviceInput.extend({
69067
+ }), method(deviceInput$1.extend({
68095
69068
  command: RemoteMediaCommandSchema,
68096
69069
  muted: boolean().optional(),
68097
69070
  source: string().optional(),
@@ -71525,6 +72498,8 @@ var IntegrationWithStateSchema = object({
71525
72498
  name: string(),
71526
72499
  enabled: boolean(),
71527
72500
  info: record(string(), unknown()).readonly(),
72501
+ /** The discovery candidate this integration was set up from (D984). */
72502
+ originStableId: string().optional(),
71528
72503
  createdAt: number(),
71529
72504
  updatedAt: number(),
71530
72505
  processState: string()
@@ -71535,6 +72510,8 @@ var IntegrationLiteSchema = object({
71535
72510
  name: string(),
71536
72511
  enabled: boolean(),
71537
72512
  info: record(string(), unknown()).readonly(),
72513
+ /** The discovery candidate this integration was set up from (D984). */
72514
+ originStableId: string().optional(),
71538
72515
  createdAt: number(),
71539
72516
  updatedAt: number()
71540
72517
  });
@@ -71632,6 +72609,8 @@ var CreateIntegrationInputSchema = object({
71632
72609
  name: string(),
71633
72610
  enabled: boolean().optional(),
71634
72611
  info: record(string(), unknown()).optional(),
72612
+ /** `stableId` of the discovery candidate the form was seeded from (D984). */
72613
+ originStableId: string().optional(),
71635
72614
  settings: record(string(), unknown()).optional(),
71636
72615
  /** Keys of `settings` the form's schema declares secret (by type) — kept in
71637
72616
  * the keychain, not the table (D787). */
@@ -72003,7 +72982,7 @@ var lawnMowerControlCapability = {
72003
72982
  * clients — they reverse-connect to the hub. Exposing their interfaces
72004
72983
  * via the same surface would leak internal topology with no upside.
72005
72984
  *
72006
- * Implementation in `@camstack/system/builtins/local-network/`.
72985
+ * Implementation in `@camstack/server/system/builtins/local-network/`.
72007
72986
  */
72008
72987
  /** Coarse classification derived from the interface name + IP range. */
72009
72988
  var InterfaceKindEnum = _enum([
@@ -72412,12 +73391,22 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
72412
73391
  * reports `LockEntityFeature.OPEN` (bit value 1) in HA
72413
73392
  * `supported_features`.
72414
73393
  */
73394
+ /**
73395
+ * `door-open` (D984): the linked door contact reads open, so the bolt cannot be
73396
+ * thrown. It is the lock's REAL reported state, written into the mirrored slice
73397
+ * by the door-contact Customize extension — never a provider's own value, and
73398
+ * never a projection a reader has to compute. Precedence: a native `jammed`
73399
+ * wins; otherwise an open contact reports `door-open` whatever the native state
73400
+ * says (`locked` included); a closed or unknown contact leaves the native state.
73401
+ * A `lock()` is refused while the state is `door-open`; `unlock()` never is.
73402
+ */
72415
73403
  var LockStateSchema = _enum([
72416
73404
  "locked",
72417
73405
  "unlocked",
72418
73406
  "locking",
72419
73407
  "unlocking",
72420
- "jammed"
73408
+ "jammed",
73409
+ "door-open"
72421
73410
  ]);
72422
73411
  var LockControlStatusSchema = object({
72423
73412
  /** Lifecycle state of the lock. `jammed` means the motor reported
@@ -72426,6 +73415,38 @@ var LockControlStatusSchema = object({
72426
73415
  /** Ms epoch when the slice was last updated. */
72427
73416
  lastChangedAt: number()
72428
73417
  });
73418
+ /**
73419
+ * "Door contact" Customize extension (D984): the ONE extension that replaces a
73420
+ * native field. `state` is listed in `overridable` for this target, so the
73421
+ * validator and the guard accept it and nothing else.
73422
+ */
73423
+ var lockControlCustomize = {
73424
+ version: 1,
73425
+ overridable: [{
73426
+ path: "state",
73427
+ by: "door-contact"
73428
+ }],
73429
+ targets: [{
73430
+ id: "door-contact",
73431
+ cardinality: "scalar",
73432
+ vocabulary: {
73433
+ cap: "lock-control",
73434
+ stateField: "state"
73435
+ },
73436
+ deviceTypes: [DeviceType.Lock],
73437
+ apply: "replace",
73438
+ path: "state",
73439
+ override: {
73440
+ whenSourceTrue: "door-open",
73441
+ unlessNative: ["jammed"]
73442
+ },
73443
+ sources: [{
73444
+ kind: "binary",
73445
+ transforms: [{ kind: "identity" }]
73446
+ }],
73447
+ sourceReport: { status: "assignment-status" }
73448
+ }]
73449
+ };
72429
73450
  var lockControlCapability = {
72430
73451
  name: "lock-control",
72431
73452
  scope: "device",
@@ -72471,6 +73492,7 @@ var lockControlCapability = {
72471
73492
  * transitions.
72472
73493
  */
72473
73494
  runtimeState: LockControlStatusSchema,
73495
+ customize: lockControlCustomize,
72474
73496
  /**
72475
73497
  * Runtime-state durability: **restored** — a lock left locked must still read locked.
72476
73498
  *
@@ -72487,6 +73509,117 @@ var lockControlCapability = {
72487
73509
  * whether persisting is worth a SQLite commit. */
72488
73510
  volatileStateFields: ["lastChangedAt"]
72489
73511
  };
73512
+ var PowerSourceRefusalReasonSchema = _enum([
73513
+ "no-power-source",
73514
+ "switch-missing",
73515
+ "switch-offline",
73516
+ "switch-unknown",
73517
+ "restart-in-progress",
73518
+ "switch-call-failed"
73519
+ ]);
73520
+ var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
73521
+ accepted: literal(false),
73522
+ reason: PowerSourceRefusalReasonSchema,
73523
+ /** One line for the operator, naming the switch. */
73524
+ message: string()
73525
+ })]);
73526
+ var PowerSourceInfoSchema = object({
73527
+ /** False when the device has no power source linked; the rest is then empty. */
73528
+ linked: boolean(),
73529
+ switchDeviceId: number().nullable(),
73530
+ switchName: string().nullable(),
73531
+ /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
73532
+ switchOn: boolean().nullable(),
73533
+ switchOnline: boolean().nullable(),
73534
+ restartInProgress: boolean(),
73535
+ offSeconds: number().int()
73536
+ });
73537
+ /** The composed slice: the switch state the link mirrors, plus the stored off time. */
73538
+ var PowerSourceStateSchema = object({
73539
+ /** Null while the switch has not reported: unknown is never `false` (D393). */
73540
+ switchOn: boolean().nullable(),
73541
+ offSeconds: number().int(),
73542
+ lastFetchedAt: number()
73543
+ });
73544
+ var POWER_SOURCE_DEVICE_TYPES = [
73545
+ DeviceType.Gateway,
73546
+ DeviceType.Hub,
73547
+ DeviceType.Camera,
73548
+ DeviceType.Sensor,
73549
+ DeviceType.MediaPlayer,
73550
+ DeviceType.Vacuum,
73551
+ DeviceType.Generic,
73552
+ DeviceType.Washer,
73553
+ DeviceType.Dryer,
73554
+ DeviceType.Dishwasher,
73555
+ DeviceType.PetFountain,
73556
+ DeviceType.Fan,
73557
+ DeviceType.Humidifier,
73558
+ DeviceType.Climate,
73559
+ DeviceType.WaterHeater
73560
+ ];
73561
+ var powerSourceCustomize = {
73562
+ version: 1,
73563
+ targets: [{
73564
+ id: "switch",
73565
+ cardinality: "scalar",
73566
+ vocabulary: {
73567
+ cap: "power-source",
73568
+ stateField: "switchOn"
73569
+ },
73570
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
73571
+ apply: "add",
73572
+ path: "switchOn",
73573
+ sources: [{
73574
+ kind: "switch",
73575
+ transforms: [{ kind: "identity" }]
73576
+ }],
73577
+ companions: [{
73578
+ kind: "constant",
73579
+ path: "offSeconds",
73580
+ value: 10,
73581
+ when: "add"
73582
+ }, {
73583
+ kind: "clock",
73584
+ path: "lastFetchedAt",
73585
+ when: "always"
73586
+ }],
73587
+ sourceReport: { status: "assignment-status" }
73588
+ }]
73589
+ };
73590
+ var deviceInput = object({ deviceId: number().int().nonnegative() });
73591
+ var powerSourceCapability = {
73592
+ name: "power-source",
73593
+ scope: "device",
73594
+ mode: "singleton",
73595
+ deviceTypes: POWER_SOURCE_DEVICE_TYPES,
73596
+ methods: {
73597
+ getStatus: systemMethod(deviceInput, PowerSourceInfoSchema, { auth: "admin" }),
73598
+ powerOn: systemMethod(deviceInput, PowerSourceActionResultSchema, {
73599
+ kind: "mutation",
73600
+ auth: "admin"
73601
+ }),
73602
+ powerOff: systemMethod(deviceInput, PowerSourceActionResultSchema, {
73603
+ kind: "mutation",
73604
+ auth: "admin"
73605
+ }),
73606
+ forceRestart: systemMethod(deviceInput, PowerSourceActionResultSchema, {
73607
+ kind: "mutation",
73608
+ auth: "admin"
73609
+ })
73610
+ },
73611
+ runtimeState: PowerSourceStateSchema,
73612
+ customize: powerSourceCustomize,
73613
+ /**
73614
+ * Runtime-state durability: **session** — mirrored from the linked switch on
73615
+ * its next report; a restored value would outlive the reading it names.
73616
+ *
73617
+ * See `RuntimeStateDurability`. Enforced by
73618
+ * `scripts/check-runtime-state-durability.ts`.
73619
+ */
73620
+ durability: "session",
73621
+ history: "none"
73622
+ };
72490
73623
  /**
72491
73624
  * A monotonic total between resets (D944). Energy uses `power-meter.energy`
72492
73625
  * so dashboards keep one series; every other instantaneous reading lands here.
@@ -73792,7 +74925,7 @@ onStatusChanged: { data: object({
73792
74925
  * jitter, and observed/peak bandwidth per device + per client.
73793
74926
  *
73794
74927
  * Implementation lives in the server's `NetworkQualityService` (thin
73795
- * wrapper over the shared `NetworkQualityTracker` from `@camstack/system`).
74928
+ * wrapper over the shared `NetworkQualityTracker` from `@camstack/server/system`).
73796
74929
  * The provider is registered from `trpc.router.ts` against the existing
73797
74930
  * service instance — no addon owns this state.
73798
74931
  *
@@ -75978,7 +77111,7 @@ var ptzAutotrackCapability = {
75978
77111
  * - **Native**: the camera's own firmware autotrack, registered per device by
75979
77112
  * the driver when the probe says it exists (Reolink: yes; Hikvision:
75980
77113
  * model-dependent).
75981
- * - **Wrapper**: the emulated implementation (`@camstack/system`'s `autotrack` builtin),
77114
+ * - **Wrapper**: the emulated implementation (`@camstack/server/system`'s `autotrack` builtin),
75982
77115
  * which follows a subject with `ptz.move` pulses driven by the detection
75983
77116
  * pipeline. `defaultActive: false` — the operator binds it per camera in
75984
77117
  * the bindings UI.
@@ -80985,7 +82118,6 @@ discriminatedUnion("kind", [object({
80985
82118
  })]);
80986
82119
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
80987
82120
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
80988
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
80989
82121
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
80990
82122
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
80991
82123
  new Set(CAP_NAMES);
@@ -81391,6 +82523,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
81391
82523
  petFeeder: petFeederCapability,
81392
82524
  petFountain: petFountainCapability,
81393
82525
  powerMeter: powerMeterCapability,
82526
+ powerSource: powerSourceCapability,
81394
82527
  presence: presenceCapability,
81395
82528
  pressureSensor: pressureSensorCapability,
81396
82529
  privacyMask: privacyMaskCapability,
@@ -82566,6 +83699,13 @@ Object.freeze({
82566
83699
  access: "view",
82567
83700
  role: "admin"
82568
83701
  },
83702
+ "addonSettings.getIntegrationFormSchema": {
83703
+ capName: "addon-settings",
83704
+ capScope: "system",
83705
+ addonId: null,
83706
+ access: "view",
83707
+ role: "admin"
83708
+ },
82569
83709
  "addonSettings.getIntegrationSettings": {
82570
83710
  capName: "addon-settings",
82571
83711
  capScope: "system",
@@ -86654,6 +87794,69 @@ Object.freeze({
86654
87794
  access: "view",
86655
87795
  role: "admin"
86656
87796
  },
87797
+ "migrations.declareOwner": {
87798
+ capName: "migrations",
87799
+ capScope: "system",
87800
+ addonId: null,
87801
+ access: "create",
87802
+ role: "admin"
87803
+ },
87804
+ "migrations.dryRun": {
87805
+ capName: "migrations",
87806
+ capScope: "system",
87807
+ addonId: null,
87808
+ access: "view",
87809
+ role: "admin"
87810
+ },
87811
+ "migrations.finishMigration": {
87812
+ capName: "migrations",
87813
+ capScope: "system",
87814
+ addonId: null,
87815
+ access: "create",
87816
+ role: "admin"
87817
+ },
87818
+ "migrations.listLedger": {
87819
+ capName: "migrations",
87820
+ capScope: "system",
87821
+ addonId: null,
87822
+ access: "view",
87823
+ role: "admin"
87824
+ },
87825
+ "migrations.listPending": {
87826
+ capName: "migrations",
87827
+ capScope: "system",
87828
+ addonId: null,
87829
+ access: "view",
87830
+ role: "admin"
87831
+ },
87832
+ "migrations.markFailed": {
87833
+ capName: "migrations",
87834
+ capScope: "system",
87835
+ addonId: null,
87836
+ access: "create",
87837
+ role: "admin"
87838
+ },
87839
+ "migrations.restorePreMigration": {
87840
+ capName: "migrations",
87841
+ capScope: "system",
87842
+ addonId: null,
87843
+ access: "create",
87844
+ role: "admin"
87845
+ },
87846
+ "migrations.retry": {
87847
+ capName: "migrations",
87848
+ capScope: "system",
87849
+ addonId: null,
87850
+ access: "create",
87851
+ role: "admin"
87852
+ },
87853
+ "migrations.startMigration": {
87854
+ capName: "migrations",
87855
+ capScope: "system",
87856
+ addonId: null,
87857
+ access: "create",
87858
+ role: "admin"
87859
+ },
86657
87860
  "modelConvert.convert": {
86658
87861
  capName: "model-convert",
86659
87862
  capScope: "system",
@@ -89286,6 +90489,34 @@ Object.freeze({
89286
90489
  access: "view",
89287
90490
  role: "viewer"
89288
90491
  },
90492
+ "powerSource.forceRestart": {
90493
+ capName: "power-source",
90494
+ capScope: "device",
90495
+ addonId: null,
90496
+ access: "create",
90497
+ role: "admin"
90498
+ },
90499
+ "powerSource.getStatus": {
90500
+ capName: "power-source",
90501
+ capScope: "device",
90502
+ addonId: null,
90503
+ access: "view",
90504
+ role: "admin"
90505
+ },
90506
+ "powerSource.powerOff": {
90507
+ capName: "power-source",
90508
+ capScope: "device",
90509
+ addonId: null,
90510
+ access: "create",
90511
+ role: "admin"
90512
+ },
90513
+ "powerSource.powerOn": {
90514
+ capName: "power-source",
90515
+ capScope: "device",
90516
+ addonId: null,
90517
+ access: "create",
90518
+ role: "admin"
90519
+ },
89289
90520
  "presence.getStatus": {
89290
90521
  capName: "presence",
89291
90522
  capScope: "device",
@@ -90098,6 +91329,76 @@ Object.freeze({
90098
91329
  access: "create",
90099
91330
  role: "feature:scenes"
90100
91331
  },
91332
+ "schedulers.conflicts": {
91333
+ capName: "schedulers",
91334
+ capScope: "system",
91335
+ addonId: null,
91336
+ access: "view",
91337
+ role: "admin"
91338
+ },
91339
+ "schedulers.create": {
91340
+ capName: "schedulers",
91341
+ capScope: "system",
91342
+ addonId: null,
91343
+ access: "create",
91344
+ role: "admin"
91345
+ },
91346
+ "schedulers.delete": {
91347
+ capName: "schedulers",
91348
+ capScope: "system",
91349
+ addonId: null,
91350
+ access: "delete",
91351
+ role: "admin"
91352
+ },
91353
+ "schedulers.get": {
91354
+ capName: "schedulers",
91355
+ capScope: "system",
91356
+ addonId: null,
91357
+ access: "view",
91358
+ role: "admin"
91359
+ },
91360
+ "schedulers.getStatus": {
91361
+ capName: "schedulers",
91362
+ capScope: "system",
91363
+ addonId: null,
91364
+ access: "view",
91365
+ role: "admin"
91366
+ },
91367
+ "schedulers.getTraces": {
91368
+ capName: "schedulers",
91369
+ capScope: "system",
91370
+ addonId: null,
91371
+ access: "view",
91372
+ role: "admin"
91373
+ },
91374
+ "schedulers.list": {
91375
+ capName: "schedulers",
91376
+ capScope: "system",
91377
+ addonId: null,
91378
+ access: "view",
91379
+ role: "admin"
91380
+ },
91381
+ "schedulers.preview": {
91382
+ capName: "schedulers",
91383
+ capScope: "system",
91384
+ addonId: null,
91385
+ access: "view",
91386
+ role: "admin"
91387
+ },
91388
+ "schedulers.setEnabled": {
91389
+ capName: "schedulers",
91390
+ capScope: "system",
91391
+ addonId: null,
91392
+ access: "create",
91393
+ role: "admin"
91394
+ },
91395
+ "schedulers.update": {
91396
+ capName: "schedulers",
91397
+ capScope: "system",
91398
+ addonId: null,
91399
+ access: "create",
91400
+ role: "admin"
91401
+ },
90101
91402
  "scriptRunner.getStatus": {
90102
91403
  capName: "script-runner",
90103
91404
  capScope: "device",
@@ -94364,6 +95665,26 @@ Object.freeze({
94364
95665
  form: "single",
94365
95666
  optional: false
94366
95667
  }],
95668
+ "powerSource.forceRestart": [{
95669
+ name: "deviceId",
95670
+ form: "single",
95671
+ optional: false
95672
+ }],
95673
+ "powerSource.getStatus": [{
95674
+ name: "deviceId",
95675
+ form: "single",
95676
+ optional: false
95677
+ }],
95678
+ "powerSource.powerOff": [{
95679
+ name: "deviceId",
95680
+ form: "single",
95681
+ optional: false
95682
+ }],
95683
+ "powerSource.powerOn": [{
95684
+ name: "deviceId",
95685
+ form: "single",
95686
+ optional: false
95687
+ }],
94367
95688
  "presence.getStatus": [{
94368
95689
  name: "deviceId",
94369
95690
  form: "single",
@@ -98683,6 +100004,7 @@ new Set([
98683
100004
  "locking",
98684
100005
  "unlocking",
98685
100006
  "jammed",
100007
+ "door-open",
98686
100008
  "on",
98687
100009
  "off",
98688
100010
  "home",
@@ -100396,4 +101718,4 @@ function buildClipModelRegistry(entries) {
100396
101718
  /** The built-in catalog's registry. */
100397
101719
  var BUILTIN_CLIP_MODELS = buildClipModelRegistry(CLIP_IMAGE_MODELS);
100398
101720
  //#endregion
100399
- export { MAX_TRACK_RELATIONS as $, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as $i, bundledCamstack as $n, normalizeNcAction as $r, NcTemplateVarDescriptorSchema as $t, DETECTION_MACRO_CLASSES as A, record as Aa, sceneMonitorCapability as Ai, TrackPuritySchema as An, isAlertEpisodeOpen as Ar, NcAlarmConfigSchema as At, EXPORT_DENSE_MAX_RANGES as B, sunTimes as Bi, alertEpisodeKind as Bn, isLabelSupplyStepId as Br, NcRuleSchema as Bt, COCO_TO_MACRO as C, boolean as Ca, retrainRevisionConflictMessage as Ci, SetTrackRelationInputSchema as Cn, eventKindLabel as Cr, NC_GROUPING_MIN_SEC as Ct, DEFAULT_DETAIL_CROP_CONVENTION as D, number as Da, ruleDeviceCompatCapQueries as Di, TimelapseRulePatchSchema as Dn, gifDeriveSpeed as Dr, NC_SYSTEM_EVENT_KIND_GROUP_SPECS as Dt, CoverStateSchema as E, looseObject as Ea, ruleBelongsToAlarm as Ei, TimelapseRuleInputSchema as En, failureContributionCapability as Er, NC_STATE_SUMMARY_SECTION_IDS as Et, EMBEDDING_REQUIREMENTS_FAILED as F, union as Fa, storageOccupancyCapability as Fi, activateAlarmProfile as Fn, isDetectionMacroClass as Fr, NcConditionDescriptorSchema as Ft, LabelAttributionSchema as G, systemEventSummaryScheduleOf as Gi, applySecureOnArmEdit as Gn, isSystemDelivery as Gr, NcSnoozeInputSchema as Gt, FULL_IMAGE_BBOX as H, systemEventDeliveryProblems as Hi, applyBlockingEdit as Hn, isRetiredClearKey as Hr, NcRuleUpdateInputSchema as Ht, EMBEDDING_REQUIREMENTS_UNAVAILABLE as I, unknown as Ia, subKindsOf as Ii, addonWidgetsSourceCapability as In, isDrawableSubjectPath as Ir, NcConditionsSchema as It, LockStateSchema as J, templateVarsFor as Ji, audioMetricsCapability as Jn, kebabToCamel as Jr, NcStateSummarySectionIdSchema as Jt, ListTrackRelationsInputSchema as K, systemEventSummarySplitOf as Ki, armRemovalReconcile as Kn, isSystemEventSummaryRule as Kr, NcSnoozeSchema as Kt, EVENT_DENSITY_BATCH_MAX as L, EventCategory as La, subjectPathSampleFromBox as Li, alarmDeviceSecureOnArm as Ln, isEventKindLabelKey as Lr, NcRuleActionSchema as Lt, DeclaredDevices as M, string as Ma, sensorRaisedOf as Mi, TrackRelationSchema as Mn, isAppLinkBase as Mr, NcAlarmSensorsInputSchema as Mt, DetectionSourceSchema as N, templateLiteral as Na, servedTemplateVars as Ni, TrackSideSchema as Nn, isAudioClassName as Nr, NcAlarmSensorsSchema as Nt, DEFAULT_EVENT_COLOR as O, object as Oa, ruleGroupingApplies as Oi, TimelapseRuleSchema as On, gifFromMp4Args as Or, NC_TAXONOMY as Ot, EMBEDDING_MODEL_FAILED as P, tuple as Pa, startMoverUnderClearance as Pi, TrackSourceSchema as Pn, isDefaultAlarmProfileId as Pr, NcAlarmSettingsPatchSchema as Pt, MAX_TRACK_PURITIES as Q, zoneAnalyticsCapability as Qi, buildViewerLink as Qn, ncMediaPolicyError as Qr, NcTemplatePreviewSchema as Qt, EVENT_KIND_BY_CAP as R, subjectPathSvgPoints as Ri, alarmPanelCapability as Rn, isEventOwnerType as Rr, NcRuleInputSchema as Rt, BucketAccumulator as S, array as Sa, resolvePoolMemoryPolicy as Si, SetTrackPurityResultSchema as Sn, ensureAlarmProfiles as Sr, NC_GROUPING_MAX_SEC as St, ClearTrackRelationResultSchema as T, literal as Ta, ruleAutoTriggersAlarm as Ti, TRACK_RELATIONS_DEFAULT_LIMIT as Tn, faceGalleryCapability as Tr, NC_SNOOZE_MAX_MINUTES as Tt, FailureCounters as U, systemEventFamilyProblems as Ui, applyModeDelaysEdits as Un, isRootSuppliableStepId as Ur, NcScheduleSchema as Ut, FIRST_LEVEL_MACRO_CLASSES as V, syncActiveAlarmProfileSettings as Vi, alertEpisodePayload as Vn, isMediaOwnerType as Vr, NcRuleTargetSchema as Vt, LABEL_SUPPLY_MACRO as W, systemEventFilterApplies as Wi, applyNonBlockingEdit as Wn, isScheduleActive as Wr, NcSharedSequenceSchema as Wt, MAX_BIRTH_DECISION_RECORDS as X, viewerLinkBaseOf as Xi, buildEventKindDescriptor as Xn, mayWriteToLocation as Xr, NcTaxonomySchema as Xt, MAX_ARCHIVED_DEBUG_NOTES as Y, vectorDimFromBase64 as Yi, audioModeOf as Yn, linkedStepOf as Yr, NcSystemEventKindSchema as Yt, MAX_BIRTH_LATENCY_PROXY_MS as Z, withSystemEventClasses as Zi, buildSubjectPath as Zn, memoizePinnedFfmpeg as Zr, NcTemplatePreviewInputSchema as Zt, AlarmArmModeSchema as _, nodePin as _a, resolveDeliveryParams as _i, SCENE_DEFAULT_UNCOVERED_POLICY as _n, embeddingModelFailedError as _r, NC_CONFIRM_DEFAULT_TIMEOUT_MS as _t, CLIP_EMBEDDING_STEP_ID as a, BaseAddon as aa, paletteResultSchema as ai, RECORDING_SOURCE_CAMSTACK_ADDON as an, coreBlockIdFromAddonId as ar, NC_ALARM_DELAY_FIELDS as at, BaseDevice as b, ZodObject as ba, resolveModeNotifyPolicy as bi, SceneMonitorSchema as bn, embeddingRequirementsUnavailableError as br, NC_DEFAULT_SNOOZE_MINUTES as bt, readClusterModelPinStrict as c, InvalidRecordError as ca, pickFaceRescoreIntervalMinutes as ci, RetrainOriginSchema as cn, deleteAlarmProfile as cr, NC_ALARM_SYSTEM_EVENT_KINDS as ct, APPEARANCE_BIN_COUNT as d, createEvent as da, projectSubjectPath as di, SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX as dn, deriveRecordingMode as dr, NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT as dt, canonicalHash as ea, normalizeNcCapTarget as ei, OPS_LOG_DEFAULT_LIMIT as en, calendarEventLocalSpan as er, MOTION_CLOSE_AFTER_MS as et, APPEARANCE_BLOCKS as f, customAction as fa, readDeviceStateFrom as fi, SCENE_CONFIRM_DEFAULT_TIMEOUT_MS as fn, describeSystemEventDeliveryProblem as fr, NC_CLIP_POST_ROLL_DEFAULT_SEC as ft, ActionTargetResolver as g, isDeviceScopedCap as ga, resolveClipSearchMinScore as gi, SCENE_DEFAULT_QUIET_SECONDS as gn, embeddingEncoderCapability as gr, NC_CONFIRM_DEFAULT_MAX_IMAGE_PX as gt, AUDIO_MACRO_LABELS as h, hydrateSchema as ha, renameAlarmProfile as hi, SCENE_DEFAULT_OBSERVATION_SPACING_SEC as hn, deviceRemovalHookCapability as hr, NC_CONDITION_CATALOG as ht, CLIP_TEXT_MODELS as i, errMsg as ia, paintHex as ii, RECORDING_EXPORT_MAX_READ_BYTES as in, consumableLowPercentOf as ir, NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC as it, DETECTION_PIPELINE_CAP_NAME as j, strictObject as ja, secureActuationFor as ji, TrackRelationKindSchema as jn, isAlertEpisodeSeverity as jr, NcAlarmPostTriggeredActionSchema as jt, DEFAULT_TIMELAPSE_PREVIEW_TEXT as k, partialRecord as ka, ruleKindOf as ki, TrackPlacementSchema as kn, isAlarmControllableCap as kr, NC_TEMPLATE_VARS as kt, resolveClusterModelPin as l, RECORD_MAX_DATA_BYTES as la, pipelineAnalyticsCapability as li, RetrainStatusSchema as ln, deriveDetailCropRect as lr, NC_ALARM_TRIGGERED_MAX_SEC as lt, ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT as m, defineRecordKinds as ma, readTimelapseGeneratedAt as mi, SCENE_DEFAULT_CHECK_INTERVAL_SEC as mn, deviceAttentionKind as mr, NC_CLIP_RETENTION_MAX_SEC as mt, DEFAULT_CLIP_MODEL as n, isSourceCap as na, notificationRulesCapability as ni, PackageLlmVerdictSchema as nn, cloneAlarmSettings as nr, MotionTriggeredBySchema as nt, CLUSTER_MODEL_OWNER_ADDON_ID as o, CAP_ADVANCE_FIELDS as oa, parseAlertEpisodePayload as oi, RebuildObjectEmbeddingsRefusalSchema as on, cosineSimilarity as or, NC_ALARM_DELAY_MAX_SEC as ot, APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT as p, defineCustomActions as pa, readMotionTriggeredBy as pi, SCENE_DEFAULT_ANCHOR_THRESHOLD as pn, describeSystemEventFamilyProblem as pr, NC_CLIP_PRE_ROLL_DEFAULT_SEC as pt, ListTrackRelationsResultSchema as q, templateCarriesSummary as qi, assertTimelapseCadences as qn, isVectorIndexDimConflict as qr, NcSnoozeSuppressedSchema as qt, CLIP_IMAGE_MODELS as r, DeviceType as ra, occupancyScope as ri, PoolMemoryWatchdog as rn, consumableFormOf as rr, NC_ALARM_DEFAULT_PROFILE_ID as rt, FACE_EMBEDDING_STEP_ID as s, CamProfileSchema as sa, parseProcStatus as si, RetrainDismissedSchema as sn, createAlarmProfile as sr, NC_ALARM_NOTIFY_FIELDS as st, BUILTIN_CLIP_MODELS as t, evaluateSensorEdge as ta, normalizeNcMediaPolicy as ti, OpsLogEntrySchema as tn, classifyRetrainReview as tr, MediaFileKindEnum as tt, ACTION_TARGET_INVALIDATION_EVENTS as u, RECORD_QUERY_MAX as ua, plateGalleryCapability as ui, RetrainVerdictSubjectSchema as un, derivePostTriggeredAction as ur, NC_AUDIO_CONFIRM_HITS_DEFAULT as ut, ArchivedDebugNoteSchema as v, normalizeNodeId as va, resolveLocationMode as vi, SCENE_DIVERGED as vn, embeddingRefusalOf as vr, NC_CONSUMABLE_LOW_PERCENT_DEFAULT as vt, ClearTrackRelationInputSchema as w, discriminatedUnion as wa, rootSupplyServes as wi, TIMELAPSE_DENSE_FLOOR_SEC as wn, evictionPolicyOfLocation as wr, NC_SENSOR_TRANSITION_KINDS as wt, BirthDecisionRecordSchema as x, _enum as xa, resolveNcMediaImage as xi, SetTrackPurityInputSchema as xn, encodeVectorBase64 as xr, NC_GROUPING_DEFAULT_SEC as xt, BIRTH_DECISION_DEFAULT_LIMIT as y, sleep as ya, resolveModeDelays as yi, SCENE_RESET_RECAPTURES as yn, embeddingRequirementsFailedError as yr, NC_CONSUMABLE_SYSTEM_EVENT_KINDS as yt, EVENT_OWNER_TYPES as z, summarizeReview as zi, alertEpisodeId as zn, isKnownTemplateVar as zr, NcRulePatchSchema as zt };
101721
+ export { MAX_TRACK_RELATIONS as $, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as $i, bundledCamstack as $n, normalizeNcAction as $r, NcTemplateVarDescriptorSchema as $t, DETECTION_MACRO_CLASSES as A, string as Aa, sceneMonitorCapability as Ai, TrackPuritySchema as An, isAlertEpisodeOpen as Ar, NcAlarmConfigSchema as At, EXPORT_DENSE_MAX_RANGES as B, sunTimes as Bi, alertEpisodeKind as Bn, isLabelSupplyStepId as Br, NcRuleSchema as Bt, COCO_TO_MACRO as C, literal as Ca, retrainRevisionConflictMessage as Ci, SetTrackRelationInputSchema as Cn, eventKindLabel as Cr, NC_GROUPING_MIN_SEC as Ct, DEFAULT_DETAIL_CROP_CONVENTION as D, partialRecord as Da, ruleDeviceCompatCapQueries as Di, TimelapseRulePatchSchema as Dn, gifDeriveSpeed as Dr, NC_SYSTEM_EVENT_KIND_GROUP_SPECS as Dt, CoverStateSchema as E, object as Ea, ruleBelongsToAlarm as Ei, TimelapseRuleInputSchema as En, failureContributionCapability as Er, NC_STATE_SUMMARY_SECTION_IDS as Et, EMBEDDING_REQUIREMENTS_FAILED as F, DeviceType as Fa, storageOccupancyCapability as Fi, activateAlarmProfile as Fn, isDetectionMacroClass as Fr, NcConditionDescriptorSchema as Ft, LabelAttributionSchema as G, systemEventSummaryScheduleOf as Gi, applySecureOnArmEdit as Gn, isSystemDelivery as Gr, NcSnoozeInputSchema as Gt, FULL_IMAGE_BBOX as H, systemEventDeliveryProblems as Hi, applyBlockingEdit as Hn, isRetiredClearKey as Hr, NcRuleUpdateInputSchema as Ht, EMBEDDING_REQUIREMENTS_UNAVAILABLE as I, errMsg as Ia, subKindsOf as Ii, addonWidgetsSourceCapability as In, isDrawableSubjectPath as Ir, NcConditionsSchema as It, LockStateSchema as J, templateVarsFor as Ji, audioMetricsCapability as Jn, kebabToCamel as Jr, NcStateSummarySectionIdSchema as Jt, ListTrackRelationsInputSchema as K, systemEventSummarySplitOf as Ki, armRemovalReconcile as Kn, isSystemEventSummaryRule as Kr, NcSnoozeSchema as Kt, EVENT_DENSITY_BATCH_MAX as L, EventCategory as La, subjectPathSampleFromBox as Li, alarmDeviceSecureOnArm as Ln, isEventKindLabelKey as Lr, NcRuleActionSchema as Lt, DeclaredDevices as M, tuple as Ma, sensorRaisedOf as Mi, TrackRelationSchema as Mn, isAppLinkBase as Mr, NcAlarmSensorsInputSchema as Mt, DetectionSourceSchema as N, union as Na, servedTemplateVars as Ni, TrackSideSchema as Nn, isAudioClassName as Nr, NcAlarmSensorsSchema as Nt, DEFAULT_EVENT_COLOR as O, record as Oa, ruleGroupingApplies as Oi, TimelapseRuleSchema as On, gifFromMp4Args as Or, NC_TAXONOMY as Ot, EMBEDDING_MODEL_FAILED as P, unknown as Pa, startMoverUnderClearance as Pi, TrackSourceSchema as Pn, isDefaultAlarmProfileId as Pr, NcAlarmSettingsPatchSchema as Pt, MAX_TRACK_PURITIES as Q, zoneAnalyticsCapability as Qi, buildViewerLink as Qn, ncMediaPolicyError as Qr, NcTemplatePreviewSchema as Qt, EVENT_KIND_BY_CAP as R, subjectPathSvgPoints as Ri, alarmPanelCapability as Rn, isEventOwnerType as Rr, NcRuleInputSchema as Rt, BucketAccumulator as S, discriminatedUnion as Sa, resolvePoolMemoryPolicy as Si, SetTrackPurityResultSchema as Sn, ensureAlarmProfiles as Sr, NC_GROUPING_MAX_SEC as St, ClearTrackRelationResultSchema as T, number as Ta, ruleAutoTriggersAlarm as Ti, TRACK_RELATIONS_DEFAULT_LIMIT as Tn, faceGalleryCapability as Tr, NC_SNOOZE_MAX_MINUTES as Tt, FailureCounters as U, systemEventFamilyProblems as Ui, applyModeDelaysEdits as Un, isRootSuppliableStepId as Ur, NcScheduleSchema as Ut, FIRST_LEVEL_MACRO_CLASSES as V, syncActiveAlarmProfileSettings as Vi, alertEpisodePayload as Vn, isMediaOwnerType as Vr, NcRuleTargetSchema as Vt, LABEL_SUPPLY_MACRO as W, systemEventFilterApplies as Wi, applyNonBlockingEdit as Wn, isScheduleActive as Wr, NcSharedSequenceSchema as Wt, MAX_BIRTH_DECISION_RECORDS as X, viewerLinkBaseOf as Xi, buildEventKindDescriptor as Xn, mayWriteToLocation as Xr, NcTaxonomySchema as Xt, MAX_ARCHIVED_DEBUG_NOTES as Y, vectorDimFromBase64 as Yi, audioModeOf as Yn, linkedStepOf as Yr, NcSystemEventKindSchema as Yt, MAX_BIRTH_LATENCY_PROXY_MS as Z, withSystemEventClasses as Zi, buildSubjectPath as Zn, memoizePinnedFfmpeg as Zr, NcTemplatePreviewInputSchema as Zt, AlarmArmModeSchema as _, sleep as _a, resolveDeliveryParams as _i, SCENE_DEFAULT_UNCOVERED_POLICY as _n, embeddingModelFailedError as _r, NC_CONFIRM_DEFAULT_TIMEOUT_MS as _t, CLIP_EMBEDDING_STEP_ID as a, CamProfileSchema as aa, paletteResultSchema as ai, RECORDING_SOURCE_CAMSTACK_ADDON as an, coreBlockIdFromAddonId as ar, NC_ALARM_DELAY_FIELDS as at, BaseDevice as b, array as ba, resolveModeNotifyPolicy as bi, SceneMonitorSchema as bn, embeddingRequirementsUnavailableError as br, NC_DEFAULT_SNOOZE_MINUTES as bt, readClusterModelPinStrict as c, RECORD_QUERY_MAX as ca, pickFaceRescoreIntervalMinutes as ci, RetrainOriginSchema as cn, deleteAlarmProfile as cr, NC_ALARM_SYSTEM_EVENT_KINDS as ct, APPEARANCE_BIN_COUNT as d, defineCustomActions as da, projectSubjectPath as di, SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX as dn, deriveRecordingMode as dr, NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT as dt, canonicalHash as ea, normalizeNcCapTarget as ei, OPS_LOG_DEFAULT_LIMIT as en, calendarEventLocalSpan as er, MOTION_CLOSE_AFTER_MS as et, APPEARANCE_BLOCKS as f, defineRecordKinds as fa, readDeviceStateFrom as fi, SCENE_CONFIRM_DEFAULT_TIMEOUT_MS as fn, describeSystemEventDeliveryProblem as fr, NC_CLIP_POST_ROLL_DEFAULT_SEC as ft, ActionTargetResolver as g, normalizeNodeId as ga, resolveClipSearchMinScore as gi, SCENE_DEFAULT_QUIET_SECONDS as gn, embeddingEncoderCapability as gr, NC_CONFIRM_DEFAULT_MAX_IMAGE_PX as gt, AUDIO_MACRO_LABELS as h, nodePin as ha, renameAlarmProfile as hi, SCENE_DEFAULT_OBSERVATION_SPACING_SEC as hn, deviceRemovalHookCapability as hr, NC_CONDITION_CATALOG as ht, CLIP_TEXT_MODELS as i, CAP_ADVANCE_FIELDS as ia, paintHex as ii, RECORDING_EXPORT_MAX_READ_BYTES as in, consumableLowPercentOf as ir, NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC as it, DETECTION_PIPELINE_CAP_NAME as j, templateLiteral as ja, secureActuationFor as ji, TrackRelationKindSchema as jn, isAlertEpisodeSeverity as jr, NcAlarmPostTriggeredActionSchema as jt, DEFAULT_TIMELAPSE_PREVIEW_TEXT as k, strictObject as ka, ruleKindOf as ki, TrackPlacementSchema as kn, isAlarmControllableCap as kr, NC_TEMPLATE_VARS as kt, resolveClusterModelPin as l, createEvent as la, pipelineAnalyticsCapability as li, RetrainStatusSchema as ln, deriveDetailCropRect as lr, NC_ALARM_TRIGGERED_MAX_SEC as lt, ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT as m, isDeviceScopedCap as ma, readTimelapseGeneratedAt as mi, SCENE_DEFAULT_CHECK_INTERVAL_SEC as mn, deviceAttentionKind as mr, NC_CLIP_RETENTION_MAX_SEC as mt, DEFAULT_CLIP_MODEL as n, isSourceCap as na, notificationRulesCapability as ni, PackageLlmVerdictSchema as nn, cloneAlarmSettings as nr, MotionTriggeredBySchema as nt, CLUSTER_MODEL_OWNER_ADDON_ID as o, InvalidRecordError as oa, parseAlertEpisodePayload as oi, RebuildObjectEmbeddingsRefusalSchema as on, cosineSimilarity as or, NC_ALARM_DELAY_MAX_SEC as ot, APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT as p, hydrateSchema as pa, readMotionTriggeredBy as pi, SCENE_DEFAULT_ANCHOR_THRESHOLD as pn, describeSystemEventFamilyProblem as pr, NC_CLIP_PRE_ROLL_DEFAULT_SEC as pt, ListTrackRelationsResultSchema as q, templateCarriesSummary as qi, assertTimelapseCadences as qn, isVectorIndexDimConflict as qr, NcSnoozeSuppressedSchema as qt, CLIP_IMAGE_MODELS as r, BaseAddon as ra, occupancyScope as ri, PoolMemoryWatchdog as rn, consumableFormOf as rr, NC_ALARM_DEFAULT_PROFILE_ID as rt, FACE_EMBEDDING_STEP_ID as s, RECORD_MAX_DATA_BYTES as sa, parseProcStatus as si, RetrainDismissedSchema as sn, createAlarmProfile as sr, NC_ALARM_NOTIFY_FIELDS as st, BUILTIN_CLIP_MODELS as t, evaluateSensorEdge as ta, normalizeNcMediaPolicy as ti, OpsLogEntrySchema as tn, classifyRetrainReview as tr, MediaFileKindEnum as tt, ACTION_TARGET_INVALIDATION_EVENTS as u, customAction as ua, plateGalleryCapability as ui, RetrainVerdictSubjectSchema as un, derivePostTriggeredAction as ur, NC_AUDIO_CONFIRM_HITS_DEFAULT as ut, ArchivedDebugNoteSchema as v, ZodObject as va, resolveLocationMode as vi, SCENE_DIVERGED as vn, embeddingRefusalOf as vr, NC_CONSUMABLE_LOW_PERCENT_DEFAULT as vt, ClearTrackRelationInputSchema as w, looseObject as wa, rootSupplyServes as wi, TIMELAPSE_DENSE_FLOOR_SEC as wn, evictionPolicyOfLocation as wr, NC_SENSOR_TRANSITION_KINDS as wt, BirthDecisionRecordSchema as x, boolean as xa, resolveNcMediaImage as xi, SetTrackPurityInputSchema as xn, encodeVectorBase64 as xr, NC_GROUPING_DEFAULT_SEC as xt, BIRTH_DECISION_DEFAULT_LIMIT as y, _enum as ya, resolveModeDelays as yi, SCENE_RESET_RECAPTURES as yn, embeddingRequirementsFailedError as yr, NC_CONSUMABLE_SYSTEM_EVENT_KINDS as yt, EVENT_OWNER_TYPES as z, summarizeReview as zi, alertEpisodeId as zn, isKnownTemplateVar as zr, NcRulePatchSchema as zt };