@camstack/ui-library 1.2.248 → 1.2.249

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,9 +5,9 @@ import * as __mfHostTrpcReactQuery from "@trpc/react-query";
5
5
  import { createTRPCReact } from "@trpc/react-query";
6
6
  import * as __mfHostReactJsxRuntime from "react/jsx-runtime";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ import { ALL_CAPABILITY_DEFINITIONS, CAM_PROFILE_ORDER, CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, CONTAINER_CHILD_PRIORITY, DEFAULT_EVENTS_BAND_BUFFER_SEC, DEFAULT_EVENT_COLOR, DEVICE_TYPE_CONTROL_KIND, DISPLAY_ICON_GROUPS, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EVENT_TAXONOMY, EXTENSION_SLOTS, EXTENSION_SLOT_IDS, EventCategory, ExposedDeviceSchema, FIRST_LEVEL_MACRO_CLASSES, KNOWN_CAP_NAMES, MAX_SENSOR_TRIGGER_DEVICES, MODEL_PROVIDER_IDS, NAVIGATION_LIGHT_LEVEL_MAX, NAVIGATION_LIGHT_LEVEL_MIN, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RECORDING_AUDIO_TRIGGER_OFFERED_DBFS, RECORDING_SOURCE_CAMSTACK_ADDON, RecordingConfigSchema, RecordingStatusSchema, SOURCE_CAP_ACTIVE_FIELD, STATIONARY_OVERLAY_STYLE, TIMEZONES, USE_ROOT_MODEL_SETTING_KEY, buildModelVariantGroups, canConvertUnit, clearModelBoundOverrides, clearModelBoundSetting, clusterStepSettingFieldsFor, collectModelBoundKeys, createDeviceProxy, createLazyTrpcSource, createSliceHandle, deriveRecordingMode, describeModelVariant, effectiveStepSettings, findTimezone, formatParkedFor, hasModelBoundOverrides, isRootSuppliableStepId, modelBindingKey, normalizeUnit, publicEventCategories, resolveContainerPrimaryChild, resolveDeviceControlKind, resolveDisplayIconKey, resolveModelBoundSetting, resolveRecordingProfiles, resolveRootSupply, resolveVariantModelId, resolveViewableDeviceIds, searchDisplayIcons, selectAssignedProfileSlots, stepSettingSource, tryConvertUnit, unitsForDimension, withFieldFrom, withoutField, writeModelBoundSetting } from "@camstack/types";
8
9
  import * as __mfHostReactDOM from "react-dom";
9
10
  import { createPortal } from "react-dom";
10
- import { ALL_CAPABILITY_DEFINITIONS, CAM_PROFILE_ORDER, CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, CONTAINER_CHILD_PRIORITY, DEFAULT_EVENTS_BAND_BUFFER_SEC, DEFAULT_EVENT_COLOR, DEVICE_TYPE_CONTROL_KIND, DISPLAY_ICON_GROUPS, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EVENT_TAXONOMY, EXTENSION_SLOTS, EXTENSION_SLOT_IDS, EventCategory, ExposedDeviceSchema, FIRST_LEVEL_MACRO_CLASSES, KNOWN_CAP_NAMES, MAX_SENSOR_TRIGGER_DEVICES, MODEL_PROVIDER_IDS, NAVIGATION_LIGHT_LEVEL_MAX, NAVIGATION_LIGHT_LEVEL_MIN, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RECORDING_AUDIO_TRIGGER_OFFERED_DBFS, RECORDING_SOURCE_CAMSTACK_ADDON, RecordingConfigSchema, RecordingStatusSchema, SOURCE_CAP_ACTIVE_FIELD, STATIONARY_OVERLAY_STYLE, TIMEZONES, USE_ROOT_MODEL_SETTING_KEY, buildModelVariantGroups, canConvertUnit, clearModelBoundOverrides, clearModelBoundSetting, clusterStepSettingFieldsFor, collectModelBoundKeys, createDeviceProxy, createLazyTrpcSource, createSliceHandle, deriveRecordingMode, describeModelVariant, effectiveStepSettings, findTimezone, formatParkedFor, hasModelBoundOverrides, isRootSuppliableStepId, modelBindingKey, normalizeUnit, publicEventCategories, resolveContainerPrimaryChild, resolveDeviceControlKind, resolveModelBoundSetting, resolveRecordingProfiles, resolveRootSupply, resolveVariantModelId, resolveViewableDeviceIds, searchDisplayIcons, selectAssignedProfileSlots, stepSettingSource, tryConvertUnit, unitsForDimension, withFieldFrom, withoutField, writeModelBoundSetting } from "@camstack/types";
11
11
  import * as __mfHostTanstackQuery from "@tanstack/react-query";
12
12
  import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
13
13
  import * as __mfHostReactDOMClient from "react-dom/client";
@@ -6442,6 +6442,756 @@ function useClusterNodes() {
6442
6442
  };
6443
6443
  }
6444
6444
  //#endregion
6445
+ //#region src/composites/device-meta.ts
6446
+ var DEVICE_TYPE_META = {
6447
+ [DeviceType.Camera]: {
6448
+ icon: Camera,
6449
+ label: "Camera"
6450
+ },
6451
+ [DeviceType.Hub]: {
6452
+ icon: Network,
6453
+ label: "Hub"
6454
+ },
6455
+ [DeviceType.Light]: {
6456
+ icon: Lightbulb,
6457
+ label: "Light"
6458
+ },
6459
+ [DeviceType.Siren]: {
6460
+ icon: Siren,
6461
+ label: "Siren"
6462
+ },
6463
+ [DeviceType.Switch]: {
6464
+ icon: ToggleLeft,
6465
+ label: "Switch"
6466
+ },
6467
+ [DeviceType.Sensor]: {
6468
+ icon: Activity,
6469
+ label: "Sensor"
6470
+ },
6471
+ [DeviceType.Thermostat]: {
6472
+ icon: Thermometer,
6473
+ label: "Thermostat"
6474
+ },
6475
+ [DeviceType.Climate]: {
6476
+ icon: AirVent,
6477
+ label: "Air conditioner"
6478
+ },
6479
+ [DeviceType.Button]: {
6480
+ icon: CircleDot,
6481
+ label: "Button"
6482
+ },
6483
+ [DeviceType.EventEmitter]: {
6484
+ icon: Zap,
6485
+ label: "Event Emitter"
6486
+ },
6487
+ [DeviceType.Update]: {
6488
+ icon: RefreshCw,
6489
+ label: "Update"
6490
+ },
6491
+ [DeviceType.Generic]: {
6492
+ icon: Box,
6493
+ label: "Generic"
6494
+ },
6495
+ [DeviceType.Notifier]: {
6496
+ icon: Bell,
6497
+ label: "Notifier"
6498
+ },
6499
+ [DeviceType.Script]: {
6500
+ icon: ScrollText,
6501
+ label: "Script"
6502
+ },
6503
+ [DeviceType.Automation]: {
6504
+ icon: Workflow,
6505
+ label: "Automation"
6506
+ },
6507
+ [DeviceType.Lock]: {
6508
+ icon: Lock,
6509
+ label: "Lock"
6510
+ },
6511
+ [DeviceType.Cover]: {
6512
+ icon: Blinds,
6513
+ label: "Cover"
6514
+ },
6515
+ [DeviceType.Valve]: {
6516
+ icon: Pipette,
6517
+ label: "Valve"
6518
+ },
6519
+ [DeviceType.Humidifier]: {
6520
+ icon: Waves,
6521
+ label: "Humidifier"
6522
+ },
6523
+ [DeviceType.WaterHeater]: {
6524
+ icon: Bath,
6525
+ label: "Water heater"
6526
+ },
6527
+ [DeviceType.Fan]: {
6528
+ icon: Fan,
6529
+ label: "Fan"
6530
+ },
6531
+ [DeviceType.MediaPlayer]: {
6532
+ icon: MonitorPlay,
6533
+ label: "Media player"
6534
+ },
6535
+ [DeviceType.AlarmPanel]: {
6536
+ icon: ShieldAlert,
6537
+ label: "Alarm panel"
6538
+ },
6539
+ [DeviceType.Control]: {
6540
+ icon: SlidersHorizontal,
6541
+ label: "Control"
6542
+ },
6543
+ [DeviceType.Presence]: {
6544
+ icon: MapPin,
6545
+ label: "Presence"
6546
+ },
6547
+ [DeviceType.Weather]: {
6548
+ icon: CloudSun,
6549
+ label: "Weather"
6550
+ },
6551
+ [DeviceType.Vacuum]: {
6552
+ icon: Bot,
6553
+ label: "Vacuum"
6554
+ },
6555
+ [DeviceType.LawnMower]: {
6556
+ icon: Tractor,
6557
+ label: "Lawn mower"
6558
+ },
6559
+ [DeviceType.Container]: {
6560
+ icon: Box,
6561
+ label: "Container"
6562
+ },
6563
+ [DeviceType.Image]: {
6564
+ icon: Image,
6565
+ label: "Image"
6566
+ },
6567
+ [DeviceType.PetFeeder]: {
6568
+ icon: Bone,
6569
+ label: "Pet feeder"
6570
+ }
6571
+ };
6572
+ var DEVICE_ROLE_META = {
6573
+ [DeviceRole.Siren]: {
6574
+ icon: Siren,
6575
+ label: "Siren"
6576
+ },
6577
+ [DeviceRole.Floodlight]: {
6578
+ icon: Lightbulb,
6579
+ label: "Floodlight"
6580
+ },
6581
+ [DeviceRole.Spotlight]: {
6582
+ icon: Flashlight,
6583
+ label: "Spotlight"
6584
+ },
6585
+ [DeviceRole.PirSensor]: {
6586
+ icon: Radar,
6587
+ label: "PIR sensor"
6588
+ },
6589
+ [DeviceRole.Chime]: {
6590
+ icon: Bell,
6591
+ label: "Chime"
6592
+ },
6593
+ [DeviceRole.Autotrack]: {
6594
+ icon: Crosshair,
6595
+ label: "Auto-track"
6596
+ },
6597
+ [DeviceRole.Nightvision]: {
6598
+ icon: Moon,
6599
+ label: "Night vision"
6600
+ },
6601
+ [DeviceRole.PrivacyMask]: {
6602
+ icon: EyeOff,
6603
+ label: "Privacy mask"
6604
+ },
6605
+ [DeviceRole.Doorbell]: {
6606
+ icon: DoorClosed,
6607
+ label: "Doorbell"
6608
+ },
6609
+ [DeviceRole.BinaryHelper]: {
6610
+ icon: ToggleLeft,
6611
+ label: "Binary helper"
6612
+ },
6613
+ [DeviceRole.MotionSensor]: {
6614
+ icon: Footprints,
6615
+ label: "Motion sensor"
6616
+ },
6617
+ [DeviceRole.ContactSensor]: {
6618
+ icon: DoorOpen,
6619
+ label: "Contact sensor"
6620
+ },
6621
+ [DeviceRole.LeakSensor]: {
6622
+ icon: Droplet,
6623
+ label: "Leak sensor"
6624
+ },
6625
+ [DeviceRole.SmokeSensor]: {
6626
+ icon: AlarmSmoke,
6627
+ label: "Smoke sensor"
6628
+ },
6629
+ [DeviceRole.COSensor]: {
6630
+ icon: Wind,
6631
+ label: "CO sensor"
6632
+ },
6633
+ [DeviceRole.GasSensor]: {
6634
+ icon: Flame,
6635
+ label: "Gas sensor"
6636
+ },
6637
+ [DeviceRole.TamperSensor]: {
6638
+ icon: ShieldAlert,
6639
+ label: "Tamper sensor"
6640
+ },
6641
+ [DeviceRole.VibrationSensor]: {
6642
+ icon: Vibrate,
6643
+ label: "Vibration sensor"
6644
+ },
6645
+ [DeviceRole.ConnectivitySensor]: {
6646
+ icon: Wifi,
6647
+ label: "Connectivity"
6648
+ },
6649
+ [DeviceRole.SoundSensor]: {
6650
+ icon: Volume2,
6651
+ label: "Sound sensor"
6652
+ },
6653
+ [DeviceRole.BinarySensor]: {
6654
+ icon: ToggleLeft,
6655
+ label: "Binary sensor"
6656
+ },
6657
+ [DeviceRole.TemperatureSensor]: {
6658
+ icon: Thermometer,
6659
+ label: "Temperature"
6660
+ },
6661
+ [DeviceRole.HumiditySensor]: {
6662
+ icon: Droplets,
6663
+ label: "Humidity"
6664
+ },
6665
+ [DeviceRole.AmbientLightSensor]: {
6666
+ icon: Sun,
6667
+ label: "Ambient light"
6668
+ },
6669
+ [DeviceRole.PressureSensor]: {
6670
+ icon: Gauge,
6671
+ label: "Pressure"
6672
+ },
6673
+ [DeviceRole.WindSensor]: {
6674
+ icon: Wind,
6675
+ label: "Wind"
6676
+ },
6677
+ [DeviceRole.RainSensor]: {
6678
+ icon: CloudRain,
6679
+ label: "Rain"
6680
+ },
6681
+ [DeviceRole.UvSensor]: {
6682
+ icon: SunMedium,
6683
+ label: "UV index"
6684
+ },
6685
+ [DeviceRole.SolarRadiationSensor]: {
6686
+ icon: Sunrise,
6687
+ label: "Solar radiation"
6688
+ },
6689
+ [DeviceRole.SoilMoistureSensor]: {
6690
+ icon: Sprout,
6691
+ label: "Soil moisture"
6692
+ },
6693
+ [DeviceRole.PowerSensor]: {
6694
+ icon: Zap,
6695
+ label: "Power"
6696
+ },
6697
+ [DeviceRole.EnergySensor]: {
6698
+ icon: Zap,
6699
+ label: "Energy"
6700
+ },
6701
+ [DeviceRole.VoltageSensor]: {
6702
+ icon: Zap,
6703
+ label: "Voltage"
6704
+ },
6705
+ [DeviceRole.CurrentSensor]: {
6706
+ icon: Zap,
6707
+ label: "Current"
6708
+ },
6709
+ [DeviceRole.AirQualitySensor]: {
6710
+ icon: Wind,
6711
+ label: "Air quality"
6712
+ },
6713
+ [DeviceRole.BatterySensor]: {
6714
+ icon: Gauge,
6715
+ label: "Battery"
6716
+ },
6717
+ [DeviceRole.NumericSensor]: {
6718
+ icon: Hash,
6719
+ label: "Numeric sensor"
6720
+ },
6721
+ [DeviceRole.EnumSensor]: {
6722
+ icon: List,
6723
+ label: "Enum sensor"
6724
+ },
6725
+ [DeviceRole.DateTimeSensor]: {
6726
+ icon: Clock,
6727
+ label: "Date / time"
6728
+ },
6729
+ [DeviceRole.GenericSensor]: {
6730
+ icon: Gauge,
6731
+ label: "Sensor"
6732
+ },
6733
+ [DeviceRole.NumericControl]: {
6734
+ icon: SlidersHorizontal,
6735
+ label: "Numeric control"
6736
+ },
6737
+ [DeviceRole.SelectControl]: {
6738
+ icon: ListChecks,
6739
+ label: "Select control"
6740
+ },
6741
+ [DeviceRole.TextControl]: {
6742
+ icon: Type,
6743
+ label: "Text control"
6744
+ },
6745
+ [DeviceRole.DateTimeControl]: {
6746
+ icon: Clock,
6747
+ label: "Date / time control"
6748
+ },
6749
+ [DeviceRole.MobilePushNotifier]: {
6750
+ icon: Smartphone,
6751
+ label: "Mobile push"
6752
+ },
6753
+ [DeviceRole.MessagingNotifier]: {
6754
+ icon: MessageSquare,
6755
+ label: "Messaging"
6756
+ },
6757
+ [DeviceRole.EmailNotifier]: {
6758
+ icon: Mail,
6759
+ label: "Email"
6760
+ },
6761
+ [DeviceRole.GenericNotifier]: {
6762
+ icon: Bell,
6763
+ label: "Notifier"
6764
+ }
6765
+ };
6766
+ var DEVICE_TYPE_VALUES$1 = new Set(Object.values(DeviceType));
6767
+ var DEVICE_ROLE_VALUES = new Set(Object.values(DeviceRole));
6768
+ function isDeviceType(value) {
6769
+ return DEVICE_TYPE_VALUES$1.has(value);
6770
+ }
6771
+ function isDeviceRole(value) {
6772
+ return DEVICE_ROLE_VALUES.has(value);
6773
+ }
6774
+ /** Meta for a known `DeviceType`. */
6775
+ function deviceTypeMeta(type) {
6776
+ return DEVICE_TYPE_META[type];
6777
+ }
6778
+ /** Meta for a known `DeviceRole`. */
6779
+ function deviceRoleMeta(role) {
6780
+ return DEVICE_ROLE_META[role];
6781
+ }
6782
+ var FALLBACK_DEVICE_TYPE_META = DEVICE_TYPE_META[DeviceType.Generic];
6783
+ var FALLBACK_DEVICE_ROLE_META = DEVICE_ROLE_META[DeviceRole.GenericSensor];
6784
+ /**
6785
+ * Runtime-string-safe lookup: device data crosses process / network boundaries
6786
+ * as plain strings, so an unrecognised value falls back to the `Generic` meta
6787
+ * instead of throwing.
6788
+ *
6789
+ * This also guards mixed-version module-federation loads where the host
6790
+ * `@camstack/types` enum has a new member before the loaded ui-library bundle
6791
+ * has the matching `DEVICE_TYPE_META` entry.
6792
+ */
6793
+ function deviceTypeMetaOf(type) {
6794
+ if (!isDeviceType(type)) return FALLBACK_DEVICE_TYPE_META;
6795
+ return DEVICE_TYPE_META[type] ?? FALLBACK_DEVICE_TYPE_META;
6796
+ }
6797
+ /** Runtime-string-safe role lookup; unknown roles fall back to `GenericSensor`. */
6798
+ function deviceRoleMetaOf(role) {
6799
+ if (!isDeviceRole(role)) return FALLBACK_DEVICE_ROLE_META;
6800
+ return DEVICE_ROLE_META[role] ?? FALLBACK_DEVICE_ROLE_META;
6801
+ }
6802
+ /**
6803
+ * Icons an operator may pick for a per-device `display.icon` override (and for
6804
+ * a per-role default), keyed by a stable kebab-case name. Named imports only —
6805
+ * no dynamic indexing into lucide, so the bundle carries exactly this set.
6806
+ * Unknown keys resolve to `undefined` (caller falls through to the next level).
6807
+ *
6808
+ * The vocabulary is the one the VIEWER draws from too (its own icon module,
6809
+ * keyed by these same strings): a key added here that the phone cannot draw
6810
+ * falls through to the next resolution level there, which reads as "my change
6811
+ * did nothing". So the KEY is the contract — a stable kebab-case string, never
6812
+ * a glyph identity — and `device-meta.test.ts` pins that every key resolves
6813
+ * and that the set never loses a key an operator may already have persisted.
6814
+ *
6815
+ * Grouped by what an operator is naming: a device, a light or appliance, a
6816
+ * sensor or the weather it reads, a room, or what moves through it. `gauge2`
6817
+ * is a legacy alias kept for rows that persisted it.
6818
+ */
6819
+ var DISPLAY_ICON_REGISTRY = {
6820
+ camera: Camera,
6821
+ network: Network,
6822
+ "toggle-left": ToggleLeft,
6823
+ "toggle-right": ToggleRight,
6824
+ "circle-dot": CircleDot,
6825
+ "refresh-cw": RefreshCw,
6826
+ box: Box,
6827
+ "scroll-text": ScrollText,
6828
+ workflow: Workflow,
6829
+ lock: Lock,
6830
+ "lock-open": LockOpen,
6831
+ blinds: Blinds,
6832
+ "air-vent": AirVent,
6833
+ "monitor-play": MonitorPlay,
6834
+ "sliders-horizontal": SlidersHorizontal,
6835
+ "map-pin": MapPin,
6836
+ bot: Bot,
6837
+ tractor: Tractor,
6838
+ image: Image,
6839
+ bone: Bone,
6840
+ "door-closed": DoorClosed,
6841
+ "door-open": DoorOpen,
6842
+ crosshair: Crosshair,
6843
+ eye: Eye,
6844
+ "eye-off": EyeOff,
6845
+ smartphone: Smartphone,
6846
+ phone: Phone,
6847
+ tablet: Tablet,
6848
+ laptop: Laptop,
6849
+ monitor: Monitor,
6850
+ tv: Tv,
6851
+ speaker: Speaker,
6852
+ printer: Printer,
6853
+ projector: Projector,
6854
+ "gamepad-2": Gamepad2,
6855
+ radio: Radio,
6856
+ router: Router,
6857
+ bluetooth: Bluetooth,
6858
+ cpu: Cpu,
6859
+ "hard-drive": HardDrive,
6860
+ server: Server,
6861
+ mic: Mic,
6862
+ "mic-off": MicOff,
6863
+ video: Video,
6864
+ "video-off": VideoOff,
6865
+ music: Music,
6866
+ plug: Plug,
6867
+ "plug-zap": PlugZap,
6868
+ power: Power,
6869
+ "power-off": PowerOff,
6870
+ cable: Cable,
6871
+ battery: Battery,
6872
+ "battery-charging": BatteryCharging,
6873
+ "battery-full": BatteryFull,
6874
+ "battery-low": BatteryLow,
6875
+ key: Key,
6876
+ "key-round": KeyRound,
6877
+ mailbox: Mailbox,
6878
+ package: Package,
6879
+ shield: Shield,
6880
+ "shield-check": ShieldCheck,
6881
+ "shield-off": ShieldOff,
6882
+ lightbulb: Lightbulb,
6883
+ lamp: Lamp,
6884
+ "lamp-ceiling": LampCeiling,
6885
+ "lamp-desk": LampDesk,
6886
+ "lamp-floor": LampFloor,
6887
+ flashlight: Flashlight,
6888
+ heater: Heater,
6889
+ snowflake: Snowflake,
6890
+ "thermometer-sun": ThermometerSun,
6891
+ "thermometer-snowflake": ThermometerSnowflake,
6892
+ refrigerator: Refrigerator,
6893
+ microwave: Microwave,
6894
+ "washing-machine": WashingMachine,
6895
+ "cooking-pot": CookingPot,
6896
+ utensils: Utensils,
6897
+ thermometer: Thermometer,
6898
+ droplets: Droplets,
6899
+ droplet: Droplet,
6900
+ gauge: Gauge,
6901
+ gauge2: Gauge,
6902
+ sun: Sun,
6903
+ "sun-medium": SunMedium,
6904
+ sunrise: Sunrise,
6905
+ moon: Moon,
6906
+ wind: Wind,
6907
+ cloud: Cloud,
6908
+ "cloud-rain": CloudRain,
6909
+ "cloud-sun": CloudSun,
6910
+ waves: Waves,
6911
+ sprout: Sprout,
6912
+ leaf: Leaf,
6913
+ flower: Flower,
6914
+ zap: Zap,
6915
+ flame: Flame,
6916
+ bell: Bell,
6917
+ "bell-ring": BellRing,
6918
+ "bell-off": BellOff,
6919
+ siren: Siren,
6920
+ radar: Radar,
6921
+ activity: Activity,
6922
+ hash: Hash,
6923
+ clock: Clock,
6924
+ timer: Timer,
6925
+ calendar: Calendar,
6926
+ pipette: Pipette,
6927
+ fan: Fan,
6928
+ bath: Bath,
6929
+ footprints: Footprints,
6930
+ "alarm-smoke": AlarmSmoke,
6931
+ "shield-alert": ShieldAlert,
6932
+ vibrate: Vibrate,
6933
+ wifi: Wifi,
6934
+ "wifi-off": WifiOff,
6935
+ "volume-2": Volume2,
6936
+ "volume-x": VolumeX,
6937
+ heart: Heart,
6938
+ "heart-pulse": HeartPulse,
6939
+ star: Star,
6940
+ sparkles: Sparkles,
6941
+ house: House,
6942
+ sofa: Sofa,
6943
+ "bed-double": BedDouble,
6944
+ bed: Bed,
6945
+ warehouse: Warehouse,
6946
+ trees: Trees,
6947
+ "tree-pine": TreePine,
6948
+ fence: Fence,
6949
+ store: Store,
6950
+ building: Building,
6951
+ "building-2": Building2,
6952
+ mountain: Mountain,
6953
+ umbrella: Umbrella,
6954
+ dumbbell: Dumbbell,
6955
+ wrench: Wrench,
6956
+ hammer: Hammer,
6957
+ drill: Drill,
6958
+ "waves-ladder": WavesLadder,
6959
+ "circle-parking": CircleParking,
6960
+ briefcase: Briefcase,
6961
+ route: Route,
6962
+ car: Car,
6963
+ bike: Bike,
6964
+ bus: Bus,
6965
+ truck: Truck,
6966
+ plane: Plane,
6967
+ "train-front": TrainFront,
6968
+ sailboat: Sailboat,
6969
+ dog: Dog,
6970
+ cat: Cat,
6971
+ bird: Bird,
6972
+ "paw-print": PawPrint,
6973
+ baby: Baby,
6974
+ user: User,
6975
+ users: Users,
6976
+ "person-standing": PersonStanding
6977
+ };
6978
+ /** Resolve a `display.icon` name to a lucide component, or `undefined` when the
6979
+ * name is not in the curated `DISPLAY_ICON_REGISTRY`. */
6980
+ function resolveDisplayIcon(name) {
6981
+ return Object.hasOwn(DISPLAY_ICON_REGISTRY, name) ? DISPLAY_ICON_REGISTRY[name] : void 0;
6982
+ }
6983
+ /**
6984
+ * EXHAUSTIVE device-type filter options — one entry per `DeviceType` member,
6985
+ * derived from `DEVICE_TYPE_META` so the set is always complete and a newly
6986
+ * added `DeviceType` automatically appears in every type filter (Devices page,
6987
+ * adoption modal, …) without touching any consumer.
6988
+ *
6989
+ * The previous per-consumer logic built options from the types PRESENT in the
6990
+ * list (or a hand-maintained subset), so recently-added types (vacuum, lawn
6991
+ * mower, valve, humidifier, water heater, weather, image, …) were silently
6992
+ * missing from the dropdown. This is the single source of truth instead.
6993
+ *
6994
+ * `id` is the raw `DeviceType` string (matches the filtering predicate, which
6995
+ * compares against `device.type`); `label` + `icon` come from the meta map.
6996
+ * Ordered by `DeviceType` declaration order for a stable, intentional layout.
6997
+ */
6998
+ var ALL_DEVICE_TYPE_FILTER_OPTIONS = Object.values(DeviceType).map((type) => {
6999
+ const meta = deviceTypeMetaOf(type);
7000
+ return {
7001
+ id: type,
7002
+ label: meta.label,
7003
+ icon: meta.icon
7004
+ };
7005
+ });
7006
+ function allDeviceTypeFilterOptions() {
7007
+ return ALL_DEVICE_TYPE_FILTER_OPTIONS;
7008
+ }
7009
+ //#endregion
7010
+ //#region src/composites/icon-picker.tsx
7011
+ /**
7012
+ * icon-picker — the ONE control for choosing an icon, wherever admin-ui asks.
7013
+ *
7014
+ * There were two: a device one over `DISPLAY_ICON_REGISTRY` and a room one
7015
+ * over `LOCATION_ICON_IDS`, identical apart from the vocabulary they walked.
7016
+ * That was tolerable while the device set was 56 glyphs in a flat grid. It is
7017
+ * not at 157: a picker that only lets the operator HUNT is a picker they stop
7018
+ * using, and the two copies were already drifting — only one of them had an
7019
+ * i18n'd label.
7020
+ *
7021
+ * So the vocabulary, the grouping and the search all come from
7022
+ * `@camstack/types`' `display-icon-vocabulary`, which is the same module the
7023
+ * viewer's sheet searches. What is left here is the web half: a popover, a
7024
+ * grid, and a text field.
7025
+ *
7026
+ * ## What it does NOT own
7027
+ *
7028
+ * - **The glyphs.** `resolveGlyph` is a prop, because this package's table is
7029
+ * `lucide-react` and the viewer's is `lucide-react-native`. A key this build
7030
+ * cannot draw is SKIPPED rather than rendered as a hole — an empty tile
7031
+ * reads as a rendering bug, not as a missing choice.
7032
+ * - **The words.** Every string is a prop. This package has no i18n (one file
7033
+ * in it imports react-i18next, and that is the exception), and a control
7034
+ * that reached for a translation would force it on every consumer.
7035
+ * - **The write.** `onPick` is called and that is all; whether it is a form
7036
+ * value or an immediate mutation is the caller's business, as it already was
7037
+ * in both pickers this replaces.
7038
+ *
7039
+ * ## The clear tile is never filtered away
7040
+ *
7041
+ * The "inherit" / "no icon" choice sits above the sections and ignores the
7042
+ * search. A query that hid it would strand the operator on a glyph they picked
7043
+ * by accident with no way back to the default — the one action a picker must
7044
+ * always offer.
7045
+ */
7046
+ function IconPicker({ value, onPick, resolveGlyph, fallbackGlyph, labels, only, extraWords, clearGlyph, labelFor, disabled = false }) {
7047
+ const [open, setOpen] = useState(false);
7048
+ const [query, setQuery] = useState("");
7049
+ const allowed = useMemo(() => only === void 0 ? null : new Set(only), [only]);
7050
+ const sections = useMemo(() => {
7051
+ const hits = searchDisplayIcons(query, extraWords);
7052
+ const byGroup = /* @__PURE__ */ new Map();
7053
+ for (const hit of hits) {
7054
+ const { key, group } = hit.entry;
7055
+ if (allowed !== null && !allowed.has(key)) continue;
7056
+ if (resolveGlyph(key) === void 0) continue;
7057
+ const bucket = byGroup.get(group);
7058
+ if (bucket === void 0) byGroup.set(group, [key]);
7059
+ else bucket.push(key);
7060
+ }
7061
+ return DISPLAY_ICON_GROUPS.flatMap((group) => {
7062
+ const keys = byGroup.get(group);
7063
+ return keys === void 0 || keys.length === 0 ? [] : [{
7064
+ group,
7065
+ keys
7066
+ }];
7067
+ });
7068
+ }, [
7069
+ query,
7070
+ extraWords,
7071
+ allowed,
7072
+ resolveGlyph
7073
+ ]);
7074
+ const Current = (value === null ? void 0 : resolveGlyph(value)) ?? fallbackGlyph;
7075
+ const choose = async (next) => {
7076
+ setOpen(false);
7077
+ setQuery("");
7078
+ await onPick(next);
7079
+ };
7080
+ const Clear = clearGlyph ?? fallbackGlyph;
7081
+ return /* @__PURE__ */ jsxs("div", {
7082
+ className: "relative flex shrink-0 items-center gap-2",
7083
+ children: [/* @__PURE__ */ jsx("button", {
7084
+ type: "button",
7085
+ disabled,
7086
+ onClick: () => setOpen((o) => !o),
7087
+ "aria-expanded": open,
7088
+ "aria-label": labels.trigger,
7089
+ title: labels.trigger,
7090
+ className: "rounded border border-border p-1 text-foreground-muted hover:bg-surface-hover disabled:opacity-50",
7091
+ children: /* @__PURE__ */ jsx(Current, { className: "h-4 w-4" })
7092
+ }), open && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
7093
+ type: "button",
7094
+ "aria-label": labels.close,
7095
+ onClick: () => setOpen(false),
7096
+ className: "fixed inset-0 z-10 cursor-default"
7097
+ }), /* @__PURE__ */ jsxs("div", {
7098
+ role: "group",
7099
+ "aria-label": labels.panel,
7100
+ className: "absolute left-0 top-full z-20 mt-1 max-h-80 w-72 overflow-y-auto rounded-md border border-border bg-surface p-2 shadow-lg",
7101
+ children: [
7102
+ /* @__PURE__ */ jsx("input", {
7103
+ type: "search",
7104
+ value: query,
7105
+ autoFocus: true,
7106
+ onChange: (e) => setQuery(e.target.value),
7107
+ placeholder: labels.searchPlaceholder,
7108
+ "aria-label": labels.searchPlaceholder,
7109
+ className: "mb-2 w-full rounded border border-border bg-surface-raised px-2 py-1 text-xs text-foreground placeholder:text-foreground-subtle"
7110
+ }),
7111
+ /* @__PURE__ */ jsxs("button", {
7112
+ type: "button",
7113
+ onClick: () => void choose(null),
7114
+ "aria-pressed": value === null,
7115
+ className: "mb-2 flex w-full items-center gap-1.5 rounded px-1.5 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover",
7116
+ children: [/* @__PURE__ */ jsx(Clear, { className: "h-3.5 w-3.5" }), labels.clear]
7117
+ }),
7118
+ sections.length === 0 ? /* @__PURE__ */ jsx("p", {
7119
+ className: "px-1.5 py-2 text-[11px] text-foreground-subtle",
7120
+ children: labels.noMatches
7121
+ }) : sections.map(({ group, keys }) => /* @__PURE__ */ jsxs("section", {
7122
+ className: "mb-2 last:mb-0",
7123
+ children: [/* @__PURE__ */ jsx("h4", {
7124
+ className: "px-1.5 pb-1 text-[10px] uppercase tracking-wide text-foreground-subtle",
7125
+ children: labels.groups[group]
7126
+ }), /* @__PURE__ */ jsx("div", {
7127
+ className: "grid grid-cols-7 gap-1",
7128
+ children: keys.map((key) => {
7129
+ const Glyph = resolveGlyph(key);
7130
+ if (Glyph === void 0) return null;
7131
+ const selected = value === key;
7132
+ return /* @__PURE__ */ jsx("button", {
7133
+ type: "button",
7134
+ onClick: () => void choose(key),
7135
+ "aria-pressed": selected,
7136
+ "aria-label": labelFor?.(key) ?? key,
7137
+ title: labelFor?.(key) ?? key,
7138
+ className: selected ? "flex items-center justify-center rounded border border-primary bg-primary/10 p-1.5 text-foreground" : "flex items-center justify-center rounded border border-transparent p-1.5 text-foreground-muted hover:bg-surface-hover",
7139
+ children: /* @__PURE__ */ jsx(Glyph, { className: "h-4 w-4" })
7140
+ }, key);
7141
+ })
7142
+ })]
7143
+ }, group))
7144
+ ]
7145
+ })] })]
7146
+ });
7147
+ }
7148
+ //#endregion
7149
+ //#region src/composites/config-icon-field.tsx
7150
+ /**
7151
+ * The control behind a `type: 'icon'` settings field (D780).
7152
+ *
7153
+ * The shared `<IconPicker>` over the display-icon vocabulary, with the device
7154
+ * glyph table — the same control and the same keys the device and room
7155
+ * pickers use. An icon field always draws something, so clearing is "back to
7156
+ * the field's default", and a stored value the vocabulary does not know (a
7157
+ * free-text lucide name from before D780) is drawn as that default.
7158
+ */
7159
+ function labelsFor(label) {
7160
+ return {
7161
+ trigger: `Choose an icon for ${label}`,
7162
+ panel: `${label} icon`,
7163
+ close: "Close",
7164
+ searchPlaceholder: "Search icons",
7165
+ clear: "Default icon",
7166
+ noMatches: "No icon matches",
7167
+ groups: {
7168
+ device: "Devices",
7169
+ lighting: "Lighting",
7170
+ sensing: "Sensing",
7171
+ place: "Places",
7172
+ life: "Life"
7173
+ }
7174
+ };
7175
+ }
7176
+ function ConfigIconFieldControl({ label, value, defaultKey, disabled, onChange }) {
7177
+ const key = resolveDisplayIconKey(value, defaultKey);
7178
+ const DefaultGlyph = resolveDisplayIcon(defaultKey) ?? ShieldCheck;
7179
+ return /* @__PURE__ */ jsxs("div", {
7180
+ className: "flex items-center gap-2",
7181
+ children: [/* @__PURE__ */ jsx(IconPicker, {
7182
+ value: key === "" ? null : key,
7183
+ onPick: (next) => onChange(next ?? defaultKey),
7184
+ resolveGlyph: resolveDisplayIcon,
7185
+ fallbackGlyph: DefaultGlyph,
7186
+ labels: labelsFor(label),
7187
+ disabled
7188
+ }), /* @__PURE__ */ jsx("span", {
7189
+ className: "text-[11px] text-foreground-subtle font-mono",
7190
+ children: key
7191
+ })]
7192
+ });
7193
+ }
7194
+ //#endregion
6445
7195
  //#region src/generated/system-hooks.ts
6446
7196
  /**
6447
7197
  * Per-method React-Query hooks for every `scope: 'system'` capability.
@@ -7779,6 +8529,10 @@ var usePipelineAnalyticsCompleteRetrainTrack = trpc.pipelineAnalytics.completeRe
7779
8529
  var usePipelineAnalyticsRestageRetrainTrack = trpc.pipelineAnalytics.restageRetrainTrack.useMutation;
7780
8530
  /** Generated alias around `trpc.pipelineAnalytics.getRetrainExportUrl.useQuery`. */
7781
8531
  var usePipelineAnalyticsGetRetrainExportUrl = trpc.pipelineAnalytics.getRetrainExportUrl.useQuery;
8532
+ /** Generated alias around `trpc.pipelineAnalytics.setRetrainFrameComplete.useMutation`. */
8533
+ var usePipelineAnalyticsSetRetrainFrameComplete = trpc.pipelineAnalytics.setRetrainFrameComplete.useMutation;
8534
+ /** Generated alias around `trpc.pipelineAnalytics.writeRetrainArchive.useMutation`. */
8535
+ var usePipelineAnalyticsWriteRetrainArchive = trpc.pipelineAnalytics.writeRetrainArchive.useMutation;
7782
8536
  /** Generated alias around `trpc.pipelineAnalytics.getEventMedia.useQuery`. */
7783
8537
  var usePipelineAnalyticsGetEventMedia = trpc.pipelineAnalytics.getEventMedia.useQuery;
7784
8538
  /** Generated alias around `trpc.pipelineAnalytics.getTrackMedia.useQuery`. */
@@ -8567,6 +9321,14 @@ var useUserManagementOauthExchangeCode = trpc.userManagement.oauthExchangeCode.u
8567
9321
  var useUserManagementOauthRefresh = trpc.userManagement.oauthRefresh.useMutation;
8568
9322
  /** Generated alias around `trpc.userManagement.oauthVerifyAccessToken.useQuery`. */
8569
9323
  var useUserManagementOauthVerifyAccessToken = trpc.userManagement.oauthVerifyAccessToken.useQuery;
9324
+ /** Generated alias around `trpc.userManagement.listExternalIdentities.useQuery`. */
9325
+ var useUserManagementListExternalIdentities = trpc.userManagement.listExternalIdentities.useQuery;
9326
+ /** Generated alias around `trpc.userManagement.findUserByExternalIdentity.useQuery`. */
9327
+ var useUserManagementFindUserByExternalIdentity = trpc.userManagement.findUserByExternalIdentity.useQuery;
9328
+ /** Generated alias around `trpc.userManagement.linkExternalIdentity.useMutation`. */
9329
+ var useUserManagementLinkExternalIdentity = trpc.userManagement.linkExternalIdentity.useMutation;
9330
+ /** Generated alias around `trpc.userManagement.unlinkExternalIdentity.useMutation`. */
9331
+ var useUserManagementUnlinkExternalIdentity = trpc.userManagement.unlinkExternalIdentity.useMutation;
8570
9332
  /** Generated alias around `trpc.userManagement.listOauthSessions.useQuery`. */
8571
9333
  var useUserManagementListOauthSessions = trpc.userManagement.listOauthSessions.useQuery;
8572
9334
  /** Generated alias around `trpc.userManagement.revokeOauthSession.useMutation`. */
@@ -27142,6 +27904,23 @@ function ColorField({ field, value, onChange, disabled, translationFn }) {
27142
27904
  })]
27143
27905
  });
27144
27906
  }
27907
+ function IconField({ field, value, onChange, disabled, translationFn }) {
27908
+ const defaultKey = typeof field.default === "string" ? field.default : "";
27909
+ return /* @__PURE__ */ jsx(FieldWrapper, {
27910
+ label: field.label,
27911
+ description: field.description,
27912
+ required: field.required,
27913
+ span: field.span,
27914
+ translationFn,
27915
+ children: /* @__PURE__ */ jsx(ConfigIconFieldControl, {
27916
+ label: resolveLabel(field.label, translationFn) ?? field.label,
27917
+ value,
27918
+ defaultKey,
27919
+ disabled: disabled || field.disabled,
27920
+ onChange
27921
+ })
27922
+ });
27923
+ }
27145
27924
  function ProbeField({ field, value, onChange, disabled, translationFn, onTestField, onAction, externalProbe }) {
27146
27925
  const [localStatus, setLocalStatus] = useState("idle");
27147
27926
  const [localResult, setLocalResult] = useState(null);
@@ -27776,8 +28555,16 @@ function AddonActionButtonField({ field, values, disabled, onAction }) {
27776
28555
  const [loading, setLoading] = useState(false);
27777
28556
  const [status, setStatus] = useState(null);
27778
28557
  const confirm = useConfirm();
28558
+ const prompt = usePrompt();
27779
28559
  const deviceId = useDeviceId();
27780
28560
  const handleClick = async () => {
28561
+ const asked = field.prompt ? await prompt({
28562
+ title: field.prompt.title ?? field.label,
28563
+ ...field.prompt.message !== void 0 ? { message: field.prompt.message } : {},
28564
+ ...field.prompt.placeholder !== void 0 ? { placeholder: field.prompt.placeholder } : {},
28565
+ confirmLabel: field.buttonLabel
28566
+ }) : null;
28567
+ if (field.prompt && asked === null) return;
27781
28568
  if (field.confirmMessage) {
27782
28569
  if (!await confirm({
27783
28570
  title: field.label,
@@ -27788,6 +28575,7 @@ function AddonActionButtonField({ field, values, disabled, onAction }) {
27788
28575
  }
27789
28576
  if (!onAction) return;
27790
28577
  const params = resolveActionParams(field, values);
28578
+ if (field.prompt && asked !== null) params[field.prompt.param] = asked;
27791
28579
  if (field.includeDeviceId === true && deviceId !== null && deviceId !== void 0) params["deviceId"] = deviceId;
27792
28580
  setLoading(true);
27793
28581
  setStatus(null);
@@ -28248,6 +29036,13 @@ function FormField({ field, values, onChange, disabled, translationFn, onTestFie
28248
29036
  disabled,
28249
29037
  translationFn
28250
29038
  });
29039
+ case "icon": return /* @__PURE__ */ jsx(IconField, {
29040
+ field,
29041
+ value,
29042
+ onChange: handleChange,
29043
+ disabled,
29044
+ translationFn
29045
+ });
28251
29046
  case "node-select": return /* @__PURE__ */ jsx(NodeSelectField, {
28252
29047
  field,
28253
29048
  value,
@@ -49397,1248 +50192,683 @@ function SwitchInlineControl({ trpc, deviceId, features, optimistic }) {
49397
50192
  optimistic
49398
50193
  });
49399
50194
  }
49400
- function useLockCapGuard(trpc, deviceId) {
49401
- const { cap } = useDeviceCapability({
49402
- trpc,
49403
- deviceId,
49404
- cap: (d) => d.lockControl,
49405
- slice: (d) => d.state.lockControl
49406
- });
49407
- return cap !== void 0;
49408
- }
49409
- function LockInlineControl({ trpc, deviceId, features, optimistic }) {
49410
- if (!useLockCapGuard(trpc, deviceId)) return null;
49411
- return /* @__PURE__ */ jsx(LockPanel, {
49412
- trpc,
49413
- deviceId,
49414
- features,
49415
- layout: "compact",
49416
- optimistic
49417
- });
49418
- }
49419
- function LightInlineControl({ trpc, deviceId, features, optimistic }) {
49420
- if (!useSwitchCapGuard(trpc, deviceId)) return null;
49421
- return /* @__PURE__ */ jsxs("div", {
49422
- className: "inline-flex items-center gap-2",
49423
- children: [/* @__PURE__ */ jsx(SwitchPanel, {
49424
- trpc,
49425
- deviceId,
49426
- features,
49427
- layout: "compact",
49428
- optimistic
49429
- }), /* @__PURE__ */ jsx(BrightnessPanel, {
49430
- trpc,
49431
- deviceId,
49432
- features,
49433
- layout: "compact",
49434
- optimistic
49435
- })]
49436
- });
49437
- }
49438
- function useFanCapGuard(trpc, deviceId) {
49439
- const { cap } = useDeviceCapability({
49440
- trpc,
49441
- deviceId,
49442
- cap: (d) => d.fanControl,
49443
- slice: (d) => d.state.fanControl
49444
- });
49445
- return cap !== void 0;
49446
- }
49447
- function FanInlineControl({ trpc, deviceId, features, optimistic }) {
49448
- if (!useFanCapGuard(trpc, deviceId)) return null;
49449
- return /* @__PURE__ */ jsxs("div", {
49450
- className: "inline-flex items-center gap-2",
49451
- children: [/* @__PURE__ */ jsx(SwitchPanel, {
49452
- trpc,
49453
- deviceId,
49454
- features,
49455
- layout: "compact",
49456
- optimistic
49457
- }), /* @__PURE__ */ jsx(FanPanel, {
49458
- trpc,
49459
- deviceId,
49460
- features,
49461
- layout: "compact",
49462
- optimistic
49463
- })]
49464
- });
49465
- }
49466
- function useClimateCapGuard(trpc, deviceId) {
49467
- const { cap } = useDeviceCapability({
49468
- trpc,
49469
- deviceId,
49470
- cap: (d) => d.climateControl,
49471
- slice: (d) => d.state.climateControl
49472
- });
49473
- return cap !== void 0;
49474
- }
49475
- function ThermostatInlineControl({ trpc, deviceId, features, optimistic }) {
49476
- if (!useClimateCapGuard(trpc, deviceId)) return null;
49477
- return /* @__PURE__ */ jsx(ClimatePanel, {
49478
- trpc,
49479
- deviceId,
49480
- features,
49481
- layout: "compact",
49482
- optimistic
49483
- });
49484
- }
49485
- function AcInlineControl({ trpc, deviceId, features, optimistic }) {
49486
- if (!useClimateCapGuard(trpc, deviceId)) return null;
49487
- return /* @__PURE__ */ jsx(AcPanel, {
49488
- trpc,
49489
- deviceId,
49490
- features,
49491
- layout: "compact",
49492
- optimistic
49493
- });
49494
- }
49495
- function AcHeroCard({ trpc, deviceId, features, optimistic }) {
49496
- if (!useClimateCapGuard(trpc, deviceId)) return null;
49497
- return /* @__PURE__ */ jsx(AcPanel, {
49498
- trpc,
49499
- deviceId,
49500
- features,
49501
- layout: "full",
49502
- optimistic
49503
- });
49504
- }
49505
- function useMediaPlayerCapGuard(trpc, deviceId) {
49506
- const { cap } = useDeviceCapability({
49507
- trpc,
49508
- deviceId,
49509
- cap: (d) => d.mediaPlayer,
49510
- slice: (d) => d.state.mediaPlayer
49511
- });
49512
- return cap !== void 0;
49513
- }
49514
- function MediaPlayerInlineControl({ trpc, deviceId, features, optimistic }) {
49515
- if (!useMediaPlayerCapGuard(trpc, deviceId)) return null;
49516
- return /* @__PURE__ */ jsx(MediaPlayerPanel, {
49517
- trpc,
49518
- deviceId,
49519
- features,
49520
- layout: "compact",
49521
- optimistic
49522
- });
49523
- }
49524
- function useControlCapGuard(trpc, deviceId) {
49525
- const { cap } = useDeviceCapability({
49526
- trpc,
49527
- deviceId,
49528
- cap: (d) => d.control,
49529
- slice: (d) => d.state.control
49530
- });
49531
- return cap !== void 0;
49532
- }
49533
- function ControlInlineControl({ trpc, deviceId, features, optimistic }) {
49534
- if (!useControlCapGuard(trpc, deviceId)) return null;
49535
- return /* @__PURE__ */ jsx(ControlPanel, {
49536
- trpc,
49537
- deviceId,
49538
- features,
49539
- layout: "compact",
49540
- optimistic
49541
- });
49542
- }
49543
- function ControlHeroCard({ trpc, deviceId, features, optimistic }) {
49544
- if (!useControlCapGuard(trpc, deviceId)) return null;
49545
- return /* @__PURE__ */ jsx(HeroShell, { children: /* @__PURE__ */ jsx(ControlPanel, {
49546
- trpc,
49547
- deviceId,
49548
- features,
49549
- layout: "full",
49550
- optimistic
49551
- }) });
49552
- }
49553
- //#endregion
49554
- //#region src/composites/device-controls/control-registry.tsx
49555
- /**
49556
- * Presentation binding: the shared `DeviceControlKind` (decided in
49557
- * `@camstack/types` `resolveDeviceControlKind`) → this platform's web
49558
- * components. Exhaustive over `DeviceControlKind` — omitting a kind fails the
49559
- * build here. The type→kind DECISION is single-sourced in `@camstack/types`;
49560
- * this map only binds a kind to its web widgets.
49561
- */
49562
- var CONTROL_KIND_COMPONENTS = {
49563
- cover: {
49564
- InlineControl: CoverInlineControl,
49565
- HeroCard: CoverHeroCard
49566
- },
49567
- valve: {
49568
- InlineControl: ValveInlineControl,
49569
- HeroCard: ValveHeroCard
49570
- },
49571
- humidifier: {
49572
- InlineControl: HumidifierInlineControl,
49573
- HeroCard: HumidifierHeroCard
49574
- },
49575
- "water-heater": {
49576
- InlineControl: WaterHeaterInlineControl,
49577
- HeroCard: WaterHeaterHeroCard
49578
- },
49579
- switch: {
49580
- InlineControl: SwitchInlineControl,
49581
- HeroCard: SwitchHeroCard
49582
- },
49583
- light: {
49584
- InlineControl: LightInlineControl,
49585
- HeroCard: LightHeroCard
49586
- },
49587
- fan: {
49588
- InlineControl: FanInlineControl,
49589
- HeroCard: FanHeroCard
49590
- },
49591
- sensor: {
49592
- InlineControl: SensorInlineControl,
49593
- HeroCard: SensorHeroCard
49594
- },
49595
- thermostat: {
49596
- InlineControl: ThermostatInlineControl,
49597
- HeroCard: ThermostatHeroCard
49598
- },
49599
- climate: {
49600
- InlineControl: AcInlineControl,
49601
- HeroCard: AcHeroCard
49602
- },
49603
- button: {
49604
- InlineControl: ButtonControl,
49605
- HeroCard: ButtonHeroCard
49606
- },
49607
- update: {
49608
- InlineControl: UpdateInlineControl,
49609
- HeroCard: UpdateHeroCard
49610
- },
49611
- lock: {
49612
- InlineControl: LockInlineControl,
49613
- HeroCard: LockHeroCard
49614
- },
49615
- "media-player": {
49616
- InlineControl: MediaPlayerInlineControl,
49617
- HeroCard: MediaPlayerHeroCard
49618
- },
49619
- alarm: {
49620
- InlineControl: AlarmInlineControl,
49621
- HeroCard: AlarmHeroCard
49622
- },
49623
- control: {
49624
- InlineControl: ControlInlineControl,
49625
- HeroCard: ControlHeroCard
49626
- },
49627
- weather: {
49628
- InlineControl: WeatherInlineControl,
49629
- HeroCard: WeatherHeroCard
49630
- },
49631
- vacuum: {
49632
- InlineControl: VacuumInlineControl,
49633
- HeroCard: VacuumHeroCard
49634
- },
49635
- "lawn-mower": {
49636
- InlineControl: LawnMowerInlineControl,
49637
- HeroCard: LawnMowerHeroCard
49638
- },
49639
- image: {
49640
- InlineControl: ImageInlineControl,
49641
- HeroCard: ImageHeroCard
49642
- },
49643
- "pet-feeder": {
49644
- InlineControl: PetFeederInlineControl,
49645
- HeroCard: PetFeederHeroCard
49646
- },
49647
- script: {
49648
- InlineControl: ScriptInlineControl,
49649
- HeroCard: ScriptHeroCard
49650
- },
49651
- automation: {
49652
- InlineControl: AutomationInlineControl,
49653
- HeroCard: AutomationHeroCard
49654
- },
49655
- dummy: {
49656
- InlineControl: DummyInline,
49657
- HeroCard: DummyHeroCard
49658
- }
49659
- };
49660
- /**
49661
- * Exhaustive record over DeviceType, DERIVED from the shared decision
49662
- * (`DEVICE_TYPE_CONTROL_KIND`) composed with the web `CONTROL_KIND_COMPONENTS`
49663
- * binding — so the type→control mapping can never diverge from the single
49664
- * source in `@camstack/types`. Preserved as a public export for consumers/tests
49665
- * that index it by `DeviceType`.
49666
- *
49667
- * The `Object.fromEntries` result is a `{ [k: string]: DeviceTypeControl }`;
49668
- * `Object.values(DeviceType)` (a string enum) yields exactly the enum's members,
49669
- * so the record is fully populated — the assertion narrows that documented
49670
- * enum-record projection boundary.
49671
- */
49672
- var DEVICE_TYPE_CONTROL = Object.fromEntries(Object.values(DeviceType).map((type) => [type, CONTROL_KIND_COMPONENTS[DEVICE_TYPE_CONTROL_KIND[type]]]));
49673
- /**
49674
- * Per-type FILL flag.
49675
- *
49676
- * true — range / choice / button controls (cover ▲■▼ triad, thermostat
49677
- * setpoint stepper, media/control/button action buttons, alarm
49678
- * segmented control). The child STRETCHES to the full column width.
49679
- * false — readouts, tap-toggles, icon controls (lock), and dummy/placeholder
49680
- * types. The child keeps its NATURAL width; where it sits in the
49681
- * column (flush right vs centered) is decided by the caller via a
49682
- * `ControlPlacement` — see `resolveControlAlign`.
49683
- *
49684
- * Dummy types (Camera, Hub, Generic, Weather, Vacuum, Container, and placeholder
49685
- * types Notifier/Script/Automation whose InlineControl renders "—") are `false` —
49686
- * the dash placeholder must not be stretched to the full column width. (AlarmPanel
49687
- * has a real segmented inline control and is `true`.) When a later task gives a
49688
- * remaining placeholder type a real stretchy inline control, flip its flag to true.
49689
- *
49690
- * Exhaustive over DeviceType — a missing member is a TypeScript build error.
49691
- */
49692
- var CONTROL_FILLS = {
49693
- [DeviceType.Cover]: true,
49694
- [DeviceType.Valve]: true,
49695
- [DeviceType.Thermostat]: true,
49696
- [DeviceType.Climate]: true,
49697
- [DeviceType.Control]: true,
49698
- [DeviceType.Button]: true,
49699
- [DeviceType.MediaPlayer]: true,
49700
- [DeviceType.AlarmPanel]: true,
49701
- [DeviceType.Lock]: false,
49702
- [DeviceType.Vacuum]: false,
49703
- [DeviceType.LawnMower]: false,
49704
- [DeviceType.Humidifier]: false,
49705
- [DeviceType.Switch]: false,
49706
- [DeviceType.Siren]: false,
49707
- [DeviceType.Light]: false,
49708
- [DeviceType.Fan]: false,
49709
- [DeviceType.WaterHeater]: false,
49710
- [DeviceType.Sensor]: false,
49711
- [DeviceType.Presence]: false,
49712
- [DeviceType.EventEmitter]: false,
49713
- [DeviceType.Notifier]: false,
49714
- [DeviceType.Script]: false,
49715
- [DeviceType.Automation]: false,
49716
- [DeviceType.Camera]: false,
49717
- [DeviceType.Hub]: false,
49718
- [DeviceType.Update]: false,
49719
- [DeviceType.Generic]: false,
49720
- [DeviceType.Weather]: false,
49721
- [DeviceType.Container]: false,
49722
- [DeviceType.Image]: false,
49723
- [DeviceType.PetFeeder]: false
49724
- };
49725
- var DEVICE_TYPE_VALUES$1 = new Set(Object.values(DeviceType));
49726
- /**
49727
- * Runtime-string-safe lookup: a device row's `type` crosses the wire as a plain
49728
- * string. The type→control-kind DECISION is single-sourced in `@camstack/types`
49729
- * (`resolveDeviceControlKind`, null for an unrecognised type); this maps the
49730
- * resolved kind onto the web components.
49731
- */
49732
- function resolveDeviceControl(type) {
49733
- const kind = resolveDeviceControlKind(type);
49734
- return kind ? CONTROL_KIND_COMPONENTS[kind] : null;
49735
- }
49736
- /**
49737
- * Runtime-string-safe alignment lookup for the fixed-width control column.
49738
- *
49739
- * A device's `type` crosses the wire as a plain string;
49740
- * `DEVICE_TYPE_VALUES.has(type)` narrows it to `DeviceType` before indexing
49741
- * `CONTROL_FILLS`, making the cast sound. The `'table'` placement is
49742
- * content-width for EVERY type (returns `FIT`), so a wide control never
49743
- * overflows a fixed box — the FILL flag is irrelevant there. For the fixed-box
49744
- * placements a FILL type always stretches (`FILL`) and a natural-width type is
49745
- * placed by the caller's `placement` — `'center'` → `CENTER`, `'right'` →
49746
- * `RIGHT` (card / minimal view). An unrecognised type falls back to
49747
- * natural-width placement.
49748
- */
49749
- function resolveControlAlign(type, placement) {
49750
- if (placement === "table") return "fit";
49751
- return DEVICE_TYPE_VALUES$1.has(type) && CONTROL_FILLS[type] ? FILL : placement === "center" ? CENTER : RIGHT;
49752
- }
49753
- //#endregion
49754
- //#region src/lib/format-last-seen.ts
49755
- /**
49756
- * Relative "last seen" formatter for the offline affordance.
49757
- *
49758
- * Renders a short relative-time label from a millisecond epoch:
49759
- * "just now", "5m ago", "3h ago", "2d ago". Returns `null` for missing
49760
- * inputs, or inputs below {@link MIN_PLAUSIBLE_TIMESTAMP_MS} (a
49761
- * placeholder or a seeded "known value, unknown change time" stamp), so
49762
- * callers can render nothing rather than a misleading "—", "NaN ago", or
49763
- * (for a seeded `1`) "20000 days ago".
49764
- *
49765
- * Future timestamps (clock skew between the device and the browser)
49766
- * collapse to "just now" instead of a negative duration.
49767
- */
49768
- var SECOND_MS = 1e3;
49769
- var MINUTE_MS = 60 * SECOND_MS;
49770
- var HOUR_MS = 60 * MINUTE_MS;
49771
- var DAY_MS = 24 * HOUR_MS;
49772
- var JUST_NOW_MS = 30 * SECOND_MS;
49773
- /**
49774
- * The earliest timestamp a real "last seen" could plausibly carry. Below
49775
- * this, a value is a placeholder or a seeded "known value, unknown change
49776
- * time" stamp (e.g. Homematic's `HM_SEEDED_CHANGED_AT = 1`) — never a real
49777
- * instant. Year 2000 predates every device this renders.
49778
- */
49779
- var MIN_PLAUSIBLE_TIMESTAMP_MS = Date.UTC(2e3, 0, 1);
49780
- function formatLastSeen(timestampMs) {
49781
- if (timestampMs === null || timestampMs === void 0 || timestampMs < MIN_PLAUSIBLE_TIMESTAMP_MS) return null;
49782
- const delta = Date.now() - timestampMs;
49783
- if (delta < JUST_NOW_MS) return "just now";
49784
- if (delta < HOUR_MS) return `${String(Math.floor(delta / MINUTE_MS))}m ago`;
49785
- if (delta < DAY_MS) return `${String(Math.floor(delta / HOUR_MS))}h ago`;
49786
- return `${String(Math.floor(delta / DAY_MS))}d ago`;
49787
- }
49788
- //#endregion
49789
- //#region src/composites/device-controls/offline-badge.tsx
49790
- function OfflineBadge({ lastChangedAt, className }) {
49791
- const lastSeen = formatLastSeen(lastChangedAt);
49792
- return /* @__PURE__ */ jsxs("span", {
49793
- title: lastSeen === null ? "Unavailable" : `Unavailable · last seen ${lastSeen}`,
49794
- className: cn("inline-flex items-center gap-1 rounded-full px-2 py-0.5", "bg-surface-hover text-foreground-subtle text-[10px] font-medium", "border border-border", className),
49795
- children: [
49796
- /* @__PURE__ */ jsx(WifiOff, {
49797
- className: "h-3 w-3 flex-shrink-0",
49798
- "aria-hidden": "true"
49799
- }),
49800
- /* @__PURE__ */ jsx("span", { children: "Unavailable" }),
49801
- lastSeen !== null ? /* @__PURE__ */ jsx("span", {
49802
- className: "text-foreground-subtle/70",
49803
- children: `· last seen ${lastSeen}`
49804
- }) : null
49805
- ]
49806
- });
49807
- }
49808
- //#endregion
49809
- //#region src/composites/device-controls/primary-child.ts
49810
- /**
49811
- * Which child a container is rendered AS — this surface's name for the shared
49812
- * rule.
49813
- *
49814
- * The list and the resolution used to live here, in a COPY that carried a
49815
- * comment asking the Home Assistant adoption mapping and the viewer's panel to
49816
- * stay in sync with it. Three lists and three comments is not one rule, it is
49817
- * three chances to drift. Both now come from `@camstack/types`, which all of
49818
- * them already depend on; what remains here is the adapter for this module's
49819
- * historical `ChildRef` shape.
49820
- */
49821
- /** The shared precedence, under this module's historical name. */
49822
- var PRIORITY = CONTAINER_CHILD_PRIORITY;
49823
- /**
49824
- * Resolve a container's primary child: the operator's pick when it still
49825
- * matches a live child, else the highest-priority type.
49826
- *
49827
- * `entityId` here is `sourceInfo?.id ?? stableId` (see `containerChildToRef`).
49828
- * The shared resolver matches its canonical `stableId` key first and accepts
49829
- * that legacy value too, so a pick stored under either keeps resolving.
49830
- */
49831
- function resolvePrimaryChild(children, overrideEntityId, containerName) {
49832
- const picked = resolveContainerPrimaryChild(children.map((c) => ({
49833
- id: c.id,
49834
- stableId: c.entityId,
49835
- type: c.type,
49836
- ...c.role !== void 0 ? { role: c.role } : {},
49837
- ...c.name !== void 0 ? { name: c.name } : {}
49838
- })), overrideEntityId, containerName);
49839
- return picked === null ? null : children.find((c) => c.id === picked.id) ?? null;
49840
- }
49841
- //#endregion
49842
- //#region src/composites/device-controls/container-primary.ts
49843
- /**
49844
- * Re-sync-stable identity of a child. Prefers the upstream-system dispatch key
49845
- * (`sourceInfo.id` — e.g. HA `entity_id`); falls back to `stableId` for
49846
- * providers that never populated SourceInfo. Both survive a re-sync that may
49847
- * reallocate the numeric `id`.
49848
- */
49849
- function childEntityId(child) {
49850
- return child.sourceInfo?.id ?? child.stableId;
49851
- }
49852
- /** Project a raw child row onto the pure resolver's {@link ChildRef} shape. */
49853
- function containerChildToRef(child) {
49854
- return {
49855
- id: child.id,
49856
- entityId: childEntityId(child),
49857
- type: child.type,
49858
- ...child.role !== void 0 && child.role !== null ? { role: child.role } : {},
49859
- ...child.name !== void 0 ? { name: child.name } : {}
49860
- };
49861
- }
49862
- /**
49863
- * Resolve the entityId override from the container's persisted `linkDeviceId`.
49864
- * Returns the linked child's stable entityId, or `undefined` when there is no
49865
- * link or the link is stale (points to a numeric id no longer present) — in
49866
- * which case the resolver falls back to the priority default.
49867
- */
49868
- function overrideEntityIdFromLink(children, linkDeviceId) {
49869
- if (linkDeviceId === null || linkDeviceId === void 0) return void 0;
49870
- const linked = children.find((c) => c.id === linkDeviceId);
49871
- return linked === void 0 ? void 0 : childEntityId(linked);
49872
- }
49873
- /**
49874
- * Full container → primary-child resolution: project children to refs, resolve
49875
- * the override entityId, and delegate to the pure resolver. Returns `null` for a
49876
- * container with no children (caller degrades gracefully).
49877
- *
49878
- * Override precedence:
49879
- * 1. `primaryChildEntityId` — the DURABLE override (re-sync/rename-stable),
49880
- * passed straight to the resolver which matches on `entityId`. Wins when a
49881
- * non-empty string is supplied.
49882
- * 2. `linkDeviceId` — LEGACY numeric override, re-derived to an entityId from
49883
- * the live child set. Consulted only when there is no durable override.
49884
- *
49885
- * When neither resolves to a live child, the resolver falls back to the
49886
- * priority default.
49887
- */
49888
- function resolveContainerPrimary(children, linkDeviceId, primaryChildEntityId) {
49889
- return resolvePrimaryChild(children.map(containerChildToRef), primaryChildEntityId !== null && primaryChildEntityId !== void 0 && primaryChildEntityId !== "" ? primaryChildEntityId : overrideEntityIdFromLink(children, linkDeviceId));
49890
- }
49891
- //#endregion
49892
- //#region src/composites/device-controls/container-primary-hero.tsx
49893
- function ContainerPrimaryHero({ trpc, children, linkDeviceId, primaryChildEntityId }) {
49894
- const primary = resolveContainerPrimary(children, linkDeviceId, primaryChildEntityId);
49895
- const primaryChild = primary === null ? void 0 : children.find((c) => c.id === primary.id);
49896
- if (primaryChild === void 0) return /* @__PURE__ */ jsx("div", {
49897
- className: "flex h-full w-full items-center justify-center p-4 text-[11px] italic text-foreground-subtle",
49898
- children: "No child entities to display."
50195
+ function useLockCapGuard(trpc, deviceId) {
50196
+ const { cap } = useDeviceCapability({
50197
+ trpc,
50198
+ deviceId,
50199
+ cap: (d) => d.lockControl,
50200
+ slice: (d) => d.state.lockControl
49899
50201
  });
49900
- return /* @__PURE__ */ jsx("div", {
49901
- className: "flex h-full w-full flex-col items-stretch gap-3 p-3 sm:p-4",
49902
- children: /* @__PURE__ */ jsx(resolveDeviceControl(primaryChild.type)?.HeroCard ?? DummyHeroCard, {
50202
+ return cap !== void 0;
50203
+ }
50204
+ function LockInlineControl({ trpc, deviceId, features, optimistic }) {
50205
+ if (!useLockCapGuard(trpc, deviceId)) return null;
50206
+ return /* @__PURE__ */ jsx(LockPanel, {
50207
+ trpc,
50208
+ deviceId,
50209
+ features,
50210
+ layout: "compact",
50211
+ optimistic
50212
+ });
50213
+ }
50214
+ function LightInlineControl({ trpc, deviceId, features, optimistic }) {
50215
+ if (!useSwitchCapGuard(trpc, deviceId)) return null;
50216
+ return /* @__PURE__ */ jsxs("div", {
50217
+ className: "inline-flex items-center gap-2",
50218
+ children: [/* @__PURE__ */ jsx(SwitchPanel, {
49903
50219
  trpc,
49904
- deviceId: primaryChild.id,
49905
- features: primaryChild.features
49906
- })
50220
+ deviceId,
50221
+ features,
50222
+ layout: "compact",
50223
+ optimistic
50224
+ }), /* @__PURE__ */ jsx(BrightnessPanel, {
50225
+ trpc,
50226
+ deviceId,
50227
+ features,
50228
+ layout: "compact",
50229
+ optimistic
50230
+ })]
49907
50231
  });
49908
50232
  }
49909
- //#endregion
49910
- //#region src/composites/device-controls/primary-child-picker.tsx
49911
- var BASE_SELECT_CLASS = "rounded-md border border-border bg-surface px-1.5 py-0.5 text-[11px] text-foreground";
49912
- function PrimaryChildPicker({ childrenList, selectedId, onSetPrimary, disabled, className }) {
49913
- return /* @__PURE__ */ jsx("select", {
49914
- value: selectedId,
49915
- disabled,
49916
- onChange: (e) => {
49917
- const nextId = Number(e.target.value);
49918
- if (!Number.isFinite(nextId) || nextId === selectedId) return;
49919
- const picked = childrenList.find((c) => c.id === nextId);
49920
- if (picked !== void 0) onSetPrimary(picked.stableId);
49921
- },
49922
- className: className === void 0 ? BASE_SELECT_CLASS : `${BASE_SELECT_CLASS} ${className}`,
49923
- children: childrenList.map((c) => /* @__PURE__ */ jsxs("option", {
49924
- value: c.id,
49925
- children: [
49926
- c.name,
49927
- " (",
49928
- c.type,
49929
- ")"
49930
- ]
49931
- }, c.id))
50233
+ function useFanCapGuard(trpc, deviceId) {
50234
+ const { cap } = useDeviceCapability({
50235
+ trpc,
50236
+ deviceId,
50237
+ cap: (d) => d.fanControl,
50238
+ slice: (d) => d.state.fanControl
49932
50239
  });
50240
+ return cap !== void 0;
49933
50241
  }
49934
- //#endregion
49935
- //#region src/composites/device-list/batch-toolbar.tsx
49936
- function DeviceBatchToolbar({ selectedIds, onClear, remove, disable, enable }) {
49937
- const [pendingDelete, setPendingDelete] = useState(false);
49938
- if (selectedIds.size === 0) return null;
49939
- const ids = [...selectedIds];
49940
- const count = ids.length;
49941
- const handleDisable = () => {
49942
- for (const id of ids) disable({ deviceId: id });
49943
- onClear();
49944
- };
49945
- const handleEnable = () => {
49946
- for (const id of ids) enable({ deviceId: id });
49947
- onClear();
49948
- };
49949
- const handleDeleteFirst = () => {
49950
- setPendingDelete(true);
49951
- };
49952
- const handleDeleteConfirm = () => {
49953
- for (const id of ids) remove({ deviceId: id });
49954
- setPendingDelete(false);
49955
- onClear();
49956
- };
49957
- const handleDeleteCancel = () => {
49958
- setPendingDelete(false);
49959
- };
49960
- const handleClear = () => {
49961
- setPendingDelete(false);
49962
- onClear();
49963
- };
50242
+ function FanInlineControl({ trpc, deviceId, features, optimistic }) {
50243
+ if (!useFanCapGuard(trpc, deviceId)) return null;
49964
50244
  return /* @__PURE__ */ jsxs("div", {
49965
- className: "flex items-center gap-2 px-3 py-2 rounded-lg border border-border bg-surface-hover",
49966
- children: [
49967
- /* @__PURE__ */ jsxs("span", {
49968
- className: "text-xs font-medium text-foreground-subtle shrink-0",
49969
- children: [count, " selected"]
49970
- }),
49971
- /* @__PURE__ */ jsx("div", {
49972
- className: "h-3.5 w-px bg-border shrink-0",
49973
- "aria-hidden": "true"
49974
- }),
49975
- /* @__PURE__ */ jsxs("div", {
49976
- className: "flex items-center gap-1.5 flex-wrap",
49977
- children: [
49978
- /* @__PURE__ */ jsx("button", {
49979
- type: "button",
49980
- onClick: handleDisable,
49981
- className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
49982
- children: "Disable"
49983
- }),
49984
- /* @__PURE__ */ jsx("button", {
49985
- type: "button",
49986
- onClick: handleEnable,
49987
- className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
49988
- children: "Enable"
49989
- }),
49990
- pendingDelete ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("button", {
49991
- type: "button",
49992
- onClick: handleDeleteConfirm,
49993
- className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-red-500/50 bg-red-500/10 text-red-400 hover:bg-red-500/20"),
49994
- children: [
49995
- "Confirm delete ",
49996
- count,
49997
- "?"
49998
- ]
49999
- }), /* @__PURE__ */ jsx("button", {
50000
- type: "button",
50001
- onClick: handleDeleteCancel,
50002
- className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
50003
- children: "Cancel"
50004
- })] }) : /* @__PURE__ */ jsx("button", {
50005
- type: "button",
50006
- onClick: handleDeleteFirst,
50007
- className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-red-500/40 bg-red-500/10 text-red-400 hover:bg-red-500/20"),
50008
- children: "Delete"
50009
- }),
50010
- /* @__PURE__ */ jsx("button", {
50011
- type: "button",
50012
- onClick: handleClear,
50013
- className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
50014
- children: "Clear"
50015
- })
50016
- ]
50017
- })
50018
- ]
50245
+ className: "inline-flex items-center gap-2",
50246
+ children: [/* @__PURE__ */ jsx(SwitchPanel, {
50247
+ trpc,
50248
+ deviceId,
50249
+ features,
50250
+ layout: "compact",
50251
+ optimistic
50252
+ }), /* @__PURE__ */ jsx(FanPanel, {
50253
+ trpc,
50254
+ deviceId,
50255
+ features,
50256
+ layout: "compact",
50257
+ optimistic
50258
+ })]
50019
50259
  });
50020
50260
  }
50021
- //#endregion
50022
- //#region src/composites/device-list/batch-actions-bar.tsx
50023
- /**
50024
- * <DeviceBatchActionsBar> — responsive wrapper around <DeviceBatchToolbar>.
50025
- *
50026
- * Surfaces the available bulk actions once devices are selected:
50027
- * - L2+ (`sm` and up): the toolbar renders inline as a row of buttons.
50028
- * - L1 (`< sm`): a compact "Actions · N" trigger opens a popover that
50029
- * hosts the SAME <DeviceBatchToolbar>, so the action logic (inline
50030
- * delete-confirm, clear) is reused verbatim — never forked.
50031
- *
50032
- * Renders nothing while the selection is empty (delegated to the toolbar).
50033
- */
50034
- function DeviceBatchActionsBar(props) {
50035
- const [open, setOpen] = useState(false);
50036
- const rootRef = useRef(null);
50037
- const count = props.selectedIds.size;
50038
- useEffect(() => {
50039
- if (!open) return;
50040
- const onOutside = (e) => {
50041
- const target = e.target instanceof Node ? e.target : null;
50042
- if (target && rootRef.current && !rootRef.current.contains(target)) setOpen(false);
50043
- };
50044
- const onKey = (e) => {
50045
- if (e.key === "Escape") setOpen(false);
50046
- };
50047
- document.addEventListener("mousedown", onOutside);
50048
- document.addEventListener("touchstart", onOutside);
50049
- document.addEventListener("keydown", onKey);
50050
- return () => {
50051
- document.removeEventListener("mousedown", onOutside);
50052
- document.removeEventListener("touchstart", onOutside);
50053
- document.removeEventListener("keydown", onKey);
50054
- };
50055
- }, [open]);
50056
- if (count === 0) return null;
50057
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
50058
- className: "hidden sm:block",
50059
- children: /* @__PURE__ */ jsx(DeviceBatchToolbar, { ...props })
50060
- }), /* @__PURE__ */ jsxs("div", {
50061
- ref: rootRef,
50062
- "data-testid": "batch-actions-compact",
50063
- className: "relative sm:hidden",
50064
- children: [/* @__PURE__ */ jsxs("button", {
50065
- type: "button",
50066
- onClick: () => setOpen((v) => !v),
50067
- "aria-haspopup": "menu",
50068
- "aria-expanded": open,
50069
- className: "inline-flex items-center gap-1.5 h-7 px-2.5 rounded-full border border-primary/40 bg-primary/15 text-[10.5px] font-medium text-primary",
50070
- children: [/* @__PURE__ */ jsxs("span", { children: ["Actions · ", String(count)] }), /* @__PURE__ */ jsx(ChevronDown, { className: `h-3 w-3 transition-transform ${open ? "rotate-180" : ""}` })]
50071
- }), open && /* @__PURE__ */ jsx("div", {
50072
- role: "menu",
50073
- className: "absolute left-0 top-full mt-1 z-30 rounded-lg border border-border bg-surface shadow-md",
50074
- children: /* @__PURE__ */ jsx(DeviceBatchToolbar, { ...props })
50075
- })]
50076
- })] });
50261
+ function useClimateCapGuard(trpc, deviceId) {
50262
+ const { cap } = useDeviceCapability({
50263
+ trpc,
50264
+ deviceId,
50265
+ cap: (d) => d.climateControl,
50266
+ slice: (d) => d.state.climateControl
50267
+ });
50268
+ return cap !== void 0;
50077
50269
  }
50078
- //#endregion
50079
- //#region src/composites/device-meta.ts
50080
- var DEVICE_TYPE_META = {
50081
- [DeviceType.Camera]: {
50082
- icon: Camera,
50083
- label: "Camera"
50084
- },
50085
- [DeviceType.Hub]: {
50086
- icon: Network,
50087
- label: "Hub"
50088
- },
50089
- [DeviceType.Light]: {
50090
- icon: Lightbulb,
50091
- label: "Light"
50092
- },
50093
- [DeviceType.Siren]: {
50094
- icon: Siren,
50095
- label: "Siren"
50096
- },
50097
- [DeviceType.Switch]: {
50098
- icon: ToggleLeft,
50099
- label: "Switch"
50100
- },
50101
- [DeviceType.Sensor]: {
50102
- icon: Activity,
50103
- label: "Sensor"
50104
- },
50105
- [DeviceType.Thermostat]: {
50106
- icon: Thermometer,
50107
- label: "Thermostat"
50108
- },
50109
- [DeviceType.Climate]: {
50110
- icon: AirVent,
50111
- label: "Air conditioner"
50112
- },
50113
- [DeviceType.Button]: {
50114
- icon: CircleDot,
50115
- label: "Button"
50116
- },
50117
- [DeviceType.EventEmitter]: {
50118
- icon: Zap,
50119
- label: "Event Emitter"
50120
- },
50121
- [DeviceType.Update]: {
50122
- icon: RefreshCw,
50123
- label: "Update"
50124
- },
50125
- [DeviceType.Generic]: {
50126
- icon: Box,
50127
- label: "Generic"
50128
- },
50129
- [DeviceType.Notifier]: {
50130
- icon: Bell,
50131
- label: "Notifier"
50132
- },
50133
- [DeviceType.Script]: {
50134
- icon: ScrollText,
50135
- label: "Script"
50136
- },
50137
- [DeviceType.Automation]: {
50138
- icon: Workflow,
50139
- label: "Automation"
50140
- },
50141
- [DeviceType.Lock]: {
50142
- icon: Lock,
50143
- label: "Lock"
50144
- },
50145
- [DeviceType.Cover]: {
50146
- icon: Blinds,
50147
- label: "Cover"
50148
- },
50149
- [DeviceType.Valve]: {
50150
- icon: Pipette,
50151
- label: "Valve"
50152
- },
50153
- [DeviceType.Humidifier]: {
50154
- icon: Waves,
50155
- label: "Humidifier"
50156
- },
50157
- [DeviceType.WaterHeater]: {
50158
- icon: Bath,
50159
- label: "Water heater"
50160
- },
50161
- [DeviceType.Fan]: {
50162
- icon: Fan,
50163
- label: "Fan"
50164
- },
50165
- [DeviceType.MediaPlayer]: {
50166
- icon: MonitorPlay,
50167
- label: "Media player"
50168
- },
50169
- [DeviceType.AlarmPanel]: {
50170
- icon: ShieldAlert,
50171
- label: "Alarm panel"
50172
- },
50173
- [DeviceType.Control]: {
50174
- icon: SlidersHorizontal,
50175
- label: "Control"
50176
- },
50177
- [DeviceType.Presence]: {
50178
- icon: MapPin,
50179
- label: "Presence"
50180
- },
50181
- [DeviceType.Weather]: {
50182
- icon: CloudSun,
50183
- label: "Weather"
50184
- },
50185
- [DeviceType.Vacuum]: {
50186
- icon: Bot,
50187
- label: "Vacuum"
50188
- },
50189
- [DeviceType.LawnMower]: {
50190
- icon: Tractor,
50191
- label: "Lawn mower"
50192
- },
50193
- [DeviceType.Container]: {
50194
- icon: Box,
50195
- label: "Container"
50196
- },
50197
- [DeviceType.Image]: {
50198
- icon: Image,
50199
- label: "Image"
50200
- },
50201
- [DeviceType.PetFeeder]: {
50202
- icon: Bone,
50203
- label: "Pet feeder"
50204
- }
50205
- };
50206
- var DEVICE_ROLE_META = {
50207
- [DeviceRole.Siren]: {
50208
- icon: Siren,
50209
- label: "Siren"
50210
- },
50211
- [DeviceRole.Floodlight]: {
50212
- icon: Lightbulb,
50213
- label: "Floodlight"
50214
- },
50215
- [DeviceRole.Spotlight]: {
50216
- icon: Flashlight,
50217
- label: "Spotlight"
50218
- },
50219
- [DeviceRole.PirSensor]: {
50220
- icon: Radar,
50221
- label: "PIR sensor"
50222
- },
50223
- [DeviceRole.Chime]: {
50224
- icon: Bell,
50225
- label: "Chime"
50226
- },
50227
- [DeviceRole.Autotrack]: {
50228
- icon: Crosshair,
50229
- label: "Auto-track"
50230
- },
50231
- [DeviceRole.Nightvision]: {
50232
- icon: Moon,
50233
- label: "Night vision"
50234
- },
50235
- [DeviceRole.PrivacyMask]: {
50236
- icon: EyeOff,
50237
- label: "Privacy mask"
50238
- },
50239
- [DeviceRole.Doorbell]: {
50240
- icon: DoorClosed,
50241
- label: "Doorbell"
50242
- },
50243
- [DeviceRole.BinaryHelper]: {
50244
- icon: ToggleLeft,
50245
- label: "Binary helper"
50246
- },
50247
- [DeviceRole.MotionSensor]: {
50248
- icon: Footprints,
50249
- label: "Motion sensor"
50250
- },
50251
- [DeviceRole.ContactSensor]: {
50252
- icon: DoorOpen,
50253
- label: "Contact sensor"
50254
- },
50255
- [DeviceRole.LeakSensor]: {
50256
- icon: Droplet,
50257
- label: "Leak sensor"
50258
- },
50259
- [DeviceRole.SmokeSensor]: {
50260
- icon: AlarmSmoke,
50261
- label: "Smoke sensor"
50262
- },
50263
- [DeviceRole.COSensor]: {
50264
- icon: Wind,
50265
- label: "CO sensor"
50266
- },
50267
- [DeviceRole.GasSensor]: {
50268
- icon: Flame,
50269
- label: "Gas sensor"
50270
- },
50271
- [DeviceRole.TamperSensor]: {
50272
- icon: ShieldAlert,
50273
- label: "Tamper sensor"
50274
- },
50275
- [DeviceRole.VibrationSensor]: {
50276
- icon: Vibrate,
50277
- label: "Vibration sensor"
50278
- },
50279
- [DeviceRole.ConnectivitySensor]: {
50280
- icon: Wifi,
50281
- label: "Connectivity"
50282
- },
50283
- [DeviceRole.SoundSensor]: {
50284
- icon: Volume2,
50285
- label: "Sound sensor"
50286
- },
50287
- [DeviceRole.BinarySensor]: {
50288
- icon: ToggleLeft,
50289
- label: "Binary sensor"
50290
- },
50291
- [DeviceRole.TemperatureSensor]: {
50292
- icon: Thermometer,
50293
- label: "Temperature"
50294
- },
50295
- [DeviceRole.HumiditySensor]: {
50296
- icon: Droplets,
50297
- label: "Humidity"
50298
- },
50299
- [DeviceRole.AmbientLightSensor]: {
50300
- icon: Sun,
50301
- label: "Ambient light"
50302
- },
50303
- [DeviceRole.PressureSensor]: {
50304
- icon: Gauge,
50305
- label: "Pressure"
50270
+ function ThermostatInlineControl({ trpc, deviceId, features, optimistic }) {
50271
+ if (!useClimateCapGuard(trpc, deviceId)) return null;
50272
+ return /* @__PURE__ */ jsx(ClimatePanel, {
50273
+ trpc,
50274
+ deviceId,
50275
+ features,
50276
+ layout: "compact",
50277
+ optimistic
50278
+ });
50279
+ }
50280
+ function AcInlineControl({ trpc, deviceId, features, optimistic }) {
50281
+ if (!useClimateCapGuard(trpc, deviceId)) return null;
50282
+ return /* @__PURE__ */ jsx(AcPanel, {
50283
+ trpc,
50284
+ deviceId,
50285
+ features,
50286
+ layout: "compact",
50287
+ optimistic
50288
+ });
50289
+ }
50290
+ function AcHeroCard({ trpc, deviceId, features, optimistic }) {
50291
+ if (!useClimateCapGuard(trpc, deviceId)) return null;
50292
+ return /* @__PURE__ */ jsx(AcPanel, {
50293
+ trpc,
50294
+ deviceId,
50295
+ features,
50296
+ layout: "full",
50297
+ optimistic
50298
+ });
50299
+ }
50300
+ function useMediaPlayerCapGuard(trpc, deviceId) {
50301
+ const { cap } = useDeviceCapability({
50302
+ trpc,
50303
+ deviceId,
50304
+ cap: (d) => d.mediaPlayer,
50305
+ slice: (d) => d.state.mediaPlayer
50306
+ });
50307
+ return cap !== void 0;
50308
+ }
50309
+ function MediaPlayerInlineControl({ trpc, deviceId, features, optimistic }) {
50310
+ if (!useMediaPlayerCapGuard(trpc, deviceId)) return null;
50311
+ return /* @__PURE__ */ jsx(MediaPlayerPanel, {
50312
+ trpc,
50313
+ deviceId,
50314
+ features,
50315
+ layout: "compact",
50316
+ optimistic
50317
+ });
50318
+ }
50319
+ function useControlCapGuard(trpc, deviceId) {
50320
+ const { cap } = useDeviceCapability({
50321
+ trpc,
50322
+ deviceId,
50323
+ cap: (d) => d.control,
50324
+ slice: (d) => d.state.control
50325
+ });
50326
+ return cap !== void 0;
50327
+ }
50328
+ function ControlInlineControl({ trpc, deviceId, features, optimistic }) {
50329
+ if (!useControlCapGuard(trpc, deviceId)) return null;
50330
+ return /* @__PURE__ */ jsx(ControlPanel, {
50331
+ trpc,
50332
+ deviceId,
50333
+ features,
50334
+ layout: "compact",
50335
+ optimistic
50336
+ });
50337
+ }
50338
+ function ControlHeroCard({ trpc, deviceId, features, optimistic }) {
50339
+ if (!useControlCapGuard(trpc, deviceId)) return null;
50340
+ return /* @__PURE__ */ jsx(HeroShell, { children: /* @__PURE__ */ jsx(ControlPanel, {
50341
+ trpc,
50342
+ deviceId,
50343
+ features,
50344
+ layout: "full",
50345
+ optimistic
50346
+ }) });
50347
+ }
50348
+ //#endregion
50349
+ //#region src/composites/device-controls/control-registry.tsx
50350
+ /**
50351
+ * Presentation binding: the shared `DeviceControlKind` (decided in
50352
+ * `@camstack/types` `resolveDeviceControlKind`) → this platform's web
50353
+ * components. Exhaustive over `DeviceControlKind` — omitting a kind fails the
50354
+ * build here. The type→kind DECISION is single-sourced in `@camstack/types`;
50355
+ * this map only binds a kind to its web widgets.
50356
+ */
50357
+ var CONTROL_KIND_COMPONENTS = {
50358
+ cover: {
50359
+ InlineControl: CoverInlineControl,
50360
+ HeroCard: CoverHeroCard
50306
50361
  },
50307
- [DeviceRole.WindSensor]: {
50308
- icon: Wind,
50309
- label: "Wind"
50362
+ valve: {
50363
+ InlineControl: ValveInlineControl,
50364
+ HeroCard: ValveHeroCard
50310
50365
  },
50311
- [DeviceRole.RainSensor]: {
50312
- icon: CloudRain,
50313
- label: "Rain"
50366
+ humidifier: {
50367
+ InlineControl: HumidifierInlineControl,
50368
+ HeroCard: HumidifierHeroCard
50314
50369
  },
50315
- [DeviceRole.UvSensor]: {
50316
- icon: SunMedium,
50317
- label: "UV index"
50370
+ "water-heater": {
50371
+ InlineControl: WaterHeaterInlineControl,
50372
+ HeroCard: WaterHeaterHeroCard
50318
50373
  },
50319
- [DeviceRole.SolarRadiationSensor]: {
50320
- icon: Sunrise,
50321
- label: "Solar radiation"
50374
+ switch: {
50375
+ InlineControl: SwitchInlineControl,
50376
+ HeroCard: SwitchHeroCard
50322
50377
  },
50323
- [DeviceRole.SoilMoistureSensor]: {
50324
- icon: Sprout,
50325
- label: "Soil moisture"
50378
+ light: {
50379
+ InlineControl: LightInlineControl,
50380
+ HeroCard: LightHeroCard
50326
50381
  },
50327
- [DeviceRole.PowerSensor]: {
50328
- icon: Zap,
50329
- label: "Power"
50382
+ fan: {
50383
+ InlineControl: FanInlineControl,
50384
+ HeroCard: FanHeroCard
50330
50385
  },
50331
- [DeviceRole.EnergySensor]: {
50332
- icon: Zap,
50333
- label: "Energy"
50386
+ sensor: {
50387
+ InlineControl: SensorInlineControl,
50388
+ HeroCard: SensorHeroCard
50334
50389
  },
50335
- [DeviceRole.VoltageSensor]: {
50336
- icon: Zap,
50337
- label: "Voltage"
50390
+ thermostat: {
50391
+ InlineControl: ThermostatInlineControl,
50392
+ HeroCard: ThermostatHeroCard
50338
50393
  },
50339
- [DeviceRole.CurrentSensor]: {
50340
- icon: Zap,
50341
- label: "Current"
50394
+ climate: {
50395
+ InlineControl: AcInlineControl,
50396
+ HeroCard: AcHeroCard
50342
50397
  },
50343
- [DeviceRole.AirQualitySensor]: {
50344
- icon: Wind,
50345
- label: "Air quality"
50398
+ button: {
50399
+ InlineControl: ButtonControl,
50400
+ HeroCard: ButtonHeroCard
50346
50401
  },
50347
- [DeviceRole.BatterySensor]: {
50348
- icon: Gauge,
50349
- label: "Battery"
50402
+ update: {
50403
+ InlineControl: UpdateInlineControl,
50404
+ HeroCard: UpdateHeroCard
50350
50405
  },
50351
- [DeviceRole.NumericSensor]: {
50352
- icon: Hash,
50353
- label: "Numeric sensor"
50406
+ lock: {
50407
+ InlineControl: LockInlineControl,
50408
+ HeroCard: LockHeroCard
50354
50409
  },
50355
- [DeviceRole.EnumSensor]: {
50356
- icon: List,
50357
- label: "Enum sensor"
50410
+ "media-player": {
50411
+ InlineControl: MediaPlayerInlineControl,
50412
+ HeroCard: MediaPlayerHeroCard
50358
50413
  },
50359
- [DeviceRole.DateTimeSensor]: {
50360
- icon: Clock,
50361
- label: "Date / time"
50414
+ alarm: {
50415
+ InlineControl: AlarmInlineControl,
50416
+ HeroCard: AlarmHeroCard
50362
50417
  },
50363
- [DeviceRole.GenericSensor]: {
50364
- icon: Gauge,
50365
- label: "Sensor"
50418
+ control: {
50419
+ InlineControl: ControlInlineControl,
50420
+ HeroCard: ControlHeroCard
50366
50421
  },
50367
- [DeviceRole.NumericControl]: {
50368
- icon: SlidersHorizontal,
50369
- label: "Numeric control"
50422
+ weather: {
50423
+ InlineControl: WeatherInlineControl,
50424
+ HeroCard: WeatherHeroCard
50370
50425
  },
50371
- [DeviceRole.SelectControl]: {
50372
- icon: ListChecks,
50373
- label: "Select control"
50426
+ vacuum: {
50427
+ InlineControl: VacuumInlineControl,
50428
+ HeroCard: VacuumHeroCard
50374
50429
  },
50375
- [DeviceRole.TextControl]: {
50376
- icon: Type,
50377
- label: "Text control"
50430
+ "lawn-mower": {
50431
+ InlineControl: LawnMowerInlineControl,
50432
+ HeroCard: LawnMowerHeroCard
50378
50433
  },
50379
- [DeviceRole.DateTimeControl]: {
50380
- icon: Clock,
50381
- label: "Date / time control"
50434
+ image: {
50435
+ InlineControl: ImageInlineControl,
50436
+ HeroCard: ImageHeroCard
50382
50437
  },
50383
- [DeviceRole.MobilePushNotifier]: {
50384
- icon: Smartphone,
50385
- label: "Mobile push"
50438
+ "pet-feeder": {
50439
+ InlineControl: PetFeederInlineControl,
50440
+ HeroCard: PetFeederHeroCard
50386
50441
  },
50387
- [DeviceRole.MessagingNotifier]: {
50388
- icon: MessageSquare,
50389
- label: "Messaging"
50442
+ script: {
50443
+ InlineControl: ScriptInlineControl,
50444
+ HeroCard: ScriptHeroCard
50390
50445
  },
50391
- [DeviceRole.EmailNotifier]: {
50392
- icon: Mail,
50393
- label: "Email"
50446
+ automation: {
50447
+ InlineControl: AutomationInlineControl,
50448
+ HeroCard: AutomationHeroCard
50394
50449
  },
50395
- [DeviceRole.GenericNotifier]: {
50396
- icon: Bell,
50397
- label: "Notifier"
50450
+ dummy: {
50451
+ InlineControl: DummyInline,
50452
+ HeroCard: DummyHeroCard
50398
50453
  }
50399
50454
  };
50455
+ /**
50456
+ * Exhaustive record over DeviceType, DERIVED from the shared decision
50457
+ * (`DEVICE_TYPE_CONTROL_KIND`) composed with the web `CONTROL_KIND_COMPONENTS`
50458
+ * binding — so the type→control mapping can never diverge from the single
50459
+ * source in `@camstack/types`. Preserved as a public export for consumers/tests
50460
+ * that index it by `DeviceType`.
50461
+ *
50462
+ * The `Object.fromEntries` result is a `{ [k: string]: DeviceTypeControl }`;
50463
+ * `Object.values(DeviceType)` (a string enum) yields exactly the enum's members,
50464
+ * so the record is fully populated — the assertion narrows that documented
50465
+ * enum-record projection boundary.
50466
+ */
50467
+ var DEVICE_TYPE_CONTROL = Object.fromEntries(Object.values(DeviceType).map((type) => [type, CONTROL_KIND_COMPONENTS[DEVICE_TYPE_CONTROL_KIND[type]]]));
50468
+ /**
50469
+ * Per-type FILL flag.
50470
+ *
50471
+ * true — range / choice / button controls (cover ▲■▼ triad, thermostat
50472
+ * setpoint stepper, media/control/button action buttons, alarm
50473
+ * segmented control). The child STRETCHES to the full column width.
50474
+ * false — readouts, tap-toggles, icon controls (lock), and dummy/placeholder
50475
+ * types. The child keeps its NATURAL width; where it sits in the
50476
+ * column (flush right vs centered) is decided by the caller via a
50477
+ * `ControlPlacement` — see `resolveControlAlign`.
50478
+ *
50479
+ * Dummy types (Camera, Hub, Generic, Weather, Vacuum, Container, and placeholder
50480
+ * types Notifier/Script/Automation whose InlineControl renders "—") are `false` —
50481
+ * the dash placeholder must not be stretched to the full column width. (AlarmPanel
50482
+ * has a real segmented inline control and is `true`.) When a later task gives a
50483
+ * remaining placeholder type a real stretchy inline control, flip its flag to true.
50484
+ *
50485
+ * Exhaustive over DeviceType — a missing member is a TypeScript build error.
50486
+ */
50487
+ var CONTROL_FILLS = {
50488
+ [DeviceType.Cover]: true,
50489
+ [DeviceType.Valve]: true,
50490
+ [DeviceType.Thermostat]: true,
50491
+ [DeviceType.Climate]: true,
50492
+ [DeviceType.Control]: true,
50493
+ [DeviceType.Button]: true,
50494
+ [DeviceType.MediaPlayer]: true,
50495
+ [DeviceType.AlarmPanel]: true,
50496
+ [DeviceType.Lock]: false,
50497
+ [DeviceType.Vacuum]: false,
50498
+ [DeviceType.LawnMower]: false,
50499
+ [DeviceType.Humidifier]: false,
50500
+ [DeviceType.Switch]: false,
50501
+ [DeviceType.Siren]: false,
50502
+ [DeviceType.Light]: false,
50503
+ [DeviceType.Fan]: false,
50504
+ [DeviceType.WaterHeater]: false,
50505
+ [DeviceType.Sensor]: false,
50506
+ [DeviceType.Presence]: false,
50507
+ [DeviceType.EventEmitter]: false,
50508
+ [DeviceType.Notifier]: false,
50509
+ [DeviceType.Script]: false,
50510
+ [DeviceType.Automation]: false,
50511
+ [DeviceType.Camera]: false,
50512
+ [DeviceType.Hub]: false,
50513
+ [DeviceType.Update]: false,
50514
+ [DeviceType.Generic]: false,
50515
+ [DeviceType.Weather]: false,
50516
+ [DeviceType.Container]: false,
50517
+ [DeviceType.Image]: false,
50518
+ [DeviceType.PetFeeder]: false
50519
+ };
50400
50520
  var DEVICE_TYPE_VALUES = new Set(Object.values(DeviceType));
50401
- var DEVICE_ROLE_VALUES = new Set(Object.values(DeviceRole));
50402
- function isDeviceType(value) {
50403
- return DEVICE_TYPE_VALUES.has(value);
50404
- }
50405
- function isDeviceRole(value) {
50406
- return DEVICE_ROLE_VALUES.has(value);
50407
- }
50408
- /** Meta for a known `DeviceType`. */
50409
- function deviceTypeMeta(type) {
50410
- return DEVICE_TYPE_META[type];
50411
- }
50412
- /** Meta for a known `DeviceRole`. */
50413
- function deviceRoleMeta(role) {
50414
- return DEVICE_ROLE_META[role];
50521
+ /**
50522
+ * Runtime-string-safe lookup: a device row's `type` crosses the wire as a plain
50523
+ * string. The type→control-kind DECISION is single-sourced in `@camstack/types`
50524
+ * (`resolveDeviceControlKind`, null for an unrecognised type); this maps the
50525
+ * resolved kind onto the web components.
50526
+ */
50527
+ function resolveDeviceControl(type) {
50528
+ const kind = resolveDeviceControlKind(type);
50529
+ return kind ? CONTROL_KIND_COMPONENTS[kind] : null;
50415
50530
  }
50416
- var FALLBACK_DEVICE_TYPE_META = DEVICE_TYPE_META[DeviceType.Generic];
50417
- var FALLBACK_DEVICE_ROLE_META = DEVICE_ROLE_META[DeviceRole.GenericSensor];
50418
50531
  /**
50419
- * Runtime-string-safe lookup: device data crosses process / network boundaries
50420
- * as plain strings, so an unrecognised value falls back to the `Generic` meta
50421
- * instead of throwing.
50532
+ * Runtime-string-safe alignment lookup for the fixed-width control column.
50422
50533
  *
50423
- * This also guards mixed-version module-federation loads where the host
50424
- * `@camstack/types` enum has a new member before the loaded ui-library bundle
50425
- * has the matching `DEVICE_TYPE_META` entry.
50534
+ * A device's `type` crosses the wire as a plain string;
50535
+ * `DEVICE_TYPE_VALUES.has(type)` narrows it to `DeviceType` before indexing
50536
+ * `CONTROL_FILLS`, making the cast sound. The `'table'` placement is
50537
+ * content-width for EVERY type (returns `FIT`), so a wide control never
50538
+ * overflows a fixed box — the FILL flag is irrelevant there. For the fixed-box
50539
+ * placements a FILL type always stretches (`FILL`) and a natural-width type is
50540
+ * placed by the caller's `placement` — `'center'` → `CENTER`, `'right'` →
50541
+ * `RIGHT` (card / minimal view). An unrecognised type falls back to
50542
+ * natural-width placement.
50426
50543
  */
50427
- function deviceTypeMetaOf(type) {
50428
- if (!isDeviceType(type)) return FALLBACK_DEVICE_TYPE_META;
50429
- return DEVICE_TYPE_META[type] ?? FALLBACK_DEVICE_TYPE_META;
50430
- }
50431
- /** Runtime-string-safe role lookup; unknown roles fall back to `GenericSensor`. */
50432
- function deviceRoleMetaOf(role) {
50433
- if (!isDeviceRole(role)) return FALLBACK_DEVICE_ROLE_META;
50434
- return DEVICE_ROLE_META[role] ?? FALLBACK_DEVICE_ROLE_META;
50544
+ function resolveControlAlign(type, placement) {
50545
+ if (placement === "table") return "fit";
50546
+ return DEVICE_TYPE_VALUES.has(type) && CONTROL_FILLS[type] ? FILL : placement === "center" ? CENTER : RIGHT;
50435
50547
  }
50548
+ //#endregion
50549
+ //#region src/lib/format-last-seen.ts
50436
50550
  /**
50437
- * Icons an operator may pick for a per-device `display.icon` override (and for
50438
- * a per-role default), keyed by a stable kebab-case name. Named imports only —
50439
- * no dynamic indexing into lucide, so the bundle carries exactly this set.
50440
- * Unknown keys resolve to `undefined` (caller falls through to the next level).
50551
+ * Relative "last seen" formatter for the offline affordance.
50441
50552
  *
50442
- * The vocabulary is the one the VIEWER draws from too (its own icon module,
50443
- * keyed by these same strings): a key added here that the phone cannot draw
50444
- * falls through to the next resolution level there, which reads as "my change
50445
- * did nothing". So the KEY is the contract — a stable kebab-case string, never
50446
- * a glyph identity — and `device-meta.test.ts` pins that every key resolves
50447
- * and that the set never loses a key an operator may already have persisted.
50553
+ * Renders a short relative-time label from a millisecond epoch:
50554
+ * "just now", "5m ago", "3h ago", "2d ago". Returns `null` for missing
50555
+ * inputs, or inputs below {@link MIN_PLAUSIBLE_TIMESTAMP_MS} (a
50556
+ * placeholder or a seeded "known value, unknown change time" stamp), so
50557
+ * callers can render nothing rather than a misleading "—", "NaN ago", or
50558
+ * (for a seeded `1`) "20000 days ago".
50448
50559
  *
50449
- * Grouped by what an operator is naming: a device, a light or appliance, a
50450
- * sensor or the weather it reads, a room, or what moves through it. `gauge2`
50451
- * is a legacy alias kept for rows that persisted it.
50560
+ * Future timestamps (clock skew between the device and the browser)
50561
+ * collapse to "just now" instead of a negative duration.
50452
50562
  */
50453
- var DISPLAY_ICON_REGISTRY = {
50454
- camera: Camera,
50455
- network: Network,
50456
- "toggle-left": ToggleLeft,
50457
- "toggle-right": ToggleRight,
50458
- "circle-dot": CircleDot,
50459
- "refresh-cw": RefreshCw,
50460
- box: Box,
50461
- "scroll-text": ScrollText,
50462
- workflow: Workflow,
50463
- lock: Lock,
50464
- "lock-open": LockOpen,
50465
- blinds: Blinds,
50466
- "air-vent": AirVent,
50467
- "monitor-play": MonitorPlay,
50468
- "sliders-horizontal": SlidersHorizontal,
50469
- "map-pin": MapPin,
50470
- bot: Bot,
50471
- tractor: Tractor,
50472
- image: Image,
50473
- bone: Bone,
50474
- "door-closed": DoorClosed,
50475
- "door-open": DoorOpen,
50476
- crosshair: Crosshair,
50477
- eye: Eye,
50478
- "eye-off": EyeOff,
50479
- smartphone: Smartphone,
50480
- phone: Phone,
50481
- tablet: Tablet,
50482
- laptop: Laptop,
50483
- monitor: Monitor,
50484
- tv: Tv,
50485
- speaker: Speaker,
50486
- printer: Printer,
50487
- projector: Projector,
50488
- "gamepad-2": Gamepad2,
50489
- radio: Radio,
50490
- router: Router,
50491
- bluetooth: Bluetooth,
50492
- cpu: Cpu,
50493
- "hard-drive": HardDrive,
50494
- server: Server,
50495
- mic: Mic,
50496
- "mic-off": MicOff,
50497
- video: Video,
50498
- "video-off": VideoOff,
50499
- music: Music,
50500
- plug: Plug,
50501
- "plug-zap": PlugZap,
50502
- power: Power,
50503
- "power-off": PowerOff,
50504
- cable: Cable,
50505
- battery: Battery,
50506
- "battery-charging": BatteryCharging,
50507
- "battery-full": BatteryFull,
50508
- "battery-low": BatteryLow,
50509
- key: Key,
50510
- "key-round": KeyRound,
50511
- mailbox: Mailbox,
50512
- package: Package,
50513
- shield: Shield,
50514
- "shield-check": ShieldCheck,
50515
- "shield-off": ShieldOff,
50516
- lightbulb: Lightbulb,
50517
- lamp: Lamp,
50518
- "lamp-ceiling": LampCeiling,
50519
- "lamp-desk": LampDesk,
50520
- "lamp-floor": LampFloor,
50521
- flashlight: Flashlight,
50522
- heater: Heater,
50523
- snowflake: Snowflake,
50524
- "thermometer-sun": ThermometerSun,
50525
- "thermometer-snowflake": ThermometerSnowflake,
50526
- refrigerator: Refrigerator,
50527
- microwave: Microwave,
50528
- "washing-machine": WashingMachine,
50529
- "cooking-pot": CookingPot,
50530
- utensils: Utensils,
50531
- thermometer: Thermometer,
50532
- droplets: Droplets,
50533
- droplet: Droplet,
50534
- gauge: Gauge,
50535
- gauge2: Gauge,
50536
- sun: Sun,
50537
- "sun-medium": SunMedium,
50538
- sunrise: Sunrise,
50539
- moon: Moon,
50540
- wind: Wind,
50541
- cloud: Cloud,
50542
- "cloud-rain": CloudRain,
50543
- "cloud-sun": CloudSun,
50544
- waves: Waves,
50545
- sprout: Sprout,
50546
- leaf: Leaf,
50547
- flower: Flower,
50548
- zap: Zap,
50549
- flame: Flame,
50550
- bell: Bell,
50551
- "bell-ring": BellRing,
50552
- "bell-off": BellOff,
50553
- siren: Siren,
50554
- radar: Radar,
50555
- activity: Activity,
50556
- hash: Hash,
50557
- clock: Clock,
50558
- timer: Timer,
50559
- calendar: Calendar,
50560
- pipette: Pipette,
50561
- fan: Fan,
50562
- bath: Bath,
50563
- footprints: Footprints,
50564
- "alarm-smoke": AlarmSmoke,
50565
- "shield-alert": ShieldAlert,
50566
- vibrate: Vibrate,
50567
- wifi: Wifi,
50568
- "wifi-off": WifiOff,
50569
- "volume-2": Volume2,
50570
- "volume-x": VolumeX,
50571
- heart: Heart,
50572
- "heart-pulse": HeartPulse,
50573
- star: Star,
50574
- sparkles: Sparkles,
50575
- house: House,
50576
- sofa: Sofa,
50577
- "bed-double": BedDouble,
50578
- bed: Bed,
50579
- warehouse: Warehouse,
50580
- trees: Trees,
50581
- "tree-pine": TreePine,
50582
- fence: Fence,
50583
- store: Store,
50584
- building: Building,
50585
- "building-2": Building2,
50586
- mountain: Mountain,
50587
- umbrella: Umbrella,
50588
- dumbbell: Dumbbell,
50589
- wrench: Wrench,
50590
- hammer: Hammer,
50591
- drill: Drill,
50592
- "waves-ladder": WavesLadder,
50593
- "circle-parking": CircleParking,
50594
- briefcase: Briefcase,
50595
- route: Route,
50596
- car: Car,
50597
- bike: Bike,
50598
- bus: Bus,
50599
- truck: Truck,
50600
- plane: Plane,
50601
- "train-front": TrainFront,
50602
- sailboat: Sailboat,
50603
- dog: Dog,
50604
- cat: Cat,
50605
- bird: Bird,
50606
- "paw-print": PawPrint,
50607
- baby: Baby,
50608
- user: User,
50609
- users: Users,
50610
- "person-standing": PersonStanding
50611
- };
50612
- /** Resolve a `display.icon` name to a lucide component, or `undefined` when the
50613
- * name is not in the curated `DISPLAY_ICON_REGISTRY`. */
50614
- function resolveDisplayIcon(name) {
50615
- return Object.hasOwn(DISPLAY_ICON_REGISTRY, name) ? DISPLAY_ICON_REGISTRY[name] : void 0;
50563
+ var SECOND_MS = 1e3;
50564
+ var MINUTE_MS = 60 * SECOND_MS;
50565
+ var HOUR_MS = 60 * MINUTE_MS;
50566
+ var DAY_MS = 24 * HOUR_MS;
50567
+ var JUST_NOW_MS = 30 * SECOND_MS;
50568
+ /**
50569
+ * The earliest timestamp a real "last seen" could plausibly carry. Below
50570
+ * this, a value is a placeholder or a seeded "known value, unknown change
50571
+ * time" stamp (e.g. Homematic's `HM_SEEDED_CHANGED_AT = 1`) — never a real
50572
+ * instant. Year 2000 predates every device this renders.
50573
+ */
50574
+ var MIN_PLAUSIBLE_TIMESTAMP_MS = Date.UTC(2e3, 0, 1);
50575
+ function formatLastSeen(timestampMs) {
50576
+ if (timestampMs === null || timestampMs === void 0 || timestampMs < MIN_PLAUSIBLE_TIMESTAMP_MS) return null;
50577
+ const delta = Date.now() - timestampMs;
50578
+ if (delta < JUST_NOW_MS) return "just now";
50579
+ if (delta < HOUR_MS) return `${String(Math.floor(delta / MINUTE_MS))}m ago`;
50580
+ if (delta < DAY_MS) return `${String(Math.floor(delta / HOUR_MS))}h ago`;
50581
+ return `${String(Math.floor(delta / DAY_MS))}d ago`;
50582
+ }
50583
+ //#endregion
50584
+ //#region src/composites/device-controls/offline-badge.tsx
50585
+ function OfflineBadge({ lastChangedAt, className }) {
50586
+ const lastSeen = formatLastSeen(lastChangedAt);
50587
+ return /* @__PURE__ */ jsxs("span", {
50588
+ title: lastSeen === null ? "Unavailable" : `Unavailable · last seen ${lastSeen}`,
50589
+ className: cn("inline-flex items-center gap-1 rounded-full px-2 py-0.5", "bg-surface-hover text-foreground-subtle text-[10px] font-medium", "border border-border", className),
50590
+ children: [
50591
+ /* @__PURE__ */ jsx(WifiOff, {
50592
+ className: "h-3 w-3 flex-shrink-0",
50593
+ "aria-hidden": "true"
50594
+ }),
50595
+ /* @__PURE__ */ jsx("span", { children: "Unavailable" }),
50596
+ lastSeen !== null ? /* @__PURE__ */ jsx("span", {
50597
+ className: "text-foreground-subtle/70",
50598
+ children: `· last seen ${lastSeen}`
50599
+ }) : null
50600
+ ]
50601
+ });
50616
50602
  }
50603
+ //#endregion
50604
+ //#region src/composites/device-controls/primary-child.ts
50617
50605
  /**
50618
- * EXHAUSTIVE device-type filter options — one entry per `DeviceType` member,
50619
- * derived from `DEVICE_TYPE_META` so the set is always complete and a newly
50620
- * added `DeviceType` automatically appears in every type filter (Devices page,
50621
- * adoption modal, …) without touching any consumer.
50606
+ * Which child a container is rendered AS — this surface's name for the shared
50607
+ * rule.
50622
50608
  *
50623
- * The previous per-consumer logic built options from the types PRESENT in the
50624
- * list (or a hand-maintained subset), so recently-added types (vacuum, lawn
50625
- * mower, valve, humidifier, water heater, weather, image, …) were silently
50626
- * missing from the dropdown. This is the single source of truth instead.
50609
+ * The list and the resolution used to live here, in a COPY that carried a
50610
+ * comment asking the Home Assistant adoption mapping and the viewer's panel to
50611
+ * stay in sync with it. Three lists and three comments is not one rule, it is
50612
+ * three chances to drift. Both now come from `@camstack/types`, which all of
50613
+ * them already depend on; what remains here is the adapter for this module's
50614
+ * historical `ChildRef` shape.
50615
+ */
50616
+ /** The shared precedence, under this module's historical name. */
50617
+ var PRIORITY = CONTAINER_CHILD_PRIORITY;
50618
+ /**
50619
+ * Resolve a container's primary child: the operator's pick when it still
50620
+ * matches a live child, else the highest-priority type.
50627
50621
  *
50628
- * `id` is the raw `DeviceType` string (matches the filtering predicate, which
50629
- * compares against `device.type`); `label` + `icon` come from the meta map.
50630
- * Ordered by `DeviceType` declaration order for a stable, intentional layout.
50622
+ * `entityId` here is `sourceInfo?.id ?? stableId` (see `containerChildToRef`).
50623
+ * The shared resolver matches its canonical `stableId` key first and accepts
50624
+ * that legacy value too, so a pick stored under either keeps resolving.
50631
50625
  */
50632
- var ALL_DEVICE_TYPE_FILTER_OPTIONS = Object.values(DeviceType).map((type) => {
50633
- const meta = deviceTypeMetaOf(type);
50626
+ function resolvePrimaryChild(children, overrideEntityId, containerName) {
50627
+ const picked = resolveContainerPrimaryChild(children.map((c) => ({
50628
+ id: c.id,
50629
+ stableId: c.entityId,
50630
+ type: c.type,
50631
+ ...c.role !== void 0 ? { role: c.role } : {},
50632
+ ...c.name !== void 0 ? { name: c.name } : {}
50633
+ })), overrideEntityId, containerName);
50634
+ return picked === null ? null : children.find((c) => c.id === picked.id) ?? null;
50635
+ }
50636
+ //#endregion
50637
+ //#region src/composites/device-controls/container-primary.ts
50638
+ /**
50639
+ * Re-sync-stable identity of a child. Prefers the upstream-system dispatch key
50640
+ * (`sourceInfo.id` — e.g. HA `entity_id`); falls back to `stableId` for
50641
+ * providers that never populated SourceInfo. Both survive a re-sync that may
50642
+ * reallocate the numeric `id`.
50643
+ */
50644
+ function childEntityId(child) {
50645
+ return child.sourceInfo?.id ?? child.stableId;
50646
+ }
50647
+ /** Project a raw child row onto the pure resolver's {@link ChildRef} shape. */
50648
+ function containerChildToRef(child) {
50634
50649
  return {
50635
- id: type,
50636
- label: meta.label,
50637
- icon: meta.icon
50650
+ id: child.id,
50651
+ entityId: childEntityId(child),
50652
+ type: child.type,
50653
+ ...child.role !== void 0 && child.role !== null ? { role: child.role } : {},
50654
+ ...child.name !== void 0 ? { name: child.name } : {}
50638
50655
  };
50639
- });
50640
- function allDeviceTypeFilterOptions() {
50641
- return ALL_DEVICE_TYPE_FILTER_OPTIONS;
50656
+ }
50657
+ /**
50658
+ * Resolve the entityId override from the container's persisted `linkDeviceId`.
50659
+ * Returns the linked child's stable entityId, or `undefined` when there is no
50660
+ * link or the link is stale (points to a numeric id no longer present) — in
50661
+ * which case the resolver falls back to the priority default.
50662
+ */
50663
+ function overrideEntityIdFromLink(children, linkDeviceId) {
50664
+ if (linkDeviceId === null || linkDeviceId === void 0) return void 0;
50665
+ const linked = children.find((c) => c.id === linkDeviceId);
50666
+ return linked === void 0 ? void 0 : childEntityId(linked);
50667
+ }
50668
+ /**
50669
+ * Full container → primary-child resolution: project children to refs, resolve
50670
+ * the override entityId, and delegate to the pure resolver. Returns `null` for a
50671
+ * container with no children (caller degrades gracefully).
50672
+ *
50673
+ * Override precedence:
50674
+ * 1. `primaryChildEntityId` — the DURABLE override (re-sync/rename-stable),
50675
+ * passed straight to the resolver which matches on `entityId`. Wins when a
50676
+ * non-empty string is supplied.
50677
+ * 2. `linkDeviceId` — LEGACY numeric override, re-derived to an entityId from
50678
+ * the live child set. Consulted only when there is no durable override.
50679
+ *
50680
+ * When neither resolves to a live child, the resolver falls back to the
50681
+ * priority default.
50682
+ */
50683
+ function resolveContainerPrimary(children, linkDeviceId, primaryChildEntityId) {
50684
+ return resolvePrimaryChild(children.map(containerChildToRef), primaryChildEntityId !== null && primaryChildEntityId !== void 0 && primaryChildEntityId !== "" ? primaryChildEntityId : overrideEntityIdFromLink(children, linkDeviceId));
50685
+ }
50686
+ //#endregion
50687
+ //#region src/composites/device-controls/container-primary-hero.tsx
50688
+ function ContainerPrimaryHero({ trpc, children, linkDeviceId, primaryChildEntityId }) {
50689
+ const primary = resolveContainerPrimary(children, linkDeviceId, primaryChildEntityId);
50690
+ const primaryChild = primary === null ? void 0 : children.find((c) => c.id === primary.id);
50691
+ if (primaryChild === void 0) return /* @__PURE__ */ jsx("div", {
50692
+ className: "flex h-full w-full items-center justify-center p-4 text-[11px] italic text-foreground-subtle",
50693
+ children: "No child entities to display."
50694
+ });
50695
+ return /* @__PURE__ */ jsx("div", {
50696
+ className: "flex h-full w-full flex-col items-stretch gap-3 p-3 sm:p-4",
50697
+ children: /* @__PURE__ */ jsx(resolveDeviceControl(primaryChild.type)?.HeroCard ?? DummyHeroCard, {
50698
+ trpc,
50699
+ deviceId: primaryChild.id,
50700
+ features: primaryChild.features
50701
+ })
50702
+ });
50703
+ }
50704
+ //#endregion
50705
+ //#region src/composites/device-controls/primary-child-picker.tsx
50706
+ var BASE_SELECT_CLASS = "rounded-md border border-border bg-surface px-1.5 py-0.5 text-[11px] text-foreground";
50707
+ function PrimaryChildPicker({ childrenList, selectedId, onSetPrimary, disabled, className }) {
50708
+ return /* @__PURE__ */ jsx("select", {
50709
+ value: selectedId,
50710
+ disabled,
50711
+ onChange: (e) => {
50712
+ const nextId = Number(e.target.value);
50713
+ if (!Number.isFinite(nextId) || nextId === selectedId) return;
50714
+ const picked = childrenList.find((c) => c.id === nextId);
50715
+ if (picked !== void 0) onSetPrimary(picked.stableId);
50716
+ },
50717
+ className: className === void 0 ? BASE_SELECT_CLASS : `${BASE_SELECT_CLASS} ${className}`,
50718
+ children: childrenList.map((c) => /* @__PURE__ */ jsxs("option", {
50719
+ value: c.id,
50720
+ children: [
50721
+ c.name,
50722
+ " (",
50723
+ c.type,
50724
+ ")"
50725
+ ]
50726
+ }, c.id))
50727
+ });
50728
+ }
50729
+ //#endregion
50730
+ //#region src/composites/device-list/batch-toolbar.tsx
50731
+ function DeviceBatchToolbar({ selectedIds, onClear, remove, disable, enable }) {
50732
+ const [pendingDelete, setPendingDelete] = useState(false);
50733
+ if (selectedIds.size === 0) return null;
50734
+ const ids = [...selectedIds];
50735
+ const count = ids.length;
50736
+ const handleDisable = () => {
50737
+ for (const id of ids) disable({ deviceId: id });
50738
+ onClear();
50739
+ };
50740
+ const handleEnable = () => {
50741
+ for (const id of ids) enable({ deviceId: id });
50742
+ onClear();
50743
+ };
50744
+ const handleDeleteFirst = () => {
50745
+ setPendingDelete(true);
50746
+ };
50747
+ const handleDeleteConfirm = () => {
50748
+ for (const id of ids) remove({ deviceId: id });
50749
+ setPendingDelete(false);
50750
+ onClear();
50751
+ };
50752
+ const handleDeleteCancel = () => {
50753
+ setPendingDelete(false);
50754
+ };
50755
+ const handleClear = () => {
50756
+ setPendingDelete(false);
50757
+ onClear();
50758
+ };
50759
+ return /* @__PURE__ */ jsxs("div", {
50760
+ className: "flex items-center gap-2 px-3 py-2 rounded-lg border border-border bg-surface-hover",
50761
+ children: [
50762
+ /* @__PURE__ */ jsxs("span", {
50763
+ className: "text-xs font-medium text-foreground-subtle shrink-0",
50764
+ children: [count, " selected"]
50765
+ }),
50766
+ /* @__PURE__ */ jsx("div", {
50767
+ className: "h-3.5 w-px bg-border shrink-0",
50768
+ "aria-hidden": "true"
50769
+ }),
50770
+ /* @__PURE__ */ jsxs("div", {
50771
+ className: "flex items-center gap-1.5 flex-wrap",
50772
+ children: [
50773
+ /* @__PURE__ */ jsx("button", {
50774
+ type: "button",
50775
+ onClick: handleDisable,
50776
+ className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
50777
+ children: "Disable"
50778
+ }),
50779
+ /* @__PURE__ */ jsx("button", {
50780
+ type: "button",
50781
+ onClick: handleEnable,
50782
+ className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
50783
+ children: "Enable"
50784
+ }),
50785
+ pendingDelete ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("button", {
50786
+ type: "button",
50787
+ onClick: handleDeleteConfirm,
50788
+ className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-red-500/50 bg-red-500/10 text-red-400 hover:bg-red-500/20"),
50789
+ children: [
50790
+ "Confirm delete ",
50791
+ count,
50792
+ "?"
50793
+ ]
50794
+ }), /* @__PURE__ */ jsx("button", {
50795
+ type: "button",
50796
+ onClick: handleDeleteCancel,
50797
+ className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
50798
+ children: "Cancel"
50799
+ })] }) : /* @__PURE__ */ jsx("button", {
50800
+ type: "button",
50801
+ onClick: handleDeleteFirst,
50802
+ className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-red-500/40 bg-red-500/10 text-red-400 hover:bg-red-500/20"),
50803
+ children: "Delete"
50804
+ }),
50805
+ /* @__PURE__ */ jsx("button", {
50806
+ type: "button",
50807
+ onClick: handleClear,
50808
+ className: cn("inline-flex items-center h-6 px-2.5 rounded-md border text-[10.5px] font-medium transition-colors", "border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface"),
50809
+ children: "Clear"
50810
+ })
50811
+ ]
50812
+ })
50813
+ ]
50814
+ });
50815
+ }
50816
+ //#endregion
50817
+ //#region src/composites/device-list/batch-actions-bar.tsx
50818
+ /**
50819
+ * <DeviceBatchActionsBar> — responsive wrapper around <DeviceBatchToolbar>.
50820
+ *
50821
+ * Surfaces the available bulk actions once devices are selected:
50822
+ * - L2+ (`sm` and up): the toolbar renders inline as a row of buttons.
50823
+ * - L1 (`< sm`): a compact "Actions · N" trigger opens a popover that
50824
+ * hosts the SAME <DeviceBatchToolbar>, so the action logic (inline
50825
+ * delete-confirm, clear) is reused verbatim — never forked.
50826
+ *
50827
+ * Renders nothing while the selection is empty (delegated to the toolbar).
50828
+ */
50829
+ function DeviceBatchActionsBar(props) {
50830
+ const [open, setOpen] = useState(false);
50831
+ const rootRef = useRef(null);
50832
+ const count = props.selectedIds.size;
50833
+ useEffect(() => {
50834
+ if (!open) return;
50835
+ const onOutside = (e) => {
50836
+ const target = e.target instanceof Node ? e.target : null;
50837
+ if (target && rootRef.current && !rootRef.current.contains(target)) setOpen(false);
50838
+ };
50839
+ const onKey = (e) => {
50840
+ if (e.key === "Escape") setOpen(false);
50841
+ };
50842
+ document.addEventListener("mousedown", onOutside);
50843
+ document.addEventListener("touchstart", onOutside);
50844
+ document.addEventListener("keydown", onKey);
50845
+ return () => {
50846
+ document.removeEventListener("mousedown", onOutside);
50847
+ document.removeEventListener("touchstart", onOutside);
50848
+ document.removeEventListener("keydown", onKey);
50849
+ };
50850
+ }, [open]);
50851
+ if (count === 0) return null;
50852
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
50853
+ className: "hidden sm:block",
50854
+ children: /* @__PURE__ */ jsx(DeviceBatchToolbar, { ...props })
50855
+ }), /* @__PURE__ */ jsxs("div", {
50856
+ ref: rootRef,
50857
+ "data-testid": "batch-actions-compact",
50858
+ className: "relative sm:hidden",
50859
+ children: [/* @__PURE__ */ jsxs("button", {
50860
+ type: "button",
50861
+ onClick: () => setOpen((v) => !v),
50862
+ "aria-haspopup": "menu",
50863
+ "aria-expanded": open,
50864
+ className: "inline-flex items-center gap-1.5 h-7 px-2.5 rounded-full border border-primary/40 bg-primary/15 text-[10.5px] font-medium text-primary",
50865
+ children: [/* @__PURE__ */ jsxs("span", { children: ["Actions · ", String(count)] }), /* @__PURE__ */ jsx(ChevronDown, { className: `h-3 w-3 transition-transform ${open ? "rotate-180" : ""}` })]
50866
+ }), open && /* @__PURE__ */ jsx("div", {
50867
+ role: "menu",
50868
+ className: "absolute left-0 top-full mt-1 z-30 rounded-lg border border-border bg-surface shadow-md",
50869
+ children: /* @__PURE__ */ jsx(DeviceBatchToolbar, { ...props })
50870
+ })]
50871
+ })] });
50642
50872
  }
50643
50873
  //#endregion
50644
50874
  //#region src/hooks/use-debounced-string.ts
@@ -50659,6 +50889,59 @@ function useDebouncedString(value, delayMs) {
50659
50889
  return debounced;
50660
50890
  }
50661
50891
  //#endregion
50892
+ //#region src/hooks/use-persisted-page-size.ts
50893
+ /**
50894
+ * usePersistedPageSize — a "rows per page" choice remembered per viewer.
50895
+ *
50896
+ * The caller names the storage key, the allowed sizes and the default. Each
50897
+ * surface with its own default or option set keeps its own key, so one
50898
+ * surface's choice never lands on another that cannot show it (the device
50899
+ * list's global key stays `useDeviceListPageSize`'s, with its own options).
50900
+ *
50901
+ * `PAGE_SIZE_ALL` (0) is the "All" option: one page holding every row.
50902
+ *
50903
+ * Storage is a convenience, never a requirement: every read and write is
50904
+ * guarded, and a blocked / private-mode / missing storage simply means the
50905
+ * choice lasts until the page is left. A stored value that is not one of the
50906
+ * options (an older option set, a hand-edited value) is ignored.
50907
+ */
50908
+ /** The "All" option — one page holding every row. */
50909
+ var PAGE_SIZE_ALL = 0;
50910
+ function readStored(key, options, fallback) {
50911
+ if (key === null || typeof window === "undefined") return fallback;
50912
+ try {
50913
+ const raw = window.localStorage.getItem(key);
50914
+ if (raw === null) return fallback;
50915
+ const parsed = Number.parseInt(raw, 10);
50916
+ return Number.isFinite(parsed) && options.includes(parsed) ? parsed : fallback;
50917
+ } catch {
50918
+ return fallback;
50919
+ }
50920
+ }
50921
+ function writeStored(key, value) {
50922
+ if (key === null || typeof window === "undefined") return;
50923
+ try {
50924
+ window.localStorage.setItem(key, String(value));
50925
+ } catch {}
50926
+ }
50927
+ /** @returns `[size, setSize]`; `setSize` ignores a size that is not an option. */
50928
+ function usePersistedPageSize({ storageKey, options, defaultSize }) {
50929
+ const seed = options.includes(defaultSize) ? defaultSize : options[0] ?? defaultSize;
50930
+ const [size, setSize] = useState(() => readStored(storageKey, options, seed));
50931
+ return [size, useCallback((next) => {
50932
+ if (!options.includes(next)) return;
50933
+ setSize(next);
50934
+ writeStored(storageKey, next);
50935
+ }, [options, storageKey])];
50936
+ }
50937
+ /**
50938
+ * The number of rows one page holds for a chosen size: "All" is every row
50939
+ * (at least 1, so the pager arithmetic never divides by zero).
50940
+ */
50941
+ function effectivePageSize(size, totalItems) {
50942
+ return size === 0 ? Math.max(1, totalItems) : size;
50943
+ }
50944
+ //#endregion
50662
50945
  //#region src/hooks/use-device-list-page-size.ts
50663
50946
  /**
50664
50947
  * useDeviceListPageSize — global page-size preference for the device-list
@@ -50669,8 +50952,9 @@ function useDebouncedString(value, delayMs) {
50669
50952
  * value is validated against {@link DEVICE_LIST_PAGE_SIZE_OPTIONS}; any
50670
50953
  * garbage / out-of-range value falls back to the provided default.
50671
50954
  *
50672
- * SSR / jsdom-safe: every `window` access is guarded so the hook can run in
50673
- * a non-browser environment without throwing.
50955
+ * A thin binding of `usePersistedPageSize` to that key and option set; a
50956
+ * surface with a different default or options uses the generic hook with a
50957
+ * key of its own.
50674
50958
  */
50675
50959
  /** Selectable per-page sizes. The table default (50) is intentionally one of them. */
50676
50960
  var DEVICE_LIST_PAGE_SIZE_OPTIONS = [
@@ -50681,33 +50965,6 @@ var DEVICE_LIST_PAGE_SIZE_OPTIONS = [
50681
50965
  ];
50682
50966
  /** Single global key shared across every device-list surface. */
50683
50967
  var DEVICE_LIST_PAGE_SIZE_KEY = "camstack.device-list.pageSize";
50684
- function isAllowed(value) {
50685
- return DEVICE_LIST_PAGE_SIZE_OPTIONS.includes(value);
50686
- }
50687
- /**
50688
- * Normalize a caller-supplied `defaultSize` to an allowed option, falling back
50689
- * to the canonical default so a stray value can never seed a phantom size.
50690
- */
50691
- function safeDefault(defaultSize) {
50692
- return isAllowed(defaultSize) ? defaultSize : 50;
50693
- }
50694
- function readStored(defaultSize) {
50695
- if (typeof window === "undefined") return defaultSize;
50696
- try {
50697
- const raw = window.localStorage.getItem(DEVICE_LIST_PAGE_SIZE_KEY);
50698
- if (raw === null) return defaultSize;
50699
- const parsed = Number.parseInt(raw, 10);
50700
- return Number.isFinite(parsed) && isAllowed(parsed) ? parsed : defaultSize;
50701
- } catch {
50702
- return defaultSize;
50703
- }
50704
- }
50705
- function writeStored(value) {
50706
- if (typeof window === "undefined") return;
50707
- try {
50708
- window.localStorage.setItem(DEVICE_LIST_PAGE_SIZE_KEY, String(value));
50709
- } catch {}
50710
- }
50711
50968
  /**
50712
50969
  * @param defaultSize fallback when nothing valid is stored. If it is not itself
50713
50970
  * a member of {@link DEVICE_LIST_PAGE_SIZE_OPTIONS}, it is replaced by
@@ -50715,13 +50972,11 @@ function writeStored(value) {
50715
50972
  * @returns `[size, setSize]` — `setSize` ignores values outside the allowed set.
50716
50973
  */
50717
50974
  function useDeviceListPageSize(defaultSize) {
50718
- const seed = safeDefault(defaultSize);
50719
- const [size, setSize] = useState(() => readStored(seed));
50720
- return [size, useCallback((next) => {
50721
- if (!isAllowed(next)) return;
50722
- setSize(next);
50723
- writeStored(next);
50724
- }, [])];
50975
+ return usePersistedPageSize({
50976
+ storageKey: DEVICE_LIST_PAGE_SIZE_KEY,
50977
+ options: DEVICE_LIST_PAGE_SIZE_OPTIONS,
50978
+ defaultSize: DEVICE_LIST_PAGE_SIZE_OPTIONS.includes(defaultSize) ? defaultSize : 50
50979
+ });
50725
50980
  }
50726
50981
  //#endregion
50727
50982
  //#region src/composites/device-item/helpers.ts
@@ -53019,6 +53274,29 @@ function groupTopLevelAndAccessories(devices, options) {
53019
53274
  };
53020
53275
  }
53021
53276
  //#endregion
53277
+ //#region src/composites/device-list/page-size-select.tsx
53278
+ function pageSizeLabel(size) {
53279
+ return size === 0 ? "All" : String(size);
53280
+ }
53281
+ function PageSizeSelect({ value, options, onChange, ariaLabel }) {
53282
+ return /* @__PURE__ */ jsxs("div", {
53283
+ className: "inline-flex items-center gap-1.5",
53284
+ children: [/* @__PURE__ */ jsx("span", {
53285
+ "aria-hidden": "true",
53286
+ children: "Per page"
53287
+ }), /* @__PURE__ */ jsx("select", {
53288
+ "aria-label": ariaLabel,
53289
+ value,
53290
+ onChange: (e) => onChange(Number.parseInt(e.target.value, 10)),
53291
+ className: "h-7 px-1.5 rounded-md border border-border bg-surface text-[11px] text-foreground focus:outline-none focus:ring-1 focus:ring-primary/40",
53292
+ children: options.map((opt) => /* @__PURE__ */ jsx("option", {
53293
+ value: opt,
53294
+ children: pageSizeLabel(opt)
53295
+ }, opt))
53296
+ })]
53297
+ });
53298
+ }
53299
+ //#endregion
53022
53300
  //#region src/composites/device-list/multi-select.tsx
53023
53301
  /**
53024
53302
  * Compact multi-select dropdown — a single chip-sized trigger ("Label · N")
@@ -53933,23 +54211,11 @@ function DeviceModeList(props) {
53933
54211
  totalItems,
53934
54212
  " ",
53935
54213
  totalItems === 1 ? "device" : "devices"
53936
- ] }), /* @__PURE__ */ jsxs("div", {
53937
- className: "inline-flex items-center gap-1.5",
53938
- children: [/* @__PURE__ */ jsx("span", {
53939
- "aria-hidden": "true",
53940
- children: "Per page"
53941
- }), /* @__PURE__ */ jsx("select", {
53942
- "aria-label": "Devices per page",
53943
- value: tablePageSize,
53944
- onChange: (e) => {
53945
- setTablePageSize(Number.parseInt(e.target.value, 10));
53946
- },
53947
- className: "h-7 px-1.5 rounded-md border border-border bg-surface text-[11px] text-foreground focus:outline-none focus:ring-1 focus:ring-primary/40",
53948
- children: DEVICE_LIST_PAGE_SIZE_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", {
53949
- value: opt,
53950
- children: opt
53951
- }, opt))
53952
- })]
54214
+ ] }), /* @__PURE__ */ jsx(PageSizeSelect, {
54215
+ ariaLabel: "Devices per page",
54216
+ value: tablePageSize,
54217
+ options: DEVICE_LIST_PAGE_SIZE_OPTIONS,
54218
+ onChange: setTablePageSize
53953
54219
  })]
53954
54220
  }), /* @__PURE__ */ jsx(Pagination, {
53955
54221
  currentPage: safePage,
@@ -53993,6 +54259,7 @@ function useDeviceListRealtimeSync() {
53993
54259
  }
53994
54260
  //#endregion
53995
54261
  //#region src/composites/device-list/generic-mode.tsx
54262
+ var FIXED_PAGE_SIZE = 100;
53996
54263
  /**
53997
54264
  * Generic render-prop list: search + pagination chrome shared with the device
53998
54265
  * list, over an arbitrary item type. Local (non-URL) state — intended for
@@ -54000,7 +54267,12 @@ function useDeviceListRealtimeSync() {
54000
54267
  * `renderItem`.
54001
54268
  */
54002
54269
  function GenericModeList(props) {
54003
- const { items, getKey, getSearchText, renderItem, getFilterType, mapFilterOption, filterOptions, filterLabel = "Type", initialTypeFilters = [], columns, renderExpanded, isExpanded, rowClassName, onRowClick, isLoading = false, searchPlaceholder = "Search", emptyLabel = "No items found.", pageSize = 100, toolbar } = props;
54270
+ const { items, getKey, getSearchText, renderItem, getFilterType, mapFilterOption, filterOptions, filterLabel = "Type", initialTypeFilters = [], columns, renderExpanded, isExpanded, rowClassName, onRowClick, isLoading = false, searchPlaceholder = "Search", emptyLabel = "No items found.", pageSize: fixedPageSize = FIXED_PAGE_SIZE, pageSizeSelector, toolbar } = props;
54271
+ const [chosenPageSize, setChosenPageSize] = usePersistedPageSize(pageSizeSelector ?? {
54272
+ storageKey: null,
54273
+ options: [fixedPageSize],
54274
+ defaultSize: fixedPageSize
54275
+ });
54004
54276
  const [searchInput, setSearchInput] = useState("");
54005
54277
  const debouncedSearch = useDebouncedString(searchInput, 300);
54006
54278
  const [typeFilters, setTypeFilters] = useState(() => [...initialTypeFilters]);
@@ -54049,6 +54321,7 @@ function GenericModeList(props) {
54049
54321
  getFilterType
54050
54322
  ]);
54051
54323
  const totalItems = filtered.length;
54324
+ const pageSize = pageSizeSelector ? effectivePageSize(chosenPageSize, totalItems) : fixedPageSize;
54052
54325
  const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
54053
54326
  const safePage = Math.min(page, totalPages);
54054
54327
  const startIdx = (safePage - 1) * pageSize;
@@ -54132,7 +54405,23 @@ function GenericModeList(props) {
54132
54405
  }),
54133
54406
  /* @__PURE__ */ jsx("div", {
54134
54407
  className: "shrink-0",
54135
- children: /* @__PURE__ */ jsx(Pagination, {
54408
+ children: pageSizeSelector && totalItems > 0 ? /* @__PURE__ */ jsxs("div", {
54409
+ className: "flex items-center justify-between gap-4 flex-wrap pt-2 text-[11px] text-foreground-subtle",
54410
+ children: [/* @__PURE__ */ jsxs("div", {
54411
+ className: "inline-flex items-center gap-2",
54412
+ children: [totalItems <= pageSize && /* @__PURE__ */ jsxs("span", { children: [totalItems, " total"] }), /* @__PURE__ */ jsx(PageSizeSelect, {
54413
+ ariaLabel: "Rows per page",
54414
+ value: chosenPageSize,
54415
+ options: pageSizeSelector.options,
54416
+ onChange: setChosenPageSize
54417
+ })]
54418
+ }), /* @__PURE__ */ jsx(Pagination, {
54419
+ currentPage: safePage,
54420
+ totalItems,
54421
+ pageSize,
54422
+ onPageChange: setPage
54423
+ })]
54424
+ }) : /* @__PURE__ */ jsx(Pagination, {
54136
54425
  currentPage: safePage,
54137
54426
  totalItems,
54138
54427
  pageSize,
@@ -54261,6 +54550,24 @@ var DeviceItemRowSchema = z.object({
54261
54550
  });
54262
54551
  var SupportedKindsSchema = z.array(z.string());
54263
54552
  var STATUS_POLL_INTERVAL_MS = 1e4;
54553
+ /** Where the viewer's "rows per page" choice for this table is remembered. */
54554
+ var DEVICE_EXPORT_PAGE_SIZE_KEY = "camstack.device-export.pageSize";
54555
+ /**
54556
+ * The exposed-devices table's page sizes. Its own key and options, not the
54557
+ * Devices page's (25/50/100/200, default 50): the operator asked for a short
54558
+ * default here (25) and an "All" for a quick scan of every exportable device.
54559
+ */
54560
+ var DEVICE_EXPORT_PAGE_SIZE = {
54561
+ options: [
54562
+ 10,
54563
+ 25,
54564
+ 50,
54565
+ 100,
54566
+ 0
54567
+ ],
54568
+ defaultSize: 25,
54569
+ storageKey: DEVICE_EXPORT_PAGE_SIZE_KEY
54570
+ };
54264
54571
  /**
54265
54572
  * The fleet-list poll. Deliberately the same 30 s as the window
54266
54573
  * `applyFleetQueryDefaults` registers for `deviceManager.listAll` (D274) —
@@ -54345,6 +54652,35 @@ function SetupSection({ setup }) {
54345
54652
  })]
54346
54653
  });
54347
54654
  }
54655
+ /** Parse `getStatus` against the canonical cap schema (loose wire output). */
54656
+ function parseStatus(data) {
54657
+ const parsed = DeviceExportStatusSchema.safeParse(data);
54658
+ return parsed.success ? parsed.data : null;
54659
+ }
54660
+ /**
54661
+ * The exporter's error and Setup block on their own — for a page that shows
54662
+ * them above its settings while the devices table sits at the bottom. Renders
54663
+ * nothing when the exporter reports neither. Shares `getStatus`'s query key
54664
+ * with the panel, so mounting both costs one request.
54665
+ */
54666
+ function DeviceExportSetup({ addonId }) {
54667
+ const statusQuery = useDeviceExportGetStatus({
54668
+ nodeId: "hub",
54669
+ addonId
54670
+ }, {
54671
+ refetchInterval: STATUS_POLL_INTERVAL_MS,
54672
+ retry: false
54673
+ });
54674
+ const status = useMemo(() => parseStatus(statusQuery.data), [statusQuery.data]);
54675
+ if (!status?.error && !status?.setup) return null;
54676
+ return /* @__PURE__ */ jsxs("div", {
54677
+ className: "rounded-lg border border-border bg-surface overflow-hidden",
54678
+ children: [status.error && /* @__PURE__ */ jsx("div", {
54679
+ className: "px-2 sm:px-4 py-3 border-b border-border",
54680
+ children: /* @__PURE__ */ jsx(ErrorBox, { message: status.error })
54681
+ }), status.setup && /* @__PURE__ */ jsx(SetupSection, { setup: status.setup })]
54682
+ });
54683
+ }
54348
54684
  /**
54349
54685
  * Per-row Expose/Exposed toggle. Reflects whether the row's device is exposed
54350
54686
  * to THIS exporter and flips it through the SAME runner the bulk buttons use —
@@ -54423,7 +54759,7 @@ function BulkSummary({ outcome, onDismiss }) {
54423
54759
  * Generic device-export panel. Addon-agnostic — only the `device-export`
54424
54760
  * cap drives it.
54425
54761
  */
54426
- function DeviceExportPanel({ addonId, onOpenDevice }) {
54762
+ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
54427
54763
  const queryClient = useQueryClient();
54428
54764
  const statusQuery = useDeviceExportGetStatus({
54429
54765
  nodeId: "hub",
@@ -54449,10 +54785,7 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54449
54785
  }, [queryClient]);
54450
54786
  const exposeMutation = useDeviceExportExposeDevice({ onSuccess: invalidate });
54451
54787
  const unexposeMutation = useDeviceExportUnexposeDevice({ onSuccess: invalidate });
54452
- const status = useMemo(() => {
54453
- const parsed = DeviceExportStatusSchema.safeParse(statusQuery.data);
54454
- return parsed.success ? parsed.data : null;
54455
- }, [statusQuery.data]);
54788
+ const status = useMemo(() => parseStatus(statusQuery.data), [statusQuery.data]);
54456
54789
  const exposed = useMemo(() => {
54457
54790
  const parsed = ExposedDeviceArraySchema.safeParse(exposedQuery.data);
54458
54791
  return parsed.success ? parsed.data : [];
@@ -54675,11 +55008,11 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54675
55008
  ]
54676
55009
  }), loading && /* @__PURE__ */ jsx(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin text-foreground-subtle" })]
54677
55010
  }),
54678
- status?.error && /* @__PURE__ */ jsx("div", {
55011
+ showSetup && status?.error && /* @__PURE__ */ jsx("div", {
54679
55012
  className: "px-2 sm:px-4 py-3 border-b border-border",
54680
55013
  children: /* @__PURE__ */ jsx(ErrorBox, { message: status.error })
54681
55014
  }),
54682
- status?.setup && /* @__PURE__ */ jsx(SetupSection, { setup: status.setup }),
55015
+ showSetup && status?.setup && /* @__PURE__ */ jsx(SetupSection, { setup: status.setup }),
54683
55016
  outcome !== null && /* @__PURE__ */ jsx(BulkSummary, {
54684
55017
  outcome,
54685
55018
  onDismiss: dismissOutcome
@@ -54699,6 +55032,7 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54699
55032
  columns,
54700
55033
  onRowClick: onOpenDevice ? onRowClick : void 0,
54701
55034
  toolbar: selectionToolbar,
55035
+ pageSizeSelector: DEVICE_EXPORT_PAGE_SIZE,
54702
55036
  searchPlaceholder: "Search by name or stableId",
54703
55037
  emptyLabel: "No device matches this search."
54704
55038
  }) : /* @__PURE__ */ jsx(EmptyState, {
@@ -54724,145 +55058,6 @@ function DeviceGrid({ children, minCardWidth = 220, gap = 3, className }) {
54724
55058
  });
54725
55059
  }
54726
55060
  //#endregion
54727
- //#region src/composites/icon-picker.tsx
54728
- /**
54729
- * icon-picker — the ONE control for choosing an icon, wherever admin-ui asks.
54730
- *
54731
- * There were two: a device one over `DISPLAY_ICON_REGISTRY` and a room one
54732
- * over `LOCATION_ICON_IDS`, identical apart from the vocabulary they walked.
54733
- * That was tolerable while the device set was 56 glyphs in a flat grid. It is
54734
- * not at 157: a picker that only lets the operator HUNT is a picker they stop
54735
- * using, and the two copies were already drifting — only one of them had an
54736
- * i18n'd label.
54737
- *
54738
- * So the vocabulary, the grouping and the search all come from
54739
- * `@camstack/types`' `display-icon-vocabulary`, which is the same module the
54740
- * viewer's sheet searches. What is left here is the web half: a popover, a
54741
- * grid, and a text field.
54742
- *
54743
- * ## What it does NOT own
54744
- *
54745
- * - **The glyphs.** `resolveGlyph` is a prop, because this package's table is
54746
- * `lucide-react` and the viewer's is `lucide-react-native`. A key this build
54747
- * cannot draw is SKIPPED rather than rendered as a hole — an empty tile
54748
- * reads as a rendering bug, not as a missing choice.
54749
- * - **The words.** Every string is a prop. This package has no i18n (one file
54750
- * in it imports react-i18next, and that is the exception), and a control
54751
- * that reached for a translation would force it on every consumer.
54752
- * - **The write.** `onPick` is called and that is all; whether it is a form
54753
- * value or an immediate mutation is the caller's business, as it already was
54754
- * in both pickers this replaces.
54755
- *
54756
- * ## The clear tile is never filtered away
54757
- *
54758
- * The "inherit" / "no icon" choice sits above the sections and ignores the
54759
- * search. A query that hid it would strand the operator on a glyph they picked
54760
- * by accident with no way back to the default — the one action a picker must
54761
- * always offer.
54762
- */
54763
- function IconPicker({ value, onPick, resolveGlyph, fallbackGlyph, labels, only, extraWords, clearGlyph, labelFor, disabled = false }) {
54764
- const [open, setOpen] = useState(false);
54765
- const [query, setQuery] = useState("");
54766
- const allowed = useMemo(() => only === void 0 ? null : new Set(only), [only]);
54767
- const sections = useMemo(() => {
54768
- const hits = searchDisplayIcons(query, extraWords);
54769
- const byGroup = /* @__PURE__ */ new Map();
54770
- for (const hit of hits) {
54771
- const { key, group } = hit.entry;
54772
- if (allowed !== null && !allowed.has(key)) continue;
54773
- if (resolveGlyph(key) === void 0) continue;
54774
- const bucket = byGroup.get(group);
54775
- if (bucket === void 0) byGroup.set(group, [key]);
54776
- else bucket.push(key);
54777
- }
54778
- return DISPLAY_ICON_GROUPS.flatMap((group) => {
54779
- const keys = byGroup.get(group);
54780
- return keys === void 0 || keys.length === 0 ? [] : [{
54781
- group,
54782
- keys
54783
- }];
54784
- });
54785
- }, [
54786
- query,
54787
- extraWords,
54788
- allowed,
54789
- resolveGlyph
54790
- ]);
54791
- const Current = (value === null ? void 0 : resolveGlyph(value)) ?? fallbackGlyph;
54792
- const choose = async (next) => {
54793
- setOpen(false);
54794
- setQuery("");
54795
- await onPick(next);
54796
- };
54797
- const Clear = clearGlyph ?? fallbackGlyph;
54798
- return /* @__PURE__ */ jsxs("div", {
54799
- className: "relative flex shrink-0 items-center gap-2",
54800
- children: [/* @__PURE__ */ jsx("button", {
54801
- type: "button",
54802
- disabled,
54803
- onClick: () => setOpen((o) => !o),
54804
- "aria-expanded": open,
54805
- "aria-label": labels.trigger,
54806
- title: labels.trigger,
54807
- className: "rounded border border-border p-1 text-foreground-muted hover:bg-surface-hover disabled:opacity-50",
54808
- children: /* @__PURE__ */ jsx(Current, { className: "h-4 w-4" })
54809
- }), open && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
54810
- type: "button",
54811
- "aria-label": labels.close,
54812
- onClick: () => setOpen(false),
54813
- className: "fixed inset-0 z-10 cursor-default"
54814
- }), /* @__PURE__ */ jsxs("div", {
54815
- role: "group",
54816
- "aria-label": labels.panel,
54817
- className: "absolute left-0 top-full z-20 mt-1 max-h-80 w-72 overflow-y-auto rounded-md border border-border bg-surface p-2 shadow-lg",
54818
- children: [
54819
- /* @__PURE__ */ jsx("input", {
54820
- type: "search",
54821
- value: query,
54822
- autoFocus: true,
54823
- onChange: (e) => setQuery(e.target.value),
54824
- placeholder: labels.searchPlaceholder,
54825
- "aria-label": labels.searchPlaceholder,
54826
- className: "mb-2 w-full rounded border border-border bg-surface-raised px-2 py-1 text-xs text-foreground placeholder:text-foreground-subtle"
54827
- }),
54828
- /* @__PURE__ */ jsxs("button", {
54829
- type: "button",
54830
- onClick: () => void choose(null),
54831
- "aria-pressed": value === null,
54832
- className: "mb-2 flex w-full items-center gap-1.5 rounded px-1.5 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover",
54833
- children: [/* @__PURE__ */ jsx(Clear, { className: "h-3.5 w-3.5" }), labels.clear]
54834
- }),
54835
- sections.length === 0 ? /* @__PURE__ */ jsx("p", {
54836
- className: "px-1.5 py-2 text-[11px] text-foreground-subtle",
54837
- children: labels.noMatches
54838
- }) : sections.map(({ group, keys }) => /* @__PURE__ */ jsxs("section", {
54839
- className: "mb-2 last:mb-0",
54840
- children: [/* @__PURE__ */ jsx("h4", {
54841
- className: "px-1.5 pb-1 text-[10px] uppercase tracking-wide text-foreground-subtle",
54842
- children: labels.groups[group]
54843
- }), /* @__PURE__ */ jsx("div", {
54844
- className: "grid grid-cols-7 gap-1",
54845
- children: keys.map((key) => {
54846
- const Glyph = resolveGlyph(key);
54847
- if (Glyph === void 0) return null;
54848
- const selected = value === key;
54849
- return /* @__PURE__ */ jsx("button", {
54850
- type: "button",
54851
- onClick: () => void choose(key),
54852
- "aria-pressed": selected,
54853
- "aria-label": labelFor?.(key) ?? key,
54854
- title: labelFor?.(key) ?? key,
54855
- className: selected ? "flex items-center justify-center rounded border border-primary bg-primary/10 p-1.5 text-foreground" : "flex items-center justify-center rounded border border-transparent p-1.5 text-foreground-muted hover:bg-surface-hover",
54856
- children: /* @__PURE__ */ jsx(Glyph, { className: "h-4 w-4" })
54857
- }, key);
54858
- })
54859
- })]
54860
- }, group))
54861
- ]
54862
- })] })]
54863
- });
54864
- }
54865
- //#endregion
54866
55061
  //#region src/composites/device-selector-picker.tsx
54867
55062
  /**
54868
55063
  * <DeviceSelectorPicker> — the operator-facing editor for a v3
@@ -60996,4 +61191,4 @@ var SceneMonitorEditor = lazy(() => import("./SceneMonitorEditor-vfWTm6qv.js").t
60996
61191
  /** Lazy-wrapped `PrivacyMaskSettings` — code-split off the main bundle. */
60997
61192
  var PrivacyMaskSettings = lazy(() => import("./PrivacyMaskSettings-DMkchqO1.js").then((m) => ({ default: m.PrivacyMaskSettings })));
60998
61193
  //#endregion
60999
- export { AddonGlobalSettingsForm, AdminLinkQuickAction, AgentStepEditor, AlarmHeroCard, AlarmInlineControl as AlarmPanelInlineControl, AppShell, ArcKnob, AudioClassificationList, AudioLevelWaveform, AudioWaveform, AutomationHeroCard, AutomationInlineControl, AutotrackSection, BTN_COMPACT, BTN_COMPACT_DANGER, BTN_COMPACT_PRIMARY, BTN_COMPACT_WARNING, BackToStepsButton, Badge, BatteryBadge, BottomSheet, Breadcrumb, BrightnessPanel, Button, ButtonControl, ButtonHeroCard, CARD_MODE_MIN_COLUMNS, CENTER, CHIP_ACTIVE, CHIP_BASE, CHIP_INACTIVE, CLASS_COLORS, COLUMN_BREAKPOINT_CLASS, COLUMN_PRIORITY, COMMIT_DEDUPE_TOLERANCE_MS, COMMIT_DEDUPE_WINDOW_MS, CONTROL_CAP_NAMES, CONTROL_FILLS, CameraStreamPlayer, Card, Checkbox, ChildSectionAccordion, ClimatePanel, CodeBlock, CollapsibleCard, ConfigFormBuilder, FormField as ConfigFormField, ConfigNotificationTargetMultiSelectFieldView, ConfigNotificationTargetSelectFieldView, ConfigSchemaField, ConfirmActionButton, ConfirmDialogProvider, ConsumablesPanel, ContainerChildrenProvider, ContainerPrimaryHero, ControlColumn, ControlHeroCard, ControlInlineControl, ControlPanel, CopyButton, CoverHeroCard, CoverInlineControl, CoverPanel, CustomFieldRenderersProvider, DEFAULT_COLOR, DEVICE_COLUMNS, DEVICE_LIST_PAGE_SIZE_KEY, DEVICE_LIST_PAGE_SIZE_OPTIONS, DEVICE_PICKER_GROUP_STORAGE_KEY, DEVICE_ROLE_META, DEVICE_TYPE_CONTROL, DEVICE_TYPE_META, DISPLAY_ICON_REGISTRY, DataTable, DataTableFilterBar, DetectionCanvas, DetectionOverlay, DetectionResultTree, DevShell, DeviceActivityPanel, DeviceBatchToolbar, DeviceCard, DeviceContextProvider, DeviceExportPanel, DeviceFilterBar, DeviceGrid, DeviceItem, DeviceList, DeviceMultiSelectField, DevicePicker, DeviceQuickDetailProvider, DeviceSelectField, DeviceSelectorPicker, DeviceStepMatrix, DevicesFailedLine, DevicesLoadingLine, DevicesWidenWarning, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DiscoveryPanel, DoorbellRecentPanel, Dropdown, DropdownContent, DropdownItem, DropdownTrigger, DummyHeroCard, DummyInline, EMPTY_DATA_TABLE_FILTER, EVENT_KIND_ICONS, EmptyState, EndpointResultsCard, EndpointTestCard, ErrorBox, EventKindGlyph, EventStream, ExtensionsPanel, FILL, FLEET_LIST_GC_TIME_MS, FLEET_LIST_QUERY_KEY, FLEET_LIST_STALE_TIME_MS, FanHeroCard, FanInlineControl, FanPanel, FilterBar, FloatingEventStream, FloatingLogStream, FloatingPanel, FormField$1 as FormField, GRID_GAP, GRID_PAIRED, GRID_QUICK_STATS, GateBadge, GateControl, GripTrack, GroupedModelSelector, HOST_WIDGETS, HUB_SESSION_RUN_MS, HlsVideo, HoverZoomImage, HubSessionTestCard, HumidifierHeroCard, HumidifierInlineControl, INPUT_COMPACT, INPUT_SHELL, IconAction, IconButton, IconPicker, ImageHeroCard, ImageInlineControl, ImageSelector, InferenceConfigSelector, Input, KebabMenu, KeyValueList, LIST_ROW, Label, LawnMowerHeroCard, LawnMowerInlineControl, LayeredSettingsForm, LightHeroCard, LightInlineControl, LockHeroCard, LockInlineControl, LockPanel, LogStream, LoginForm, MODE_COLOR, MaskShapeCanvas, MediaPlayerHeroCard, MediaPlayerInlineControl, MediaPlayerPanel, MobileDrawer, ModelPicker, MotionZonesSettings, NAVIGATION_DRIVE_INTERVAL_MS, NatResultCard, NavigationPanel, NetworkLinkBadge, NodeMultiSelectField, NodePicker, NodeSelectField, NotificationTargetSelectField, NotificationTargetsField, OPTION_SELECT_SEARCH_MIN_OPTIONS, OfflineBadge, OptionSelect, PHASE_CONFIG, PICKER_CHIP_LIMIT, PICKER_DEFAULT_GROUP_BY, PICKER_GROUP_BY_MODES, PICKER_GROUP_COLLAPSE_AT, PICKER_NONE_KEY, PICKER_NOUN_PLURAL, PICKER_NO_FILTERS, PICKER_PAGE_SIZE, PICKER_SEARCH_DEBOUNCE_MS, PICKER_WINDOW, PRIORITY, PTZOverlay, PageHeader, PhaseIcon, PipelineBuilder, PipelineRuntimeSelector, PipelineStep, PlaceholderChipField, PlayerOverlaysProvider, Popover, PopoverContent, PopoverRowAction, PopoverTrigger, PrimaryChildPicker, PrivacyMaskSettings, ProviderBadge, PtzPanel, QrCode, RECONNECT_POLICY, RECORDED_PLAYBACK_MODES, RIGHT, ROI_AREA_WARN_FRACTION, ROLE_DESCRIPTOR, RadialGauge, RecordedPlaybackProvider, RecordingPanel, ResponseLog, RoleDisplayDefaultsFromServer, RoleDisplayDefaultsProvider, RootModelSourceControl, RootSuppliedModelNote, RunReport, SCENE_CAPTURE_VARIANTS, SECTION_BODY, SECTION_CARD, SECTION_HEADER, SETTING_ROW, SETTING_ROW_LABEL, SETTING_ROW_STACK_BREAKPOINT, SETTING_ROW_VALUE, SETTING_ROW_VALUE_TEXT, SNAPSHOT_MEDIA_PATH, SPLIT_PANEL_OUTER, SPLIT_PANEL_SIDE, STACK_GAP, STATE_COLOR, STEP_DOCK_WIDTH_PX, SceneMonitorEditor, ScopePicker, ScriptHeroCard, ScriptInlineControl, ScrollArea, SectionCard, SegmentedChoice, Select, SelectedChip, SemanticBadge, SensorHeroCard, SensorInlineControl, SensorValueAtom, Separator, SettingRow, Sidebar, SidebarItem, Skeleton, SlideToggle, SnapshotButton, SortHeaderButton, StatCard, StateValuesStream, StatusBadge, StepDetailDock, StepInspectorPanel, StepModelRow, StepTimings, Stepper, StreamBrokerSelector, StreamDebugOverlay, StreamPanel, Switch, SwitchHeroCard, SwitchInlineControl, SwitchPanel, SystemProvider, TABLE_SCROLL_MAX_H, TEXT_FIELD_LABEL, TEXT_HINT, TEXT_METRIC, TEXT_SECTION_LABEL, TEXT_VALUE, TIMEZONES, Tabs, TabsContent, TabsList, TabsTrigger, TapToggle, ThemeProvider, ThermostatHeroCard, ThermostatInlineControl, TimezoneSelector, Tooltip, TooltipContent, TooltipTrigger, UnitPicker, VacuumHeroCard, VacuumInlineControl, ValueReadout, ValveHeroCard, ValveInlineControl, VersionBadge, VodPlaybackProvider, WaterHeaterHeroCard, WaterHeaterInlineControl, WeatherHeroCard, WeatherInlineControl, WidgetMetricCard, WidgetPanel, WidgetRegistryProvider, WidgetSlot, ZoneEditingProvider, MicOff as a, acceleratorLabel, acceleratorShortLabel, agentColumnKey, allDeviceTypeFilterOptions, applyFleetQueryDefaults, applyPickerFilters, ariaSortForColumn, badgeFor, bindsToModel, buildPeerConnectionConfig, buildStepTreeFromSchema, EyeOff as c, catalogHasDraft, childEntityId, childListName, chipOverflow, classifyAddressScope, classifyCapability, classifySessionVerdict, clearPickerGroup, cn, columnsForContext, compareSrflxMappings, containerChildToRef, countableDevices, coverHighlight, coverageLine, createSharedContext, createTheme, cursorFractionFor, darkColors, dataTableFacets, defaultOfField, defaultTheme, deriveDeviceKind, deriveWebrtcDebugStats, describeDeviceSelector, deviceIntegrationResolver, deviceListStateOf, deviceMatchesFilter, deviceOptionLabel, deviceRoleMeta, deviceRoleMetaOf, deviceTypeLabel, deviceTypeMeta, deviceTypeMetaOf, devicesToOptions, dockBox, draftFromQuery, emptyDevicesWidens, emptySessionRun, ensureMfHostInit, eventKindLabel, filterDataTableRows, filterDeviceOptions, filterOptions as filterOptionSelectOptions, filterPickerDevices, finalizeSessionRun, findTimezone, flattenPickerGrouping, flattenPickerRows, fleetViewOf, formatControlDateTime, formatLastSeen, formatNumeric, formatSettingValue, freezeVerdict, fuzzyMatch, getClassColor, getPhaseVisual, groupAgentColumns, groupChildrenByLayout, groupPickerDevices, groupPickerDevicesBy, hardwareLabel, hubOrigin, hubPath, hubUrl, hubWsUrl, humanDuration, humidifierTint, Mic as i, initialScrubState, insertAtSelection, isAbsentProvider, isDataTableFilterActive, isFieldVisible, isGroupExpanded, isValueField, Camera as l, lawnMowerActivityMeta, lightColors, loadRemoteBundle, makeScrubBridge, metadataEntries, metadataString, mirror, mountAddonPage, mountPath, Square as n, nextGridPosition, nextReconnectAction, nextSort, nextSortDirection, nextTableSort, normalizeForSearch, ImageOff as o, offListSelections, openTopLayer, optionSearchText as optionSelectSearchText, orphanDeviceLabel, overrideEntityIdFromLink, pageRows, parentDetailOf, parseIceCandidate, parsePickerGroupBy, parseRecordedServerMessage, partitionSelected as partitionOptionSelectSelected, partitionSuggested, pickerFacetFilterShown, pickerFacetKey, pickerFacetOptions, pickerFiltersActive, pickerGroupKey, pickerHiddenSelectedCount, pickerLocationKey, pickerQueryTokens, pickerRowFacts, pickerSearchText, pickerZoneFilterSuspended, pinnedStreamChoices, providerIcons, RotateCcw as r, readStoredGroupBy, reduceSessionRun, resetHubMountForTests, resetPickerFilters, resolveContainerPrimary, resolveControlAlign, resolveDeviceControl, resolveDeviceIntegration, resolveDisplayIcon, resolveEffectiveDefaultModel, resolveEffectiveStepModel, resolveEventKindIcon, resolveSearchable as resolveOptionSelectSearchable, resolveParentNames, resolvePrimaryChild, resolveSelectedDevices, resolveSensorDisplay, resolveStepDefaultModel, resolveTableLayout, roiTooLarge, rootSourceState, routerBasename, Grid2x2 as s, sameLocationSuggestions, scrubReducer, searchSteps, selectPickerGroup, selectedDeviceOptions, serializeRecordedCommand, settingsModelIdFor, shouldCommit, shouldEmit, shouldShowAddNewFooter, shouldShowUseQueryRow, shouldUseSingleNode, showsNoCamerasHint, sortRows, sortRowsByColumn, statusIcons, statusLine, stepFieldDomId, stepHasModelForFormat, stepLocalFields, stepModelOptions, stripParentNamePrefix, summarizeHubCandidates, Trash2 as t, tabForSelector, tankAlert, themeToCss, toStationaryOverlayBox, toggleDataTableFacet, togglePickerDevice, togglePickerKey, trpc, typeLabel, createLucideIcon as u, useAccessoriesGetStatus, useAccessoriesSetChildHidden, useAddonPagesListPages, useAddonSettingsGetDeviceSettings, useAddonSettingsGetGlobalSettings, useAddonSettingsGetIntegrationSettings, useAddonSettingsUpdateDeviceSettings, useAddonSettingsUpdateGlobalSettings, useAddonWidgetsListWidgets, useAddonsApplyAutoUpdateToAll, useAddonsCancelJob, useAddonsCustom, useAddonsForceRefresh, useAddonsGetAddonAutoUpdate, useAddonsGetAutoUpdateSettings, useAddonsGetJob, useAddonsGetLastRestart, useAddonsGetLogs, useAddonsGetVersions, useAddonsInstallFromWorkspace, useAddonsInstallPackage, useAddonsIsWorkspaceAvailable, useAddonsList, useAddonsListCapabilityProviders, useAddonsListFrameworkPackages, useAddonsListInstallations, useAddonsListJobs, useAddonsListPackages, useAddonsListUpdates, useAddonsListWorkspacePackages, useAddonsOnAddonLogs, useAddonsReloadPackages, useAddonsRestartAddon, useAddonsRestartServer, useAddonsRetryLoad, useAddonsRollbackPackage, useAddonsSearchAvailable, useAddonsSetAddonAutoUpdate, useAddonsSetAutoUpdateSettings, useAddonsSetCapabilityProviderEnabled, useAddonsStartJob, useAddonsUninstallPackage, useAddonsUpdatePackage, useAirQualitySensorGetStatus, useAlarmPanelArm, useAlarmPanelDisarm, useAlarmPanelGetStatus, useAlarmPanelTrigger, useAlert, useAlertsDismiss, useAlertsEmit, useAlertsGetUnreadCount, useAlertsList, useAlertsMarkAllRead, useAlertsMarkRead, useAlertsUpdate, useAllWidgets, useAmbientLightSensorGetStatus, useAudioAnalysisApplyDeviceSettingsPatch, useAudioAnalysisGetDeviceLiveContribution, useAudioAnalysisGetDeviceSettingsContribution, useAudioAnalysisResolveDeviceSettings, useAudioAnalyzerAnalyseChunk, useAudioAnalyzerAttachDevice, useAudioAnalyzerClassify, useAudioAnalyzerDetachDevice, useAudioAnalyzerDispose, useAudioAnalyzerIsReady, useAudioAnalyzerReprobeAudioEngine, useAudioCodecCanHandle, useAudioCodecCloseSession, useAudioCodecCreateDecodeSession, useAudioCodecCreateEncodeSession, useAudioCodecFlushEncode, useAudioCodecListActiveSessions, useAudioCodecListSupportedCodecs, useAudioCodecPullEncoded, useAudioCodecPullPcm, useAudioCodecPushEncodedFrame, useAudioCodecPushPcm, useAudioMetricsGetCurrentSnapshot, useAudioMetricsGetHistory, useAutomationControlDisable, useAutomationControlEnable, useAutomationControlGetStatus, useAutomationControlTrigger, useBackupCancel, useBackupDelete, useBackupDeleteSchedule, useBackupGetEntries, useBackupList, useBackupListArchives, useBackupListDestinations, useBackupListLocations, useBackupListRuns, useBackupListSchedules, useBackupPreviewSchedule, useBackupRestore, useBackupTrigger, useBackupUpsertDestinationPolicy, useBackupUpsertSchedule, useBatteryGetStatus, useBatteryWakeForStream, useBinaryGetStatus, useBrightnessGetStatus, useBrightnessSetBrightness, useBrokerAdd, useBrokerGet, useBrokerGetBrokerConfig, useBrokerGetSettings, useBrokerGetSettingsSchema, useBrokerGetState, useBrokerGetStatus, useBrokerList, useBrokerListProviders, useBrokerPublish, useBrokerRemove, useBrokerSetSettings, useBrokerSubscribe, useBrokerTestConnection, useBrokerTestSettings, useBrokerUnsubscribe, useButtonPress, useCalendarsGetPreferences, useCalendarsListCalendars, useCalendarsListEvents, useCalendarsSetCalendarPreference, useCameraCredentialsGetCredentials, useCameraCredentialsGetStatus, useCameraGridLayoutApplyDeviceSettingsPatch, useCameraGridLayoutGetDeviceLiveContribution, useCameraGridLayoutGetDeviceSettingsContribution, useCameraGridLayoutGetLayout, useCameraGridLayoutSaveLayout, useCameraPipelineConfigApplyDeviceSettingsPatch, useCameraPipelineConfigGetDeviceLiveContribution, useCameraPipelineConfigGetDeviceSettingsContribution, useCameraStreamsGetBrokerStreams, useCameraStreamsGetCameraStreams, useCameraStreamsGetProfileRtspEntries, useCameraStreamsGetRtspEntries, useCameraStreamsPickStream, useCapabilityPresence, useCarbonMonoxideGetStatus, useClimateControlGetStatus, useClimateControlSetFanMode, useClimateControlSetMode, useClimateControlSetPreset, useClimateControlSetSwingHorizontal, useClimateControlSetSwingVertical, useClimateControlSetTarget, useClimateControlSetTargetHumidity, useClimateControlSetTargetRange, useClusterNodes, useClusterTopology, useColorGetStatus, useColorSetColor, useConfirm, useConnectionTestDescribeTest, useConnectionTestTestSettings, useConnectivityGetStatus, useConsumablesGetStatus, useConsumablesReset, useContactGetStatus, useContainerChildren, useControlGetStatus, useControlSetValue, useCoreBlocksCompile, useCoreBlocksCreate, useCoreBlocksCreateComposition, useCoreBlocksDelete, useCoreBlocksGet, useCoreBlocksGetBlockSettings, useCoreBlocksGetComposition, useCoreBlocksGetCustomization, useCoreBlocksGetTypeDefs, useCoreBlocksList, useCoreBlocksListComposableCaps, useCoreBlocksListCompositions, useCoreBlocksRestart, useCoreBlocksSetEnabled, useCoreBlocksUpdate, useCoreBlocksUpdateBlockSettings, useCoreBlocksUpdateComposition, useCoreBlocksValidateComposition, useCoverClose, useCoverGetStatus, useCoverOpen, useCoverSetPosition, useCoverSetTiltPosition, useCoverStop, useCustomFieldRenderer, useDataTableFilter, useDayNightGetOptions, useDayNightGetStatus, useDayNightSetSettings, useDebouncedString, useDecoderGetInfo, useDecoderReprobeHwaccel, useDetectionPipelineApplyDeviceSettingsPatch, useDetectionPipelineGetDeviceLiveContribution, useDetectionPipelineGetDeviceSettingsContribution, useDevShell, useDevice, useDeviceAdminLink, useDeviceAdminLinkGetAdminLink, useDeviceAdoptionAdopt, useDeviceAdoptionGetCandidate, useDeviceAdoptionGetStatus, useDeviceAdoptionListCandidateFilters, useDeviceAdoptionListCandidates, useDeviceAdoptionRefresh, useDeviceAdoptionRelease, useDeviceAdoptionResync, useDeviceAutotrack, useDeviceBattery, useDeviceCapSlice, useDeviceCapability, useDeviceDetections, useDeviceDiscoveryAdoptDevice, useDeviceDiscoveryGetStatus, useDeviceDiscoveryListDiscovered, useDeviceDiscoveryRefreshDiscovery, useDeviceDiscoveryReleaseDevice, useDeviceExportApplyDeviceSettingsPatch, useDeviceExportExposeDevice, useDeviceExportGetDeviceLiveContribution, useDeviceExportGetDeviceSettingsContribution, useDeviceExportGetStatus, useDeviceExportListExposedDevices, useDeviceExportListSupportedDeviceKinds, useDeviceExportUnexposeDevice, useDeviceExtensionListCandidates, useDeviceExtensionListSlots, useDeviceExtensionListSources, useDeviceExtensionSetSlot, useDeviceId, useDeviceListPageSize, useDeviceManagerAddLocation, useDeviceManagerAdoptDevice, useDeviceManagerAdoptionAdopt, useDeviceManagerAdoptionCancelJob, useDeviceManagerAdoptionListCandidateFilters, useDeviceManagerAdoptionListCandidates, useDeviceManagerAdoptionListJobs, useDeviceManagerAdoptionRefresh, useDeviceManagerAdoptionRelease, useDeviceManagerAdoptionResync, useDeviceManagerAdoptionStartJob, useDeviceManagerAllocateDeviceId, useDeviceManagerApplyInitialMeta, useDeviceManagerCreateDevice, useDeviceManagerDisable, useDeviceManagerDiscoverAllProviders, useDeviceManagerDiscoverDevices, useDeviceManagerDiscoverProvider, useDeviceManagerDiscoveryProviders, useDeviceManagerEnable, useDeviceManagerGetAllBindings, useDeviceManagerGetBindings, useDeviceManagerGetBindingsBatch, useDeviceManagerGetChildren, useDeviceManagerGetChildrenBatch, useDeviceManagerGetConfigSchema, useDeviceManagerGetCreationSchema, useDeviceManagerGetDevice, useDeviceManagerGetDeviceAggregate, useDeviceManagerGetDeviceLiveInfoAggregate, useDeviceManagerGetDeviceSettingsAggregate, useDeviceManagerGetDeviceStatusAggregate, useDeviceManagerGetDeviceStatusAggregateBatch, useDeviceManagerGetLinkedDevices, useDeviceManagerGetLinkedDevicesBatch, useDeviceManagerGetRoleDisplayDefaults, useDeviceManagerGetSettingsSchema, useDeviceManagerGetStreamProfileMap, useDeviceManagerGetStreamSources, useDeviceManagerGetWireableFields, useDeviceManagerGetWrapperActivation, useDeviceManagerListAll, useDeviceManagerListBindableCapsForDeviceType, useDeviceManagerListDeviceIdsByCaps, useDeviceManagerListLocationDescriptions, useDeviceManagerListLocationIcons, useDeviceManagerListLocations, useDeviceManagerListPersistedByAddon, useDeviceManagerListSiteZones, useDeviceManagerListWrappersForCap, useDeviceManagerLoadConfig, useDeviceManagerLoadMeta, useDeviceManagerLoadRuntimeState, useDeviceManagerMigrateDevice, useDeviceManagerPersistConfig, useDeviceManagerProbeStreams, useDeviceManagerProviderCreationType, useDeviceManagerProviderDiscoveryParamsSchema, useDeviceManagerRegisterDevice, useDeviceManagerRemove, useDeviceManagerRemoveByIntegration, useDeviceManagerRemoveDevice, useDeviceManagerRemoveLocation, useDeviceManagerRemoveSiteZone, useDeviceManagerRenameLocation, useDeviceManagerRenameSiteZone, useDeviceManagerRunDeviceAction, useDeviceManagerSetChildLayout, useDeviceManagerSetDisabled, useDeviceManagerSetDisplay, useDeviceManagerSetIntegrationId, useDeviceManagerSetLinkDeviceId, useDeviceManagerSetLocation, useDeviceManagerSetLocationDescription, useDeviceManagerSetLocationIcon, useDeviceManagerSetLocationZone, useDeviceManagerSetMetadata, useDeviceManagerSetName, useDeviceManagerSetPrimaryChildEntityId, useDeviceManagerSetRole, useDeviceManagerSetRoleDisplayDefaults, useDeviceManagerSetSiteZone, useDeviceManagerSetStreamProfileMap, useDeviceManagerSetType, useDeviceManagerSetWrapperActive, useDeviceManagerTestCreationField, useDeviceManagerTestField, useDeviceManagerUpdateConfig, useDeviceManagerUpdateDeviceField, useDeviceManagerUpdateDeviceFieldsBatch, useDeviceNetworkLink, useDeviceOpsGetConfigEntries, useDeviceOpsGetRawState, useDeviceOpsGetSettingsSchema, useDeviceOpsGetStreamSources, useDeviceOpsRemoveDevice, useDeviceOpsRunAction, useDeviceOpsSetConfig, useDeviceProviderAdoptDiscoveredDevice, useDeviceProviderCreateDevice, useDeviceProviderDiscoverDevices, useDeviceProviderGetChildCreationSchema, useDeviceProviderGetDevices, useDeviceProviderGetDiscoveryParamsSchema, useDeviceProviderGetManualCreationType, useDeviceProviderGetStatus, useDeviceProviderReloadDevice, useDeviceProviderStart, useDeviceProviderStop, useDeviceProviderSupportsDiscovery, useDeviceProviderSupportsManualCreation, useDeviceProviderTestCreationField, useDeviceProxy, useDeviceQuickDetailOpen, useDeviceSnapshot, useDeviceSnapshotImage, useDeviceState, useDeviceStateClaimFields, useDeviceStateGetAllSnapshots, useDeviceStateGetCapSlice, useDeviceStateGetNativeCapSlice, useDeviceStateGetSnapshot, useDeviceStateListClaims, useDeviceStatePatchOwnedFields, useDeviceStateReleaseClaim, useDeviceStateSetCapSlice, useDeviceStateSlice, useDeviceStatusGetStatus, useDeviceWebrtc, useDevices, useDoorbellEvents, useDoorbellGetStatus, useEnumSensorGetStatus, useEventEmitterGetStatus, useEventInvalidation, useEventStreamLatest, useEventStreamMap, useFaceGalleryAssignFace, useFaceGalleryAssignFaces, useFaceGalleryAuditIdentitySamples, useFaceGalleryCreateIdentity, useFaceGalleryDeleteFace, useFaceGalleryDeleteIdentity, useFaceGalleryGetFaceByTrack, useFaceGalleryGetFaceMedia, useFaceGalleryListIdentities, useFaceGalleryListIdentitySamples, useFaceGalleryListRecentFaces, useFaceGalleryRemoveSample, useFaceGalleryRenameIdentity, useFaceGallerySuggestFaceClusters, useFaceGalleryUnassignFace, useFaceGalleryUnassignFaces, useFanControlGetStatus, useFanControlSetDirection, useFanControlSetOscillating, useFanControlSetPercentage, useFanControlSetPreset, useFeatureProbeGetStatus, useFloodGetStatus, useGasGetStatus, useHumidifierGetStatus, useHumidifierSetMode, useHumidifierSetOn, useHumidifierSetTargetHumidity, useHumiditySensorGetStatus, useImageGetStatus, useImageSettingsGetOptions, useImageSettingsGetStatus, useImageSettingsSetSettings, useIntegrationsCreate, useIntegrationsDelete, useIntegrationsGet, useIntegrationsGetAvailableTypes, useIntegrationsGetByAddonId, useIntegrationsGetSettings, useIntegrationsList, useIntegrationsSetSettings, useIntegrationsTestConnection, useIntegrationsUpdate, useIntercomEndTalkSession, useIntercomGetStatus, useIntercomHandleAnswer, useIntercomPushTalkAudio, useIntercomStartSession, useIntercomStartTalkSession, useIntercomStopSession, useIsMidWidth, useIsMobile, useLawnMowerControlDock, useLawnMowerControlGetStatus, useLawnMowerControlPause, useLawnMowerControlStartMowing, useLiveBuffer, useLiveEvent, useLlmCancel, useLlmDeleteModel, useLlmDeleteProfile, useLlmGenerate, useLlmGenerateVision, useLlmGetDefaults, useLlmGetRuntimeStatus, useLlmGetUsage, useLlmInstallModel, useLlmListModelCatalog, useLlmListModels, useLlmListNodeModels, useLlmListProfileKinds, useLlmListProfiles, useLlmListRuntimeNodes, useLlmResolveModelRef, useLlmSetDefault, useLlmStartRuntime, useLlmStopRuntime, useLlmTestProfile, useLlmUpsertProfile, useLocalNetworkAddCustomAddress, useLocalNetworkDownloadCa, useLocalNetworkGetAddresses, useLocalNetworkGetAllowedAddresses, useLocalNetworkGetConnectionEndpoints, useLocalNetworkGetHubAddresses, useLocalNetworkGetNotificationEndpoint, useLocalNetworkGetPreferred, useLocalNetworkGetTlsStatus, useLocalNetworkGetViewerEndpoints, useLocalNetworkList, useLocalNetworkRegenerateCertificate, useLocalNetworkRemoveCustomAddress, useLocalNetworkResetAllowlistToBestMatch, useLocalNetworkRevertToGeneratedCertificate, useLocalNetworkSetAddressRole, useLocalNetworkSetAllowedAddresses, useLocalNetworkSetNotificationEndpoint, useLocalNetworkSetViewerEndpoints, useLocalNetworkUploadCertificate, useLockControlGetStatus, useLockControlLock, useLockControlOpen, useLockControlUnlock, useMediaPlayerGetStatus, useMediaPlayerNext, useMediaPlayerPause, useMediaPlayerPlay, useMediaPlayerPlayMedia, useMediaPlayerPrevious, useMediaPlayerSeek, useMediaPlayerSelectSource, useMediaPlayerSetMute, useMediaPlayerSetRepeat, useMediaPlayerSetShuffle, useMediaPlayerSetVolume, useMediaPlayerStop, useMeshNetworkGetStatus, useMeshNetworkJoin, useMeshNetworkLeave, useMeshNetworkListPeers, useMeshNetworkLogout, useMeshNetworkStartLogin, useMeshNetworkTestConnection, useMetricsProviderCollectSnapshot, useMetricsProviderGetAddonStats, useMetricsProviderGetCached, useMetricsProviderGetCpuTemperature, useMetricsProviderGetCurrent, useMetricsProviderGetDiskSpace, useMetricsProviderGetGpuInfo, useMetricsProviderGetLoadSeries, useMetricsProviderGetProcessStats, useMetricsProviderListAddonInstances, useMetricsProviderListNodeProcesses, useMotionDetectionAnalyze, useMotionDetectionApplyDeviceSettingsPatch, useMotionDetectionGetDeviceLiveContribution, useMotionDetectionGetDeviceSettingsContribution, useMotionDetectionRemoveCamera, useMotionDetectionReset, useMotionGetStatus, useMotionIsDetected, useMotionTriggerGetStatus, useMotionTriggerSetMotionTrigger, useMotionZonesGetOptions, useMotionZonesGetStatus, useMotionZonesSetZone, useMqttBrokerAddBroker, useMqttBrokerGetBrokerConfig, useMqttBrokerGetStatus, useMqttBrokerListBrokers, useMqttBrokerRemoveBroker, useMqttBrokerStartEmbeddedBroker, useMqttBrokerStopEmbeddedBroker, useMqttBrokerTestConnection, useNativeObjectDetectionGetOptions, useNativeObjectDetectionGetStatus, useNativeObjectDetectionSetEnabled, useNativeObjectDetectionSetSettings, useNavigation, useNavigationGetFeatures, useNavigationGetStatus, useNavigationGoToPoint, useNavigationListActions, useNavigationMove, useNavigationPlaySound, useNavigationRunAction, useNavigationSetLightLevel, useNavigationSetLightMode, useNavigationSetLightOn, useNavigationStop, useNetworkAccessGetEndpoint, useNetworkAccessGetStatus, useNetworkAccessListEndpoints, useNetworkAccessStart, useNetworkAccessStop, useNetworkLinkGetStatus, useNetworkQualityGetAllStats, useNetworkQualityGetDeviceStats, useNetworkQualityReportClientStats, useNodesClusterAddonStatus, useNodesDeployAddon, useNodesDirectory, useNodesExecuteQuery, useNodesGetCapUsageGraph, useNodesGetNodeAddons, useNodesRenameNode, useNodesRestartAddon, useNodesRestartNode, useNodesRestartProcess, useNodesSetProcessLogLevel, useNodesShutdownNode, useNodesTopology, useNodesUndeployAddon, useNotificationOutputDeleteTarget, useNotificationOutputDiscoverTargets, useNotificationOutputGetTarget, useNotificationOutputListTargetKinds, useNotificationOutputListTargets, useNotificationOutputProbeTargetConfig, useNotificationOutputSend, useNotificationOutputSetTargetEnabled, useNotificationOutputTestTarget, useNotificationOutputUpsertTarget, useNotificationRulesActivateAlarmProfile, useNotificationRulesCancelSnooze, useNotificationRulesCreateAlarmProfile, useNotificationRulesCreateRule, useNotificationRulesCreateSequence, useNotificationRulesCreateSnooze, useNotificationRulesDeleteAlarmProfile, useNotificationRulesDeleteRule, useNotificationRulesDeleteSequence, useNotificationRulesGetAlarmConfig, useNotificationRulesGetAlarmSensors, useNotificationRulesGetClipRetentionAsks, useNotificationRulesGetConditionCatalog, useNotificationRulesGetHistory, useNotificationRulesGetRule, useNotificationRulesGetRuleLastRuns, useNotificationRulesGetSequence, useNotificationRulesGetTemplateCatalog, useNotificationRulesListDeviceMutes, useNotificationRulesListRules, useNotificationRulesListSequences, useNotificationRulesListSnoozes, useNotificationRulesPreviewStateSummary, useNotificationRulesPreviewTemplate, useNotificationRulesRenameAlarmProfile, useNotificationRulesResolveArtifactUrl, useNotificationRulesSetAlarmConfig, useNotificationRulesSetDeviceMuted, useNotificationRulesSetRuleEnabled, useNotificationRulesTestRule, useNotificationRulesUpdateRule, useNotificationRulesUpdateSequence, useNotifierCancel, useNotifierGetStatus, useNotifierSend, useNumericSensorGetStatus, useOptimisticSlice, useOptionalSystem, useOptionalWidgetRegistry, useOsdGetStatus, useOsdManagerClearSlotBinding, useOsdManagerCopyDeviceConfiguration, useOsdManagerDeleteTemplate, useOsdManagerGetConditionSupport, useOsdManagerGetDeviceOsd, useOsdManagerGetSourceCatalog, useOsdManagerListTemplates, useOsdManagerPreviewSlot, useOsdManagerRenderDevice, useOsdManagerSetSlotBinding, useOsdManagerUpsertTemplate, useOsdSetOverlay, usePTZ, usePetFeederCallPet, usePetFeederCancelFeed, usePetFeederFeed, usePetFeederGetStatus, usePetFeederMarkFoodReplenished, usePetFeederPlaySound, usePetFeederResetDesiccant, usePetFeederSetChildLock, usePetFeederSetFeedSound, usePetFeederSetIndicatorLight, usePetFeederSetVolume, usePickerSiteZones, usePipelineAnalyticsApplyDeviceSettingsPatch, usePipelineAnalyticsCancelRelocateMedia, usePipelineAnalyticsCancelStorageMigrationMove, usePipelineAnalyticsClearTrackRelation, usePipelineAnalyticsClearTracks, usePipelineAnalyticsCompleteRetrainTrack, usePipelineAnalyticsCountRelocatableMedia, usePipelineAnalyticsCountUnstampedEventMedia, usePipelineAnalyticsDeleteDeviceEvents, usePipelineAnalyticsDeleteTracks, usePipelineAnalyticsDeselectRetrainFrame, usePipelineAnalyticsGetActiveTracks, usePipelineAnalyticsGetAudioEvents, usePipelineAnalyticsGetDeviceLiveContribution, usePipelineAnalyticsGetDeviceSettingsContribution, usePipelineAnalyticsGetEventDensity, usePipelineAnalyticsGetEventDensityBatch, usePipelineAnalyticsGetEventMedia, usePipelineAnalyticsGetEventMediaFootprintByKind, usePipelineAnalyticsGetEventStoreFootprint, usePipelineAnalyticsGetKeyEvents, usePipelineAnalyticsGetKeyEventsBatch, usePipelineAnalyticsGetMediaReclaimStatus, usePipelineAnalyticsGetMotionEvents, usePipelineAnalyticsGetObjectEmbeddingRebuildStatus, usePipelineAnalyticsGetObjectEvents, usePipelineAnalyticsGetRetrainExportUrl, usePipelineAnalyticsGetRetrainFrameImage, usePipelineAnalyticsGetSensorEvents, usePipelineAnalyticsGetStorageMigrationMoveStatus, usePipelineAnalyticsGetTrack, usePipelineAnalyticsGetTrackMedia, usePipelineAnalyticsGetTrainingExportSummary, usePipelineAnalyticsGetTrainingExportUrl, usePipelineAnalyticsListArchivedDebugNotes, usePipelineAnalyticsListBirthDecisions, usePipelineAnalyticsListEventKinds, usePipelineAnalyticsListEventKindsBatch, usePipelineAnalyticsListEventMedia, usePipelineAnalyticsListOpsLog, usePipelineAnalyticsListRecentTracks, usePipelineAnalyticsListRelocateMediaJobs, usePipelineAnalyticsListRetrainAnnotations, usePipelineAnalyticsListRetrainFrames, usePipelineAnalyticsListRetrainStaging, usePipelineAnalyticsListTrackMedia, usePipelineAnalyticsListTrackRelations, usePipelineAnalyticsListTracks, usePipelineAnalyticsOwnersWithMedia, usePipelineAnalyticsPauseForStorageMigration, usePipelineAnalyticsProposeRetrainAnnotations, usePipelineAnalyticsPruneEvents, usePipelineAnalyticsPruneEventsBefore, usePipelineAnalyticsPruneTracksBefore, usePipelineAnalyticsReadLongTermSeries, usePipelineAnalyticsRebuildObjectEmbeddings, usePipelineAnalyticsReclaimDebugMedia, usePipelineAnalyticsReconcileFromDisk, usePipelineAnalyticsRefreshStorageLocationsForMigration, usePipelineAnalyticsRelocateMedia, usePipelineAnalyticsRestageRetrainTrack, usePipelineAnalyticsResumeForStorageMigration, usePipelineAnalyticsSaveRetrainAnnotations, usePipelineAnalyticsSearchObjectEvents, usePipelineAnalyticsSelectRetrainFrames, usePipelineAnalyticsSetTrackFlags, usePipelineAnalyticsSetTrackPurity, usePipelineAnalyticsSetTrackRelation, usePipelineAnalyticsStartStorageMigrationMove, usePipelineAnalyticsWipeAllAnalytics, usePipelineAnalyticsWipeObjectEmbeddings, usePipelineExecutorCacheFrameInPool, usePipelineExecutorClearDeviceOverrides, usePipelineExecutorClearModelBoundDeviceOverrides, usePipelineExecutorDeleteModel, usePipelineExecutorDownloadModel, usePipelineExecutorGetAddonModels, usePipelineExecutorGetAudioCapabilities, usePipelineExecutorGetAvailableEngines, usePipelineExecutorGetCapabilities, usePipelineExecutorGetDefaultSteps, usePipelineExecutorGetDetectionConfigSchema, usePipelineExecutorGetEffectiveTuning, usePipelineExecutorGetEngineProvisioning, usePipelineExecutorGetGlobalPipelineConfig, usePipelineExecutorGetGlobalSteps, usePipelineExecutorGetInferenceDeviceHealth, usePipelineExecutorGetOrchestratorConfigSchema, usePipelineExecutorGetReferenceAudio, usePipelineExecutorGetReferenceAudioFiles, usePipelineExecutorGetReferenceImage, usePipelineExecutorGetSchema, usePipelineExecutorGetSelectedEngine, usePipelineExecutorGetVideoPipelineSteps, usePipelineExecutorInferCached, usePipelineExecutorKillEngine, usePipelineExecutorListLoadedEngines, usePipelineExecutorListReferenceImages, usePipelineExecutorRearmInferenceDevice, usePipelineExecutorRunAudioTest, usePipelineExecutorRunPipeline, usePipelineExecutorRunPipelineBatch, usePipelineExecutorSetVideoPipelineSteps, usePipelineExecutorSpinEngine, usePipelineExecutorUncacheFrame, usePipelineExecutorValidatePipeline, usePipelineOrchestratorApplyAutoTune, usePipelineOrchestratorApplyDeviceSettingsPatch, usePipelineOrchestratorAssignAudio, usePipelineOrchestratorAssignPipeline, usePipelineOrchestratorGetAgentLoad, usePipelineOrchestratorGetAgentSettings, usePipelineOrchestratorGetAudioAssignment, usePipelineOrchestratorGetAudioAssignments, usePipelineOrchestratorGetAudioNodeLoad, usePipelineOrchestratorGetCameraMetrics, usePipelineOrchestratorGetCameraSettings, usePipelineOrchestratorGetCameraStatus, usePipelineOrchestratorGetCameraStatuses, usePipelineOrchestratorGetCameraStepOverrides, usePipelineOrchestratorGetCameraSwitches, usePipelineOrchestratorGetCapabilityBindings, usePipelineOrchestratorGetClusterModelChoice, usePipelineOrchestratorGetDeviceLiveContribution, usePipelineOrchestratorGetDeviceSettingsContribution, usePipelineOrchestratorGetEffectiveShares, usePipelineOrchestratorGetGlobalMetrics, usePipelineOrchestratorGetIngestOwner, usePipelineOrchestratorGetNodeInferenceDevices, usePipelineOrchestratorGetPipelineAssignment, usePipelineOrchestratorGetPipelineAssignments, usePipelineOrchestratorGetPipelineDevicePin, usePipelineOrchestratorGetPlacementStats, usePipelineOrchestratorGetReconcileFromDiskStatus, usePipelineOrchestratorListAgentSettings, usePipelineOrchestratorPauseForStorageMigration, usePipelineOrchestratorPlanAutoTune, usePipelineOrchestratorRebalance, usePipelineOrchestratorReconcileFromDisk, usePipelineOrchestratorRemoveAgentSettings, usePipelineOrchestratorRequestOccupancyLook, usePipelineOrchestratorResetNodePipelineDefaults, usePipelineOrchestratorResolvePipeline, usePipelineOrchestratorResumeForStorageMigration, usePipelineOrchestratorSetAgentCapabilities, usePipelineOrchestratorSetAgentDecodeLimits, usePipelineOrchestratorSetAgentInferenceDevices, usePipelineOrchestratorSetAgentMaxCameras, usePipelineOrchestratorSetAgentReachableHost, usePipelineOrchestratorSetCameraPipelineForAgent, usePipelineOrchestratorSetCameraSensors, usePipelineOrchestratorSetCameraStepOverride, usePipelineOrchestratorSetCameraStepSettings, usePipelineOrchestratorSetCameraStepToggle, usePipelineOrchestratorSetCameraSwitch, usePipelineOrchestratorSetCapabilityBinding, usePipelineOrchestratorSetLoadBalancingShares, usePipelineOrchestratorSetPipelineDevicePin, usePipelineOrchestratorSetShareSource, usePipelineOrchestratorSuggestShares, usePipelineOrchestratorUnassignAudio, usePipelineOrchestratorUnassignPipeline, usePipelineRunnerAttachCamera, usePipelineRunnerDetachCamera, usePipelineRunnerGetAllCameraMetrics, usePipelineRunnerGetCameraMetrics, usePipelineRunnerGetLocalCameras, usePipelineRunnerGetLocalLoad, usePipelineRunnerGetLocalMetrics, usePipelineRunnerGetNativeCrop, usePipelineRunnerGetParkedTrackFrame, usePipelineRunnerParkTrackFrame, usePipelineRunnerReleaseParkedTrackFrames, usePipelineRunnerReportMotion, usePipelineRunnerRequestOccupancyBurst, usePipelineRunnerRunDetailSubtree, usePipelineRunnerRunStatelessStep, usePlateGalleryAssignPlate, usePlateGalleryAssignPlates, usePlateGalleryCorrectPlateText, usePlateGalleryCreateVehicle, usePlateGalleryDeletePlate, usePlateGalleryDeleteVehicle, usePlateGalleryGetPlateByTrack, usePlateGalleryGetPlateMedia, usePlateGalleryListPlates, usePlateGalleryListVehicleSamples, usePlateGalleryListVehicles, usePlateGalleryRemoveVehicleSample, usePlateGalleryRenameVehicle, usePlateGallerySearchPlates, usePlateGallerySuggestPlateClusters, usePlateGalleryUnassignPlate, usePlateGalleryUnassignPlates, usePlayerOverlayLayer, usePlayerOverlayLayers, usePlayerToolbarButton, usePlayerToolbarButtons, usePowerMeterGetStatus, usePresenceGetStatus, usePressureSensorGetStatus, usePrivacyMaskGetOptions, usePrivacyMaskGetStatus, usePrivacyMaskSetAudioEnabled, usePrivacyMaskSetMask, usePrompt, usePtzAutotrackGetSettings, usePtzAutotrackGetStatus, usePtzAutotrackSetEnabled, usePtzAutotrackSetSettings, usePtzCaptureHomeHere, usePtzContinuousMove, usePtzDeletePreset, usePtzGetHomePreset, usePtzGetHomeReturn, usePtzGetOptions, usePtzGetPosition, usePtzGetPresets, usePtzGetStatus, usePtzGoHome, usePtzGoToPreset, usePtzMove, usePtzSavePreset, usePtzSetAutofocus, usePtzSetHomePreset, usePtzSetHomeReturn, usePtzStop, useRebootReboot, useRecordedPlayback, useRecordingArchiveApplyDeviceSettingsPatch, useRecordingArchiveCancelRelocateJob, useRecordingArchiveCancelStorageMigrationMove, useRecordingArchiveDeleteFootprint, useRecordingArchiveGetAvailabilityBatch, useRecordingArchiveGetDaysWithRecordingsBatch, useRecordingArchiveGetDeviceConfig, useRecordingArchiveGetDeviceLiveContribution, useRecordingArchiveGetDeviceSettingsContribution, useRecordingArchiveGetPlacement, useRecordingArchiveGetRelocateResidue, useRecordingArchiveGetStatus, useRecordingArchiveGetStorageMigrationMoveStatus, useRecordingArchiveGetStorageUsage, useRecordingArchiveListOpsLog, useRecordingArchiveListRelocateJobs, useRecordingArchiveLocateSegment, useRecordingArchivePauseForStorageMigration, useRecordingArchivePlanStorageRebalance, useRecordingArchivePruneFootage, useRecordingArchiveReadGopBytes, useRecordingArchiveReadSegmentBytes, useRecordingArchiveReadWindowBytes, useRecordingArchiveReconcileLedgerAgainstDisk, useRecordingArchiveRefreshStorageLocationsForMigration, useRecordingArchiveRelocateFootage, useRecordingArchiveRenderClip, useRecordingArchiveRenderGif, useRecordingArchiveRescanStorage, useRecordingArchiveResumeForStorageMigration, useRecordingArchiveSetDeviceConfig, useRecordingArchiveSetDevicePlacement, useRecordingArchiveStartStorageMigrationMove, useRecordingArchiveStartStorageRebalance, useRecordingExportCancelExport, useRecordingExportCreateExport, useRecordingExportDeleteExport, useRecordingExportGetDownloadUrl, useRecordingExportGetExport, useRecordingExportListExports, useRecordingExportReadExportBytes, useRecordingGetAvailability, useRecordingGetDaysWithRecordings, useRecordingGetPlayback, useRecordingGetPlaybackOptions, useRecordingListSources, useRecordingOnboardGetOptions, useRecordingOnboardGetStatus, useRecordingOnboardSetSettings, useRecordingSignalGetStatus, useRemoteComponent, useRoleDisplayDefaults, useSceneMonitorCaptureReference, useSceneMonitorCreateScene, useSceneMonitorDeleteReference, useSceneMonitorDeleteScene, useSceneMonitorGetStatus, useSceneMonitorListScenes, useSceneMonitorListScenesBatch, useSceneMonitorRecheckNow, useSceneMonitorResetScene, useSceneMonitorUpdateScene, useScriptRunnerGetStatus, useScriptRunnerRun, useScriptRunnerStop, useScrubController, useServerManagementApplyServerUpdate, useServerManagementCheckServerUpdate, useServerManagementGetServerPackageStatus, useServerManagementRestartServer, useServerManagementRollbackServerUpdate, useSettingsStoreAggregate, useSettingsStoreCount, useSettingsStoreDeclareCollection, useSettingsStoreDelete, useSettingsStoreDeleteWhere, useSettingsStoreGet, useSettingsStoreHistogram, useSettingsStoreInsert, useSettingsStoreInsertMany, useSettingsStoreIsEmpty, useSettingsStoreQuery, useSettingsStoreSet, useSettingsStoreUpdate, useSettingsStoreUpdateWhere, useSettingsStoreUpsert, useSmokeGetStatus, useSnapshotApplyDeviceSettingsPatch, useSnapshotGetDebugState, useSnapshotGetDeviceLiveContribution, useSnapshotGetDeviceSettingsContribution, useSnapshotGetSnapshot, useSnapshotGetSnapshotLinks, useSnapshotGetSnapshotOverview, useSnapshotGetStatus, useSnapshotInvalidateCache, useStationaryOverlay, useStorageAbortUpload, useStorageBeginDownload, useStorageBeginUpload, useStorageDelete, useStorageDeleteLocation, useStorageEndDownload, useStorageExists, useStorageFinalizeUpload, useStorageGetAvailableSpace, useStorageList, useStorageListDrainProgress, useStorageListLocationDeclarations, useStorageListLocations, useStorageListProviders, useStorageMigrationCancel, useStorageMigrationCleanupCancel, useStorageMigrationCleanupStart, useStorageMigrationCleanupStatus, useStorageMigrationDrain, useStorageMigrationHistory, useStorageMigrationMovers, useStorageMigrationPlan, useStorageMigrationResidue, useStorageMigrationStart, useStorageMigrationStatus, useStorageRead, useStorageReadChunk, useStorageResolve, useStorageTestConfig, useStorageTestLocation, useStorageUpsertLocation, useStorageWrite, useStorageWriteChunk, useStoredGroupBy, useStreamBrokerAcquireEgressTranscode, useStreamBrokerApplyDeviceSettingsPatch, useStreamBrokerAssignProfile, useStreamBrokerFetchEventMedia, useStreamBrokerForgetDeviceHardware, useStreamBrokerGetAllRtspEntries, useStreamBrokerGetBrokerStats, useStreamBrokerGetDeviceAudioMute, useStreamBrokerGetDeviceLiveContribution, useStreamBrokerGetDeviceSettingsContribution, useStreamBrokerGetPreBufferInfo, useStreamBrokerGetRtspEntry, useStreamBrokerGetRtspPort, useStreamBrokerGetStreamUrl, useStreamBrokerGetStreamWithCodec, useStreamBrokerIsRtspEnabled, useStreamBrokerKillClient, useStreamBrokerListAllCameraStreams, useStreamBrokerListAllProfileSlots, useStreamBrokerListClients, useStreamBrokerProbeStream, useStreamBrokerProduceEventMedia, useStreamBrokerPublishCameraStream, useStreamBrokerPullAudioChunks, useStreamBrokerRegenerateRtspToken, useStreamBrokerReleaseEgressTranscode, useStreamBrokerReleaseStreamWithCodec, useStreamBrokerRenderPreBufferClip, useStreamBrokerReportStreamSignal, useStreamBrokerRestartProfile, useStreamBrokerRetractCameraStream, useStreamBrokerSetDeviceAudioMute, useStreamBrokerSetPreBufferDuration, useStreamBrokerSetRtspEnabled, useStreamBrokerSubscribeAudioChunks, useStreamBrokerUnassignProfile, useStreamBrokerUnsubscribeAudioChunks, useStreamCatalogGetCatalog, useStreamParamsGetConfigSchema, useStreamParamsGetOptions, useStreamParamsGetStatus, useStreamParamsSetProfile, useStreamSignalsListSignals, useSwitchGetStatus, useSwitchSetState, useSystem, useSystemDetectSiteLocation, useSystemFeatureFlags, useSystemForceRetentionCleanup, useSystemGetFailureContributions, useSystemGetLoadContributions, useSystemGetLoggingSettings, useSystemGetRequestCensus, useSystemGetRetentionConfig, useSystemGetSiteLocation, useSystemHealth, useSystemInfo, useSystemMutation, useSystemNetworkAddresses, useSystemQuery, useSystemSetLoggingSettings, useSystemSetRetentionConfig, useSystemSetSiteLocation, useTamperGetStatus, useTemperatureSensorGetStatus, useTerminalSessionAdoptLegacyMonitor, useTerminalSessionClose, useTerminalSessionCreateInstance, useTerminalSessionDeleteInstance, useTerminalSessionListInstances, useTerminalSessionListLegacyCameras, useTerminalSessionListProfiles, useTerminalSessionListSessions, useTerminalSessionOpenSession, useTerminalSessionPullOutput, useTerminalSessionResize, useTerminalSessionSetInstanceEnabled, useTerminalSessionUpdateInstance, useTerminalSessionWriteInput, useThemeMode, useToastOnToast, useTurnProviderGetTurnServers, useUpdateGetStatus, useUpdateInstallUpdate, useUrlQueryParam, useUserManagementConfirmTotp, useUserManagementCreateApiKey, useUserManagementCreateScopedToken, useUserManagementCreateUser, useUserManagementDeleteUser, useUserManagementDisableTotp, useUserManagementGetTotpStatus, useUserManagementListApiKeys, useUserManagementListOauthSessions, useUserManagementListScopedTokens, useUserManagementListUsers, useUserManagementOauthExchangeCode, useUserManagementOauthIssueCode, useUserManagementOauthRefresh, useUserManagementOauthVerifyAccessToken, useUserManagementResetPassword, useUserManagementRevokeApiKey, useUserManagementRevokeOauthSession, useUserManagementRevokeScopedToken, useUserManagementSetUserScopes, useUserManagementSetupTotp, useUserManagementUpdateUser, useUserManagementValidateApiKey, useUserManagementValidateCredentials, useUserManagementValidateScopedToken, useUserManagementVerifyTotp, useVacuumControlGetStatus, useVacuumControlLocate, useVacuumControlPause, useVacuumControlReturnToBase, useVacuumControlSetFanSpeed, useVacuumControlStart, useVacuumControlStop, useValveClose, useValveGetStatus, useValveOpen, useValveSetPosition, useValveStop, useVibrationGetStatus, useVideoclipsDialClipStream, useVideoclipsGetClipPlayback, useVideoclipsGetPlaybackOptions, useVideoclipsListClips, useVideoclipsListSources, useVideoclipsOfferClipBytes, useVideoclipsReadClipBytes, useVodPlayback, useWaterHeaterGetStatus, useWaterHeaterSetAway, useWaterHeaterSetOperationMode, useWaterHeaterSetTargetTemp, useWeatherGetStatus, useWebrtcSessionAddIceCandidate, useWebrtcSessionApplyDownlinkProbe, useWebrtcSessionCloseSession, useWebrtcSessionCreateSession, useWebrtcSessionGetIceCandidates, useWebrtcSessionGetSessionDebug, useWebrtcSessionGetSessionState, useWebrtcSessionHandleAnswer, useWebrtcSessionHandleOffer, useWebrtcSessionHasAdaptiveBitrate, useWebrtcSessionListStreams, useWidget, useWidgetMetadata, useWidgetRegistry, useZoneAnalyticsGetCameraHistory, useZoneAnalyticsGetCurrentSnapshot, useZoneAnalyticsGetCurrentSnapshotBatch, useZoneAnalyticsGetUnzonedHistory, useZoneAnalyticsGetZoneHistory, useZoneEditing, useZoneRulesListRules, useZoneRulesSetRules, useZonesAddZone, useZonesListZones, useZonesRemoveZone, useZonesUpdateZone, vacuumStateMeta, validateScopes, valveStateMeta, variantLabel, waterHeaterPhase, waterHeaterTint, weatherConditionMeta, weatherTint, withResolvedParentDetail, withUseRootModel, writeStoredGroupBy };
61194
+ export { AddonGlobalSettingsForm, AdminLinkQuickAction, AgentStepEditor, AlarmHeroCard, AlarmInlineControl as AlarmPanelInlineControl, AppShell, ArcKnob, AudioClassificationList, AudioLevelWaveform, AudioWaveform, AutomationHeroCard, AutomationInlineControl, AutotrackSection, BTN_COMPACT, BTN_COMPACT_DANGER, BTN_COMPACT_PRIMARY, BTN_COMPACT_WARNING, BackToStepsButton, Badge, BatteryBadge, BottomSheet, Breadcrumb, BrightnessPanel, Button, ButtonControl, ButtonHeroCard, CARD_MODE_MIN_COLUMNS, CENTER, CHIP_ACTIVE, CHIP_BASE, CHIP_INACTIVE, CLASS_COLORS, COLUMN_BREAKPOINT_CLASS, COLUMN_PRIORITY, COMMIT_DEDUPE_TOLERANCE_MS, COMMIT_DEDUPE_WINDOW_MS, CONTROL_CAP_NAMES, CONTROL_FILLS, CameraStreamPlayer, Card, Checkbox, ChildSectionAccordion, ClimatePanel, CodeBlock, CollapsibleCard, ConfigFormBuilder, FormField as ConfigFormField, ConfigNotificationTargetMultiSelectFieldView, ConfigNotificationTargetSelectFieldView, ConfigSchemaField, ConfirmActionButton, ConfirmDialogProvider, ConsumablesPanel, ContainerChildrenProvider, ContainerPrimaryHero, ControlColumn, ControlHeroCard, ControlInlineControl, ControlPanel, CopyButton, CoverHeroCard, CoverInlineControl, CoverPanel, CustomFieldRenderersProvider, DEFAULT_COLOR, DEVICE_COLUMNS, DEVICE_EXPORT_PAGE_SIZE_KEY, DEVICE_LIST_PAGE_SIZE_KEY, DEVICE_LIST_PAGE_SIZE_OPTIONS, DEVICE_PICKER_GROUP_STORAGE_KEY, DEVICE_ROLE_META, DEVICE_TYPE_CONTROL, DEVICE_TYPE_META, DISPLAY_ICON_REGISTRY, DataTable, DataTableFilterBar, DetectionCanvas, DetectionOverlay, DetectionResultTree, DevShell, DeviceActivityPanel, DeviceBatchToolbar, DeviceCard, DeviceContextProvider, DeviceExportPanel, DeviceExportSetup, DeviceFilterBar, DeviceGrid, DeviceItem, DeviceList, DeviceMultiSelectField, DevicePicker, DeviceQuickDetailProvider, DeviceSelectField, DeviceSelectorPicker, DeviceStepMatrix, DevicesFailedLine, DevicesLoadingLine, DevicesWidenWarning, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DiscoveryPanel, DoorbellRecentPanel, Dropdown, DropdownContent, DropdownItem, DropdownTrigger, DummyHeroCard, DummyInline, EMPTY_DATA_TABLE_FILTER, EVENT_KIND_ICONS, EmptyState, EndpointResultsCard, EndpointTestCard, ErrorBox, EventKindGlyph, EventStream, ExtensionsPanel, FILL, FLEET_LIST_GC_TIME_MS, FLEET_LIST_QUERY_KEY, FLEET_LIST_STALE_TIME_MS, FanHeroCard, FanInlineControl, FanPanel, FilterBar, FloatingEventStream, FloatingLogStream, FloatingPanel, FormField$1 as FormField, GRID_GAP, GRID_PAIRED, GRID_QUICK_STATS, GateBadge, GateControl, GripTrack, GroupedModelSelector, HOST_WIDGETS, HUB_SESSION_RUN_MS, HlsVideo, HoverZoomImage, HubSessionTestCard, HumidifierHeroCard, HumidifierInlineControl, INPUT_COMPACT, INPUT_SHELL, IconAction, IconButton, IconPicker, ImageHeroCard, ImageInlineControl, ImageSelector, InferenceConfigSelector, Input, KebabMenu, KeyValueList, LIST_ROW, Label, LawnMowerHeroCard, LawnMowerInlineControl, LayeredSettingsForm, LightHeroCard, LightInlineControl, LockHeroCard, LockInlineControl, LockPanel, LogStream, LoginForm, MODE_COLOR, MaskShapeCanvas, MediaPlayerHeroCard, MediaPlayerInlineControl, MediaPlayerPanel, MobileDrawer, ModelPicker, MotionZonesSettings, NAVIGATION_DRIVE_INTERVAL_MS, NatResultCard, NavigationPanel, NetworkLinkBadge, NodeMultiSelectField, NodePicker, NodeSelectField, NotificationTargetSelectField, NotificationTargetsField, OPTION_SELECT_SEARCH_MIN_OPTIONS, OfflineBadge, OptionSelect, PAGE_SIZE_ALL, PHASE_CONFIG, PICKER_CHIP_LIMIT, PICKER_DEFAULT_GROUP_BY, PICKER_GROUP_BY_MODES, PICKER_GROUP_COLLAPSE_AT, PICKER_NONE_KEY, PICKER_NOUN_PLURAL, PICKER_NO_FILTERS, PICKER_PAGE_SIZE, PICKER_SEARCH_DEBOUNCE_MS, PICKER_WINDOW, PRIORITY, PTZOverlay, PageHeader, PageSizeSelect, PhaseIcon, PipelineBuilder, PipelineRuntimeSelector, PipelineStep, PlaceholderChipField, PlayerOverlaysProvider, Popover, PopoverContent, PopoverRowAction, PopoverTrigger, PrimaryChildPicker, PrivacyMaskSettings, ProviderBadge, PtzPanel, QrCode, RECONNECT_POLICY, RECORDED_PLAYBACK_MODES, RIGHT, ROI_AREA_WARN_FRACTION, ROLE_DESCRIPTOR, RadialGauge, RecordedPlaybackProvider, RecordingPanel, ResponseLog, RoleDisplayDefaultsFromServer, RoleDisplayDefaultsProvider, RootModelSourceControl, RootSuppliedModelNote, RunReport, SCENE_CAPTURE_VARIANTS, SECTION_BODY, SECTION_CARD, SECTION_HEADER, SETTING_ROW, SETTING_ROW_LABEL, SETTING_ROW_STACK_BREAKPOINT, SETTING_ROW_VALUE, SETTING_ROW_VALUE_TEXT, SNAPSHOT_MEDIA_PATH, SPLIT_PANEL_OUTER, SPLIT_PANEL_SIDE, STACK_GAP, STATE_COLOR, STEP_DOCK_WIDTH_PX, SceneMonitorEditor, ScopePicker, ScriptHeroCard, ScriptInlineControl, ScrollArea, SectionCard, SegmentedChoice, Select, SelectedChip, SemanticBadge, SensorHeroCard, SensorInlineControl, SensorValueAtom, Separator, SettingRow, Sidebar, SidebarItem, Skeleton, SlideToggle, SnapshotButton, SortHeaderButton, StatCard, StateValuesStream, StatusBadge, StepDetailDock, StepInspectorPanel, StepModelRow, StepTimings, Stepper, StreamBrokerSelector, StreamDebugOverlay, StreamPanel, Switch, SwitchHeroCard, SwitchInlineControl, SwitchPanel, SystemProvider, TABLE_SCROLL_MAX_H, TEXT_FIELD_LABEL, TEXT_HINT, TEXT_METRIC, TEXT_SECTION_LABEL, TEXT_VALUE, TIMEZONES, Tabs, TabsContent, TabsList, TabsTrigger, TapToggle, ThemeProvider, ThermostatHeroCard, ThermostatInlineControl, TimezoneSelector, Tooltip, TooltipContent, TooltipTrigger, UnitPicker, VacuumHeroCard, VacuumInlineControl, ValueReadout, ValveHeroCard, ValveInlineControl, VersionBadge, VodPlaybackProvider, WaterHeaterHeroCard, WaterHeaterInlineControl, WeatherHeroCard, WeatherInlineControl, WidgetMetricCard, WidgetPanel, WidgetRegistryProvider, WidgetSlot, ZoneEditingProvider, MicOff as a, acceleratorLabel, acceleratorShortLabel, agentColumnKey, allDeviceTypeFilterOptions, applyFleetQueryDefaults, applyPickerFilters, ariaSortForColumn, badgeFor, bindsToModel, buildPeerConnectionConfig, buildStepTreeFromSchema, EyeOff as c, catalogHasDraft, childEntityId, childListName, chipOverflow, classifyAddressScope, classifyCapability, classifySessionVerdict, clearPickerGroup, cn, columnsForContext, compareSrflxMappings, containerChildToRef, countableDevices, coverHighlight, coverageLine, createSharedContext, createTheme, cursorFractionFor, darkColors, dataTableFacets, defaultOfField, defaultTheme, deriveDeviceKind, deriveWebrtcDebugStats, describeDeviceSelector, deviceIntegrationResolver, deviceListStateOf, deviceMatchesFilter, deviceOptionLabel, deviceRoleMeta, deviceRoleMetaOf, deviceTypeLabel, deviceTypeMeta, deviceTypeMetaOf, devicesToOptions, dockBox, draftFromQuery, effectivePageSize, emptyDevicesWidens, emptySessionRun, ensureMfHostInit, eventKindLabel, filterDataTableRows, filterDeviceOptions, filterOptions as filterOptionSelectOptions, filterPickerDevices, finalizeSessionRun, findTimezone, flattenPickerGrouping, flattenPickerRows, fleetViewOf, formatControlDateTime, formatLastSeen, formatNumeric, formatSettingValue, freezeVerdict, fuzzyMatch, getClassColor, getPhaseVisual, groupAgentColumns, groupChildrenByLayout, groupPickerDevices, groupPickerDevicesBy, hardwareLabel, hubOrigin, hubPath, hubUrl, hubWsUrl, humanDuration, humidifierTint, Mic as i, initialScrubState, insertAtSelection, isAbsentProvider, isDataTableFilterActive, isFieldVisible, isGroupExpanded, isValueField, Camera as l, lawnMowerActivityMeta, lightColors, loadRemoteBundle, makeScrubBridge, metadataEntries, metadataString, mirror, mountAddonPage, mountPath, Square as n, nextGridPosition, nextReconnectAction, nextSort, nextSortDirection, nextTableSort, normalizeForSearch, ImageOff as o, offListSelections, openTopLayer, optionSearchText as optionSelectSearchText, orphanDeviceLabel, overrideEntityIdFromLink, pageRows, pageSizeLabel, parentDetailOf, parseIceCandidate, parsePickerGroupBy, parseRecordedServerMessage, partitionSelected as partitionOptionSelectSelected, partitionSuggested, pickerFacetFilterShown, pickerFacetKey, pickerFacetOptions, pickerFiltersActive, pickerGroupKey, pickerHiddenSelectedCount, pickerLocationKey, pickerQueryTokens, pickerRowFacts, pickerSearchText, pickerZoneFilterSuspended, pinnedStreamChoices, providerIcons, RotateCcw as r, readStoredGroupBy, reduceSessionRun, resetHubMountForTests, resetPickerFilters, resolveContainerPrimary, resolveControlAlign, resolveDeviceControl, resolveDeviceIntegration, resolveDisplayIcon, resolveEffectiveDefaultModel, resolveEffectiveStepModel, resolveEventKindIcon, resolveSearchable as resolveOptionSelectSearchable, resolveParentNames, resolvePrimaryChild, resolveSelectedDevices, resolveSensorDisplay, resolveStepDefaultModel, resolveTableLayout, roiTooLarge, rootSourceState, routerBasename, Grid2x2 as s, sameLocationSuggestions, scrubReducer, searchSteps, selectPickerGroup, selectedDeviceOptions, serializeRecordedCommand, settingsModelIdFor, shouldCommit, shouldEmit, shouldShowAddNewFooter, shouldShowUseQueryRow, shouldUseSingleNode, showsNoCamerasHint, sortRows, sortRowsByColumn, statusIcons, statusLine, stepFieldDomId, stepHasModelForFormat, stepLocalFields, stepModelOptions, stripParentNamePrefix, summarizeHubCandidates, Trash2 as t, tabForSelector, tankAlert, themeToCss, toStationaryOverlayBox, toggleDataTableFacet, togglePickerDevice, togglePickerKey, trpc, typeLabel, createLucideIcon as u, useAccessoriesGetStatus, useAccessoriesSetChildHidden, useAddonPagesListPages, useAddonSettingsGetDeviceSettings, useAddonSettingsGetGlobalSettings, useAddonSettingsGetIntegrationSettings, useAddonSettingsUpdateDeviceSettings, useAddonSettingsUpdateGlobalSettings, useAddonWidgetsListWidgets, useAddonsApplyAutoUpdateToAll, useAddonsCancelJob, useAddonsCustom, useAddonsForceRefresh, useAddonsGetAddonAutoUpdate, useAddonsGetAutoUpdateSettings, useAddonsGetJob, useAddonsGetLastRestart, useAddonsGetLogs, useAddonsGetVersions, useAddonsInstallFromWorkspace, useAddonsInstallPackage, useAddonsIsWorkspaceAvailable, useAddonsList, useAddonsListCapabilityProviders, useAddonsListFrameworkPackages, useAddonsListInstallations, useAddonsListJobs, useAddonsListPackages, useAddonsListUpdates, useAddonsListWorkspacePackages, useAddonsOnAddonLogs, useAddonsReloadPackages, useAddonsRestartAddon, useAddonsRestartServer, useAddonsRetryLoad, useAddonsRollbackPackage, useAddonsSearchAvailable, useAddonsSetAddonAutoUpdate, useAddonsSetAutoUpdateSettings, useAddonsSetCapabilityProviderEnabled, useAddonsStartJob, useAddonsUninstallPackage, useAddonsUpdatePackage, useAirQualitySensorGetStatus, useAlarmPanelArm, useAlarmPanelDisarm, useAlarmPanelGetStatus, useAlarmPanelTrigger, useAlert, useAlertsDismiss, useAlertsEmit, useAlertsGetUnreadCount, useAlertsList, useAlertsMarkAllRead, useAlertsMarkRead, useAlertsUpdate, useAllWidgets, useAmbientLightSensorGetStatus, useAudioAnalysisApplyDeviceSettingsPatch, useAudioAnalysisGetDeviceLiveContribution, useAudioAnalysisGetDeviceSettingsContribution, useAudioAnalysisResolveDeviceSettings, useAudioAnalyzerAnalyseChunk, useAudioAnalyzerAttachDevice, useAudioAnalyzerClassify, useAudioAnalyzerDetachDevice, useAudioAnalyzerDispose, useAudioAnalyzerIsReady, useAudioAnalyzerReprobeAudioEngine, useAudioCodecCanHandle, useAudioCodecCloseSession, useAudioCodecCreateDecodeSession, useAudioCodecCreateEncodeSession, useAudioCodecFlushEncode, useAudioCodecListActiveSessions, useAudioCodecListSupportedCodecs, useAudioCodecPullEncoded, useAudioCodecPullPcm, useAudioCodecPushEncodedFrame, useAudioCodecPushPcm, useAudioMetricsGetCurrentSnapshot, useAudioMetricsGetHistory, useAutomationControlDisable, useAutomationControlEnable, useAutomationControlGetStatus, useAutomationControlTrigger, useBackupCancel, useBackupDelete, useBackupDeleteSchedule, useBackupGetEntries, useBackupList, useBackupListArchives, useBackupListDestinations, useBackupListLocations, useBackupListRuns, useBackupListSchedules, useBackupPreviewSchedule, useBackupRestore, useBackupTrigger, useBackupUpsertDestinationPolicy, useBackupUpsertSchedule, useBatteryGetStatus, useBatteryWakeForStream, useBinaryGetStatus, useBrightnessGetStatus, useBrightnessSetBrightness, useBrokerAdd, useBrokerGet, useBrokerGetBrokerConfig, useBrokerGetSettings, useBrokerGetSettingsSchema, useBrokerGetState, useBrokerGetStatus, useBrokerList, useBrokerListProviders, useBrokerPublish, useBrokerRemove, useBrokerSetSettings, useBrokerSubscribe, useBrokerTestConnection, useBrokerTestSettings, useBrokerUnsubscribe, useButtonPress, useCalendarsGetPreferences, useCalendarsListCalendars, useCalendarsListEvents, useCalendarsSetCalendarPreference, useCameraCredentialsGetCredentials, useCameraCredentialsGetStatus, useCameraGridLayoutApplyDeviceSettingsPatch, useCameraGridLayoutGetDeviceLiveContribution, useCameraGridLayoutGetDeviceSettingsContribution, useCameraGridLayoutGetLayout, useCameraGridLayoutSaveLayout, useCameraPipelineConfigApplyDeviceSettingsPatch, useCameraPipelineConfigGetDeviceLiveContribution, useCameraPipelineConfigGetDeviceSettingsContribution, useCameraStreamsGetBrokerStreams, useCameraStreamsGetCameraStreams, useCameraStreamsGetProfileRtspEntries, useCameraStreamsGetRtspEntries, useCameraStreamsPickStream, useCapabilityPresence, useCarbonMonoxideGetStatus, useClimateControlGetStatus, useClimateControlSetFanMode, useClimateControlSetMode, useClimateControlSetPreset, useClimateControlSetSwingHorizontal, useClimateControlSetSwingVertical, useClimateControlSetTarget, useClimateControlSetTargetHumidity, useClimateControlSetTargetRange, useClusterNodes, useClusterTopology, useColorGetStatus, useColorSetColor, useConfirm, useConnectionTestDescribeTest, useConnectionTestTestSettings, useConnectivityGetStatus, useConsumablesGetStatus, useConsumablesReset, useContactGetStatus, useContainerChildren, useControlGetStatus, useControlSetValue, useCoreBlocksCompile, useCoreBlocksCreate, useCoreBlocksCreateComposition, useCoreBlocksDelete, useCoreBlocksGet, useCoreBlocksGetBlockSettings, useCoreBlocksGetComposition, useCoreBlocksGetCustomization, useCoreBlocksGetTypeDefs, useCoreBlocksList, useCoreBlocksListComposableCaps, useCoreBlocksListCompositions, useCoreBlocksRestart, useCoreBlocksSetEnabled, useCoreBlocksUpdate, useCoreBlocksUpdateBlockSettings, useCoreBlocksUpdateComposition, useCoreBlocksValidateComposition, useCoverClose, useCoverGetStatus, useCoverOpen, useCoverSetPosition, useCoverSetTiltPosition, useCoverStop, useCustomFieldRenderer, useDataTableFilter, useDayNightGetOptions, useDayNightGetStatus, useDayNightSetSettings, useDebouncedString, useDecoderGetInfo, useDecoderReprobeHwaccel, useDetectionPipelineApplyDeviceSettingsPatch, useDetectionPipelineGetDeviceLiveContribution, useDetectionPipelineGetDeviceSettingsContribution, useDevShell, useDevice, useDeviceAdminLink, useDeviceAdminLinkGetAdminLink, useDeviceAdoptionAdopt, useDeviceAdoptionGetCandidate, useDeviceAdoptionGetStatus, useDeviceAdoptionListCandidateFilters, useDeviceAdoptionListCandidates, useDeviceAdoptionRefresh, useDeviceAdoptionRelease, useDeviceAdoptionResync, useDeviceAutotrack, useDeviceBattery, useDeviceCapSlice, useDeviceCapability, useDeviceDetections, useDeviceDiscoveryAdoptDevice, useDeviceDiscoveryGetStatus, useDeviceDiscoveryListDiscovered, useDeviceDiscoveryRefreshDiscovery, useDeviceDiscoveryReleaseDevice, useDeviceExportApplyDeviceSettingsPatch, useDeviceExportExposeDevice, useDeviceExportGetDeviceLiveContribution, useDeviceExportGetDeviceSettingsContribution, useDeviceExportGetStatus, useDeviceExportListExposedDevices, useDeviceExportListSupportedDeviceKinds, useDeviceExportUnexposeDevice, useDeviceExtensionListCandidates, useDeviceExtensionListSlots, useDeviceExtensionListSources, useDeviceExtensionSetSlot, useDeviceId, useDeviceListPageSize, useDeviceManagerAddLocation, useDeviceManagerAdoptDevice, useDeviceManagerAdoptionAdopt, useDeviceManagerAdoptionCancelJob, useDeviceManagerAdoptionListCandidateFilters, useDeviceManagerAdoptionListCandidates, useDeviceManagerAdoptionListJobs, useDeviceManagerAdoptionRefresh, useDeviceManagerAdoptionRelease, useDeviceManagerAdoptionResync, useDeviceManagerAdoptionStartJob, useDeviceManagerAllocateDeviceId, useDeviceManagerApplyInitialMeta, useDeviceManagerCreateDevice, useDeviceManagerDisable, useDeviceManagerDiscoverAllProviders, useDeviceManagerDiscoverDevices, useDeviceManagerDiscoverProvider, useDeviceManagerDiscoveryProviders, useDeviceManagerEnable, useDeviceManagerGetAllBindings, useDeviceManagerGetBindings, useDeviceManagerGetBindingsBatch, useDeviceManagerGetChildren, useDeviceManagerGetChildrenBatch, useDeviceManagerGetConfigSchema, useDeviceManagerGetCreationSchema, useDeviceManagerGetDevice, useDeviceManagerGetDeviceAggregate, useDeviceManagerGetDeviceLiveInfoAggregate, useDeviceManagerGetDeviceSettingsAggregate, useDeviceManagerGetDeviceStatusAggregate, useDeviceManagerGetDeviceStatusAggregateBatch, useDeviceManagerGetLinkedDevices, useDeviceManagerGetLinkedDevicesBatch, useDeviceManagerGetRoleDisplayDefaults, useDeviceManagerGetSettingsSchema, useDeviceManagerGetStreamProfileMap, useDeviceManagerGetStreamSources, useDeviceManagerGetWireableFields, useDeviceManagerGetWrapperActivation, useDeviceManagerListAll, useDeviceManagerListBindableCapsForDeviceType, useDeviceManagerListDeviceIdsByCaps, useDeviceManagerListLocationDescriptions, useDeviceManagerListLocationIcons, useDeviceManagerListLocations, useDeviceManagerListPersistedByAddon, useDeviceManagerListSiteZones, useDeviceManagerListWrappersForCap, useDeviceManagerLoadConfig, useDeviceManagerLoadMeta, useDeviceManagerLoadRuntimeState, useDeviceManagerMigrateDevice, useDeviceManagerPersistConfig, useDeviceManagerProbeStreams, useDeviceManagerProviderCreationType, useDeviceManagerProviderDiscoveryParamsSchema, useDeviceManagerRegisterDevice, useDeviceManagerRemove, useDeviceManagerRemoveByIntegration, useDeviceManagerRemoveDevice, useDeviceManagerRemoveLocation, useDeviceManagerRemoveSiteZone, useDeviceManagerRenameLocation, useDeviceManagerRenameSiteZone, useDeviceManagerRunDeviceAction, useDeviceManagerSetChildLayout, useDeviceManagerSetDisabled, useDeviceManagerSetDisplay, useDeviceManagerSetIntegrationId, useDeviceManagerSetLinkDeviceId, useDeviceManagerSetLocation, useDeviceManagerSetLocationDescription, useDeviceManagerSetLocationIcon, useDeviceManagerSetLocationZone, useDeviceManagerSetMetadata, useDeviceManagerSetName, useDeviceManagerSetPrimaryChildEntityId, useDeviceManagerSetRole, useDeviceManagerSetRoleDisplayDefaults, useDeviceManagerSetSiteZone, useDeviceManagerSetStreamProfileMap, useDeviceManagerSetType, useDeviceManagerSetWrapperActive, useDeviceManagerTestCreationField, useDeviceManagerTestField, useDeviceManagerUpdateConfig, useDeviceManagerUpdateDeviceField, useDeviceManagerUpdateDeviceFieldsBatch, useDeviceNetworkLink, useDeviceOpsGetConfigEntries, useDeviceOpsGetRawState, useDeviceOpsGetSettingsSchema, useDeviceOpsGetStreamSources, useDeviceOpsRemoveDevice, useDeviceOpsRunAction, useDeviceOpsSetConfig, useDeviceProviderAdoptDiscoveredDevice, useDeviceProviderCreateDevice, useDeviceProviderDiscoverDevices, useDeviceProviderGetChildCreationSchema, useDeviceProviderGetDevices, useDeviceProviderGetDiscoveryParamsSchema, useDeviceProviderGetManualCreationType, useDeviceProviderGetStatus, useDeviceProviderReloadDevice, useDeviceProviderStart, useDeviceProviderStop, useDeviceProviderSupportsDiscovery, useDeviceProviderSupportsManualCreation, useDeviceProviderTestCreationField, useDeviceProxy, useDeviceQuickDetailOpen, useDeviceSnapshot, useDeviceSnapshotImage, useDeviceState, useDeviceStateClaimFields, useDeviceStateGetAllSnapshots, useDeviceStateGetCapSlice, useDeviceStateGetNativeCapSlice, useDeviceStateGetSnapshot, useDeviceStateListClaims, useDeviceStatePatchOwnedFields, useDeviceStateReleaseClaim, useDeviceStateSetCapSlice, useDeviceStateSlice, useDeviceStatusGetStatus, useDeviceWebrtc, useDevices, useDoorbellEvents, useDoorbellGetStatus, useEnumSensorGetStatus, useEventEmitterGetStatus, useEventInvalidation, useEventStreamLatest, useEventStreamMap, useFaceGalleryAssignFace, useFaceGalleryAssignFaces, useFaceGalleryAuditIdentitySamples, useFaceGalleryCreateIdentity, useFaceGalleryDeleteFace, useFaceGalleryDeleteIdentity, useFaceGalleryGetFaceByTrack, useFaceGalleryGetFaceMedia, useFaceGalleryListIdentities, useFaceGalleryListIdentitySamples, useFaceGalleryListRecentFaces, useFaceGalleryRemoveSample, useFaceGalleryRenameIdentity, useFaceGallerySuggestFaceClusters, useFaceGalleryUnassignFace, useFaceGalleryUnassignFaces, useFanControlGetStatus, useFanControlSetDirection, useFanControlSetOscillating, useFanControlSetPercentage, useFanControlSetPreset, useFeatureProbeGetStatus, useFloodGetStatus, useGasGetStatus, useHumidifierGetStatus, useHumidifierSetMode, useHumidifierSetOn, useHumidifierSetTargetHumidity, useHumiditySensorGetStatus, useImageGetStatus, useImageSettingsGetOptions, useImageSettingsGetStatus, useImageSettingsSetSettings, useIntegrationsCreate, useIntegrationsDelete, useIntegrationsGet, useIntegrationsGetAvailableTypes, useIntegrationsGetByAddonId, useIntegrationsGetSettings, useIntegrationsList, useIntegrationsSetSettings, useIntegrationsTestConnection, useIntegrationsUpdate, useIntercomEndTalkSession, useIntercomGetStatus, useIntercomHandleAnswer, useIntercomPushTalkAudio, useIntercomStartSession, useIntercomStartTalkSession, useIntercomStopSession, useIsMidWidth, useIsMobile, useLawnMowerControlDock, useLawnMowerControlGetStatus, useLawnMowerControlPause, useLawnMowerControlStartMowing, useLiveBuffer, useLiveEvent, useLlmCancel, useLlmDeleteModel, useLlmDeleteProfile, useLlmGenerate, useLlmGenerateVision, useLlmGetDefaults, useLlmGetRuntimeStatus, useLlmGetUsage, useLlmInstallModel, useLlmListModelCatalog, useLlmListModels, useLlmListNodeModels, useLlmListProfileKinds, useLlmListProfiles, useLlmListRuntimeNodes, useLlmResolveModelRef, useLlmSetDefault, useLlmStartRuntime, useLlmStopRuntime, useLlmTestProfile, useLlmUpsertProfile, useLocalNetworkAddCustomAddress, useLocalNetworkDownloadCa, useLocalNetworkGetAddresses, useLocalNetworkGetAllowedAddresses, useLocalNetworkGetConnectionEndpoints, useLocalNetworkGetHubAddresses, useLocalNetworkGetNotificationEndpoint, useLocalNetworkGetPreferred, useLocalNetworkGetTlsStatus, useLocalNetworkGetViewerEndpoints, useLocalNetworkList, useLocalNetworkRegenerateCertificate, useLocalNetworkRemoveCustomAddress, useLocalNetworkResetAllowlistToBestMatch, useLocalNetworkRevertToGeneratedCertificate, useLocalNetworkSetAddressRole, useLocalNetworkSetAllowedAddresses, useLocalNetworkSetNotificationEndpoint, useLocalNetworkSetViewerEndpoints, useLocalNetworkUploadCertificate, useLockControlGetStatus, useLockControlLock, useLockControlOpen, useLockControlUnlock, useMediaPlayerGetStatus, useMediaPlayerNext, useMediaPlayerPause, useMediaPlayerPlay, useMediaPlayerPlayMedia, useMediaPlayerPrevious, useMediaPlayerSeek, useMediaPlayerSelectSource, useMediaPlayerSetMute, useMediaPlayerSetRepeat, useMediaPlayerSetShuffle, useMediaPlayerSetVolume, useMediaPlayerStop, useMeshNetworkGetStatus, useMeshNetworkJoin, useMeshNetworkLeave, useMeshNetworkListPeers, useMeshNetworkLogout, useMeshNetworkStartLogin, useMeshNetworkTestConnection, useMetricsProviderCollectSnapshot, useMetricsProviderGetAddonStats, useMetricsProviderGetCached, useMetricsProviderGetCpuTemperature, useMetricsProviderGetCurrent, useMetricsProviderGetDiskSpace, useMetricsProviderGetGpuInfo, useMetricsProviderGetLoadSeries, useMetricsProviderGetProcessStats, useMetricsProviderListAddonInstances, useMetricsProviderListNodeProcesses, useMotionDetectionAnalyze, useMotionDetectionApplyDeviceSettingsPatch, useMotionDetectionGetDeviceLiveContribution, useMotionDetectionGetDeviceSettingsContribution, useMotionDetectionRemoveCamera, useMotionDetectionReset, useMotionGetStatus, useMotionIsDetected, useMotionTriggerGetStatus, useMotionTriggerSetMotionTrigger, useMotionZonesGetOptions, useMotionZonesGetStatus, useMotionZonesSetZone, useMqttBrokerAddBroker, useMqttBrokerGetBrokerConfig, useMqttBrokerGetStatus, useMqttBrokerListBrokers, useMqttBrokerRemoveBroker, useMqttBrokerStartEmbeddedBroker, useMqttBrokerStopEmbeddedBroker, useMqttBrokerTestConnection, useNativeObjectDetectionGetOptions, useNativeObjectDetectionGetStatus, useNativeObjectDetectionSetEnabled, useNativeObjectDetectionSetSettings, useNavigation, useNavigationGetFeatures, useNavigationGetStatus, useNavigationGoToPoint, useNavigationListActions, useNavigationMove, useNavigationPlaySound, useNavigationRunAction, useNavigationSetLightLevel, useNavigationSetLightMode, useNavigationSetLightOn, useNavigationStop, useNetworkAccessGetEndpoint, useNetworkAccessGetStatus, useNetworkAccessListEndpoints, useNetworkAccessStart, useNetworkAccessStop, useNetworkLinkGetStatus, useNetworkQualityGetAllStats, useNetworkQualityGetDeviceStats, useNetworkQualityReportClientStats, useNodesClusterAddonStatus, useNodesDeployAddon, useNodesDirectory, useNodesExecuteQuery, useNodesGetCapUsageGraph, useNodesGetNodeAddons, useNodesRenameNode, useNodesRestartAddon, useNodesRestartNode, useNodesRestartProcess, useNodesSetProcessLogLevel, useNodesShutdownNode, useNodesTopology, useNodesUndeployAddon, useNotificationOutputDeleteTarget, useNotificationOutputDiscoverTargets, useNotificationOutputGetTarget, useNotificationOutputListTargetKinds, useNotificationOutputListTargets, useNotificationOutputProbeTargetConfig, useNotificationOutputSend, useNotificationOutputSetTargetEnabled, useNotificationOutputTestTarget, useNotificationOutputUpsertTarget, useNotificationRulesActivateAlarmProfile, useNotificationRulesCancelSnooze, useNotificationRulesCreateAlarmProfile, useNotificationRulesCreateRule, useNotificationRulesCreateSequence, useNotificationRulesCreateSnooze, useNotificationRulesDeleteAlarmProfile, useNotificationRulesDeleteRule, useNotificationRulesDeleteSequence, useNotificationRulesGetAlarmConfig, useNotificationRulesGetAlarmSensors, useNotificationRulesGetClipRetentionAsks, useNotificationRulesGetConditionCatalog, useNotificationRulesGetHistory, useNotificationRulesGetRule, useNotificationRulesGetRuleLastRuns, useNotificationRulesGetSequence, useNotificationRulesGetTemplateCatalog, useNotificationRulesListDeviceMutes, useNotificationRulesListRules, useNotificationRulesListSequences, useNotificationRulesListSnoozes, useNotificationRulesPreviewStateSummary, useNotificationRulesPreviewTemplate, useNotificationRulesRenameAlarmProfile, useNotificationRulesResolveArtifactUrl, useNotificationRulesSetAlarmConfig, useNotificationRulesSetDeviceMuted, useNotificationRulesSetRuleEnabled, useNotificationRulesTestRule, useNotificationRulesUpdateRule, useNotificationRulesUpdateSequence, useNotifierCancel, useNotifierGetStatus, useNotifierSend, useNumericSensorGetStatus, useOptimisticSlice, useOptionalSystem, useOptionalWidgetRegistry, useOsdGetStatus, useOsdManagerClearSlotBinding, useOsdManagerCopyDeviceConfiguration, useOsdManagerDeleteTemplate, useOsdManagerGetConditionSupport, useOsdManagerGetDeviceOsd, useOsdManagerGetSourceCatalog, useOsdManagerListTemplates, useOsdManagerPreviewSlot, useOsdManagerRenderDevice, useOsdManagerSetSlotBinding, useOsdManagerUpsertTemplate, useOsdSetOverlay, usePTZ, usePersistedPageSize, usePetFeederCallPet, usePetFeederCancelFeed, usePetFeederFeed, usePetFeederGetStatus, usePetFeederMarkFoodReplenished, usePetFeederPlaySound, usePetFeederResetDesiccant, usePetFeederSetChildLock, usePetFeederSetFeedSound, usePetFeederSetIndicatorLight, usePetFeederSetVolume, usePickerSiteZones, usePipelineAnalyticsApplyDeviceSettingsPatch, usePipelineAnalyticsCancelRelocateMedia, usePipelineAnalyticsCancelStorageMigrationMove, usePipelineAnalyticsClearTrackRelation, usePipelineAnalyticsClearTracks, usePipelineAnalyticsCompleteRetrainTrack, usePipelineAnalyticsCountRelocatableMedia, usePipelineAnalyticsCountUnstampedEventMedia, usePipelineAnalyticsDeleteDeviceEvents, usePipelineAnalyticsDeleteTracks, usePipelineAnalyticsDeselectRetrainFrame, usePipelineAnalyticsGetActiveTracks, usePipelineAnalyticsGetAudioEvents, usePipelineAnalyticsGetDeviceLiveContribution, usePipelineAnalyticsGetDeviceSettingsContribution, usePipelineAnalyticsGetEventDensity, usePipelineAnalyticsGetEventDensityBatch, usePipelineAnalyticsGetEventMedia, usePipelineAnalyticsGetEventMediaFootprintByKind, usePipelineAnalyticsGetEventStoreFootprint, usePipelineAnalyticsGetKeyEvents, usePipelineAnalyticsGetKeyEventsBatch, usePipelineAnalyticsGetMediaReclaimStatus, usePipelineAnalyticsGetMotionEvents, usePipelineAnalyticsGetObjectEmbeddingRebuildStatus, usePipelineAnalyticsGetObjectEvents, usePipelineAnalyticsGetRetrainExportUrl, usePipelineAnalyticsGetRetrainFrameImage, usePipelineAnalyticsGetSensorEvents, usePipelineAnalyticsGetStorageMigrationMoveStatus, usePipelineAnalyticsGetTrack, usePipelineAnalyticsGetTrackMedia, usePipelineAnalyticsGetTrainingExportSummary, usePipelineAnalyticsGetTrainingExportUrl, usePipelineAnalyticsListArchivedDebugNotes, usePipelineAnalyticsListBirthDecisions, usePipelineAnalyticsListEventKinds, usePipelineAnalyticsListEventKindsBatch, usePipelineAnalyticsListEventMedia, usePipelineAnalyticsListOpsLog, usePipelineAnalyticsListRecentTracks, usePipelineAnalyticsListRelocateMediaJobs, usePipelineAnalyticsListRetrainAnnotations, usePipelineAnalyticsListRetrainFrames, usePipelineAnalyticsListRetrainStaging, usePipelineAnalyticsListTrackMedia, usePipelineAnalyticsListTrackRelations, usePipelineAnalyticsListTracks, usePipelineAnalyticsOwnersWithMedia, usePipelineAnalyticsPauseForStorageMigration, usePipelineAnalyticsProposeRetrainAnnotations, usePipelineAnalyticsPruneEvents, usePipelineAnalyticsPruneEventsBefore, usePipelineAnalyticsPruneTracksBefore, usePipelineAnalyticsReadLongTermSeries, usePipelineAnalyticsRebuildObjectEmbeddings, usePipelineAnalyticsReclaimDebugMedia, usePipelineAnalyticsReconcileFromDisk, usePipelineAnalyticsRefreshStorageLocationsForMigration, usePipelineAnalyticsRelocateMedia, usePipelineAnalyticsRestageRetrainTrack, usePipelineAnalyticsResumeForStorageMigration, usePipelineAnalyticsSaveRetrainAnnotations, usePipelineAnalyticsSearchObjectEvents, usePipelineAnalyticsSelectRetrainFrames, usePipelineAnalyticsSetRetrainFrameComplete, usePipelineAnalyticsSetTrackFlags, usePipelineAnalyticsSetTrackPurity, usePipelineAnalyticsSetTrackRelation, usePipelineAnalyticsStartStorageMigrationMove, usePipelineAnalyticsWipeAllAnalytics, usePipelineAnalyticsWipeObjectEmbeddings, usePipelineAnalyticsWriteRetrainArchive, usePipelineExecutorCacheFrameInPool, usePipelineExecutorClearDeviceOverrides, usePipelineExecutorClearModelBoundDeviceOverrides, usePipelineExecutorDeleteModel, usePipelineExecutorDownloadModel, usePipelineExecutorGetAddonModels, usePipelineExecutorGetAudioCapabilities, usePipelineExecutorGetAvailableEngines, usePipelineExecutorGetCapabilities, usePipelineExecutorGetDefaultSteps, usePipelineExecutorGetDetectionConfigSchema, usePipelineExecutorGetEffectiveTuning, usePipelineExecutorGetEngineProvisioning, usePipelineExecutorGetGlobalPipelineConfig, usePipelineExecutorGetGlobalSteps, usePipelineExecutorGetInferenceDeviceHealth, usePipelineExecutorGetOrchestratorConfigSchema, usePipelineExecutorGetReferenceAudio, usePipelineExecutorGetReferenceAudioFiles, usePipelineExecutorGetReferenceImage, usePipelineExecutorGetSchema, usePipelineExecutorGetSelectedEngine, usePipelineExecutorGetVideoPipelineSteps, usePipelineExecutorInferCached, usePipelineExecutorKillEngine, usePipelineExecutorListLoadedEngines, usePipelineExecutorListReferenceImages, usePipelineExecutorRearmInferenceDevice, usePipelineExecutorRunAudioTest, usePipelineExecutorRunPipeline, usePipelineExecutorRunPipelineBatch, usePipelineExecutorSetVideoPipelineSteps, usePipelineExecutorSpinEngine, usePipelineExecutorUncacheFrame, usePipelineExecutorValidatePipeline, usePipelineOrchestratorApplyAutoTune, usePipelineOrchestratorApplyDeviceSettingsPatch, usePipelineOrchestratorAssignAudio, usePipelineOrchestratorAssignPipeline, usePipelineOrchestratorGetAgentLoad, usePipelineOrchestratorGetAgentSettings, usePipelineOrchestratorGetAudioAssignment, usePipelineOrchestratorGetAudioAssignments, usePipelineOrchestratorGetAudioNodeLoad, usePipelineOrchestratorGetCameraMetrics, usePipelineOrchestratorGetCameraSettings, usePipelineOrchestratorGetCameraStatus, usePipelineOrchestratorGetCameraStatuses, usePipelineOrchestratorGetCameraStepOverrides, usePipelineOrchestratorGetCameraSwitches, usePipelineOrchestratorGetCapabilityBindings, usePipelineOrchestratorGetClusterModelChoice, usePipelineOrchestratorGetDeviceLiveContribution, usePipelineOrchestratorGetDeviceSettingsContribution, usePipelineOrchestratorGetEffectiveShares, usePipelineOrchestratorGetGlobalMetrics, usePipelineOrchestratorGetIngestOwner, usePipelineOrchestratorGetNodeInferenceDevices, usePipelineOrchestratorGetPipelineAssignment, usePipelineOrchestratorGetPipelineAssignments, usePipelineOrchestratorGetPipelineDevicePin, usePipelineOrchestratorGetPlacementStats, usePipelineOrchestratorGetReconcileFromDiskStatus, usePipelineOrchestratorListAgentSettings, usePipelineOrchestratorPauseForStorageMigration, usePipelineOrchestratorPlanAutoTune, usePipelineOrchestratorRebalance, usePipelineOrchestratorReconcileFromDisk, usePipelineOrchestratorRemoveAgentSettings, usePipelineOrchestratorRequestOccupancyLook, usePipelineOrchestratorResetNodePipelineDefaults, usePipelineOrchestratorResolvePipeline, usePipelineOrchestratorResumeForStorageMigration, usePipelineOrchestratorSetAgentCapabilities, usePipelineOrchestratorSetAgentDecodeLimits, usePipelineOrchestratorSetAgentInferenceDevices, usePipelineOrchestratorSetAgentMaxCameras, usePipelineOrchestratorSetAgentReachableHost, usePipelineOrchestratorSetCameraPipelineForAgent, usePipelineOrchestratorSetCameraSensors, usePipelineOrchestratorSetCameraStepOverride, usePipelineOrchestratorSetCameraStepSettings, usePipelineOrchestratorSetCameraStepToggle, usePipelineOrchestratorSetCameraSwitch, usePipelineOrchestratorSetCapabilityBinding, usePipelineOrchestratorSetLoadBalancingShares, usePipelineOrchestratorSetPipelineDevicePin, usePipelineOrchestratorSetShareSource, usePipelineOrchestratorSuggestShares, usePipelineOrchestratorUnassignAudio, usePipelineOrchestratorUnassignPipeline, usePipelineRunnerAttachCamera, usePipelineRunnerDetachCamera, usePipelineRunnerGetAllCameraMetrics, usePipelineRunnerGetCameraMetrics, usePipelineRunnerGetLocalCameras, usePipelineRunnerGetLocalLoad, usePipelineRunnerGetLocalMetrics, usePipelineRunnerGetNativeCrop, usePipelineRunnerGetParkedTrackFrame, usePipelineRunnerParkTrackFrame, usePipelineRunnerReleaseParkedTrackFrames, usePipelineRunnerReportMotion, usePipelineRunnerRequestOccupancyBurst, usePipelineRunnerRunDetailSubtree, usePipelineRunnerRunStatelessStep, usePlateGalleryAssignPlate, usePlateGalleryAssignPlates, usePlateGalleryCorrectPlateText, usePlateGalleryCreateVehicle, usePlateGalleryDeletePlate, usePlateGalleryDeleteVehicle, usePlateGalleryGetPlateByTrack, usePlateGalleryGetPlateMedia, usePlateGalleryListPlates, usePlateGalleryListVehicleSamples, usePlateGalleryListVehicles, usePlateGalleryRemoveVehicleSample, usePlateGalleryRenameVehicle, usePlateGallerySearchPlates, usePlateGallerySuggestPlateClusters, usePlateGalleryUnassignPlate, usePlateGalleryUnassignPlates, usePlayerOverlayLayer, usePlayerOverlayLayers, usePlayerToolbarButton, usePlayerToolbarButtons, usePowerMeterGetStatus, usePresenceGetStatus, usePressureSensorGetStatus, usePrivacyMaskGetOptions, usePrivacyMaskGetStatus, usePrivacyMaskSetAudioEnabled, usePrivacyMaskSetMask, usePrompt, usePtzAutotrackGetSettings, usePtzAutotrackGetStatus, usePtzAutotrackSetEnabled, usePtzAutotrackSetSettings, usePtzCaptureHomeHere, usePtzContinuousMove, usePtzDeletePreset, usePtzGetHomePreset, usePtzGetHomeReturn, usePtzGetOptions, usePtzGetPosition, usePtzGetPresets, usePtzGetStatus, usePtzGoHome, usePtzGoToPreset, usePtzMove, usePtzSavePreset, usePtzSetAutofocus, usePtzSetHomePreset, usePtzSetHomeReturn, usePtzStop, useRebootReboot, useRecordedPlayback, useRecordingArchiveApplyDeviceSettingsPatch, useRecordingArchiveCancelRelocateJob, useRecordingArchiveCancelStorageMigrationMove, useRecordingArchiveDeleteFootprint, useRecordingArchiveGetAvailabilityBatch, useRecordingArchiveGetDaysWithRecordingsBatch, useRecordingArchiveGetDeviceConfig, useRecordingArchiveGetDeviceLiveContribution, useRecordingArchiveGetDeviceSettingsContribution, useRecordingArchiveGetPlacement, useRecordingArchiveGetRelocateResidue, useRecordingArchiveGetStatus, useRecordingArchiveGetStorageMigrationMoveStatus, useRecordingArchiveGetStorageUsage, useRecordingArchiveListOpsLog, useRecordingArchiveListRelocateJobs, useRecordingArchiveLocateSegment, useRecordingArchivePauseForStorageMigration, useRecordingArchivePlanStorageRebalance, useRecordingArchivePruneFootage, useRecordingArchiveReadGopBytes, useRecordingArchiveReadSegmentBytes, useRecordingArchiveReadWindowBytes, useRecordingArchiveReconcileLedgerAgainstDisk, useRecordingArchiveRefreshStorageLocationsForMigration, useRecordingArchiveRelocateFootage, useRecordingArchiveRenderClip, useRecordingArchiveRenderGif, useRecordingArchiveRescanStorage, useRecordingArchiveResumeForStorageMigration, useRecordingArchiveSetDeviceConfig, useRecordingArchiveSetDevicePlacement, useRecordingArchiveStartStorageMigrationMove, useRecordingArchiveStartStorageRebalance, useRecordingExportCancelExport, useRecordingExportCreateExport, useRecordingExportDeleteExport, useRecordingExportGetDownloadUrl, useRecordingExportGetExport, useRecordingExportListExports, useRecordingExportReadExportBytes, useRecordingGetAvailability, useRecordingGetDaysWithRecordings, useRecordingGetPlayback, useRecordingGetPlaybackOptions, useRecordingListSources, useRecordingOnboardGetOptions, useRecordingOnboardGetStatus, useRecordingOnboardSetSettings, useRecordingSignalGetStatus, useRemoteComponent, useRoleDisplayDefaults, useSceneMonitorCaptureReference, useSceneMonitorCreateScene, useSceneMonitorDeleteReference, useSceneMonitorDeleteScene, useSceneMonitorGetStatus, useSceneMonitorListScenes, useSceneMonitorListScenesBatch, useSceneMonitorRecheckNow, useSceneMonitorResetScene, useSceneMonitorUpdateScene, useScriptRunnerGetStatus, useScriptRunnerRun, useScriptRunnerStop, useScrubController, useServerManagementApplyServerUpdate, useServerManagementCheckServerUpdate, useServerManagementGetServerPackageStatus, useServerManagementRestartServer, useServerManagementRollbackServerUpdate, useSettingsStoreAggregate, useSettingsStoreCount, useSettingsStoreDeclareCollection, useSettingsStoreDelete, useSettingsStoreDeleteWhere, useSettingsStoreGet, useSettingsStoreHistogram, useSettingsStoreInsert, useSettingsStoreInsertMany, useSettingsStoreIsEmpty, useSettingsStoreQuery, useSettingsStoreSet, useSettingsStoreUpdate, useSettingsStoreUpdateWhere, useSettingsStoreUpsert, useSmokeGetStatus, useSnapshotApplyDeviceSettingsPatch, useSnapshotGetDebugState, useSnapshotGetDeviceLiveContribution, useSnapshotGetDeviceSettingsContribution, useSnapshotGetSnapshot, useSnapshotGetSnapshotLinks, useSnapshotGetSnapshotOverview, useSnapshotGetStatus, useSnapshotInvalidateCache, useStationaryOverlay, useStorageAbortUpload, useStorageBeginDownload, useStorageBeginUpload, useStorageDelete, useStorageDeleteLocation, useStorageEndDownload, useStorageExists, useStorageFinalizeUpload, useStorageGetAvailableSpace, useStorageList, useStorageListDrainProgress, useStorageListLocationDeclarations, useStorageListLocations, useStorageListProviders, useStorageMigrationCancel, useStorageMigrationCleanupCancel, useStorageMigrationCleanupStart, useStorageMigrationCleanupStatus, useStorageMigrationDrain, useStorageMigrationHistory, useStorageMigrationMovers, useStorageMigrationPlan, useStorageMigrationResidue, useStorageMigrationStart, useStorageMigrationStatus, useStorageRead, useStorageReadChunk, useStorageResolve, useStorageTestConfig, useStorageTestLocation, useStorageUpsertLocation, useStorageWrite, useStorageWriteChunk, useStoredGroupBy, useStreamBrokerAcquireEgressTranscode, useStreamBrokerApplyDeviceSettingsPatch, useStreamBrokerAssignProfile, useStreamBrokerFetchEventMedia, useStreamBrokerForgetDeviceHardware, useStreamBrokerGetAllRtspEntries, useStreamBrokerGetBrokerStats, useStreamBrokerGetDeviceAudioMute, useStreamBrokerGetDeviceLiveContribution, useStreamBrokerGetDeviceSettingsContribution, useStreamBrokerGetPreBufferInfo, useStreamBrokerGetRtspEntry, useStreamBrokerGetRtspPort, useStreamBrokerGetStreamUrl, useStreamBrokerGetStreamWithCodec, useStreamBrokerIsRtspEnabled, useStreamBrokerKillClient, useStreamBrokerListAllCameraStreams, useStreamBrokerListAllProfileSlots, useStreamBrokerListClients, useStreamBrokerProbeStream, useStreamBrokerProduceEventMedia, useStreamBrokerPublishCameraStream, useStreamBrokerPullAudioChunks, useStreamBrokerRegenerateRtspToken, useStreamBrokerReleaseEgressTranscode, useStreamBrokerReleaseStreamWithCodec, useStreamBrokerRenderPreBufferClip, useStreamBrokerReportStreamSignal, useStreamBrokerRestartProfile, useStreamBrokerRetractCameraStream, useStreamBrokerSetDeviceAudioMute, useStreamBrokerSetPreBufferDuration, useStreamBrokerSetRtspEnabled, useStreamBrokerSubscribeAudioChunks, useStreamBrokerUnassignProfile, useStreamBrokerUnsubscribeAudioChunks, useStreamCatalogGetCatalog, useStreamParamsGetConfigSchema, useStreamParamsGetOptions, useStreamParamsGetStatus, useStreamParamsSetProfile, useStreamSignalsListSignals, useSwitchGetStatus, useSwitchSetState, useSystem, useSystemDetectSiteLocation, useSystemFeatureFlags, useSystemForceRetentionCleanup, useSystemGetFailureContributions, useSystemGetLoadContributions, useSystemGetLoggingSettings, useSystemGetRequestCensus, useSystemGetRetentionConfig, useSystemGetSiteLocation, useSystemHealth, useSystemInfo, useSystemMutation, useSystemNetworkAddresses, useSystemQuery, useSystemSetLoggingSettings, useSystemSetRetentionConfig, useSystemSetSiteLocation, useTamperGetStatus, useTemperatureSensorGetStatus, useTerminalSessionAdoptLegacyMonitor, useTerminalSessionClose, useTerminalSessionCreateInstance, useTerminalSessionDeleteInstance, useTerminalSessionListInstances, useTerminalSessionListLegacyCameras, useTerminalSessionListProfiles, useTerminalSessionListSessions, useTerminalSessionOpenSession, useTerminalSessionPullOutput, useTerminalSessionResize, useTerminalSessionSetInstanceEnabled, useTerminalSessionUpdateInstance, useTerminalSessionWriteInput, useThemeMode, useToastOnToast, useTurnProviderGetTurnServers, useUpdateGetStatus, useUpdateInstallUpdate, useUrlQueryParam, useUserManagementConfirmTotp, useUserManagementCreateApiKey, useUserManagementCreateScopedToken, useUserManagementCreateUser, useUserManagementDeleteUser, useUserManagementDisableTotp, useUserManagementFindUserByExternalIdentity, useUserManagementGetTotpStatus, useUserManagementLinkExternalIdentity, useUserManagementListApiKeys, useUserManagementListExternalIdentities, useUserManagementListOauthSessions, useUserManagementListScopedTokens, useUserManagementListUsers, useUserManagementOauthExchangeCode, useUserManagementOauthIssueCode, useUserManagementOauthRefresh, useUserManagementOauthVerifyAccessToken, useUserManagementResetPassword, useUserManagementRevokeApiKey, useUserManagementRevokeOauthSession, useUserManagementRevokeScopedToken, useUserManagementSetUserScopes, useUserManagementSetupTotp, useUserManagementUnlinkExternalIdentity, useUserManagementUpdateUser, useUserManagementValidateApiKey, useUserManagementValidateCredentials, useUserManagementValidateScopedToken, useUserManagementVerifyTotp, useVacuumControlGetStatus, useVacuumControlLocate, useVacuumControlPause, useVacuumControlReturnToBase, useVacuumControlSetFanSpeed, useVacuumControlStart, useVacuumControlStop, useValveClose, useValveGetStatus, useValveOpen, useValveSetPosition, useValveStop, useVibrationGetStatus, useVideoclipsDialClipStream, useVideoclipsGetClipPlayback, useVideoclipsGetPlaybackOptions, useVideoclipsListClips, useVideoclipsListSources, useVideoclipsOfferClipBytes, useVideoclipsReadClipBytes, useVodPlayback, useWaterHeaterGetStatus, useWaterHeaterSetAway, useWaterHeaterSetOperationMode, useWaterHeaterSetTargetTemp, useWeatherGetStatus, useWebrtcSessionAddIceCandidate, useWebrtcSessionApplyDownlinkProbe, useWebrtcSessionCloseSession, useWebrtcSessionCreateSession, useWebrtcSessionGetIceCandidates, useWebrtcSessionGetSessionDebug, useWebrtcSessionGetSessionState, useWebrtcSessionHandleAnswer, useWebrtcSessionHandleOffer, useWebrtcSessionHasAdaptiveBitrate, useWebrtcSessionListStreams, useWidget, useWidgetMetadata, useWidgetRegistry, useZoneAnalyticsGetCameraHistory, useZoneAnalyticsGetCurrentSnapshot, useZoneAnalyticsGetCurrentSnapshotBatch, useZoneAnalyticsGetUnzonedHistory, useZoneAnalyticsGetZoneHistory, useZoneEditing, useZoneRulesListRules, useZoneRulesSetRules, useZonesAddZone, useZonesListZones, useZonesRemoveZone, useZonesUpdateZone, vacuumStateMeta, validateScopes, valveStateMeta, variantLabel, waterHeaterPhase, waterHeaterTint, weatherConditionMeta, weatherTint, withResolvedParentDetail, withUseRootModel, writeStoredGroupBy };