@camstack/ui-library 0.1.57 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/composites/addon-global-settings-form.d.ts +1 -1
  2. package/dist/composites/agent-step-editor.d.ts +1 -1
  3. package/dist/composites/app-shell/app-shell.d.ts +1 -1
  4. package/dist/composites/app-shell/sidebar-item.d.ts +1 -1
  5. package/dist/composites/app-shell/sidebar.d.ts +1 -1
  6. package/dist/composites/audio-classification-list.d.ts +1 -1
  7. package/dist/composites/audio-level-waveform.d.ts +1 -1
  8. package/dist/composites/audio-waveform.d.ts +1 -1
  9. package/dist/composites/battery-badge.d.ts +4 -1
  10. package/dist/composites/breadcrumb.d.ts +2 -1
  11. package/dist/composites/camera-stream-player.d.ts +94 -2
  12. package/dist/composites/cap-settings/AutotrackSection.d.ts +1 -1
  13. package/dist/composites/cap-settings/ConsumablesPanel.d.ts +2 -0
  14. package/dist/composites/cap-settings/CoverageTrack.d.ts +12 -0
  15. package/dist/composites/cap-settings/EventBucketStrip.d.ts +25 -0
  16. package/dist/composites/cap-settings/EventHeatmap.d.ts +10 -0
  17. package/dist/composites/cap-settings/MaskShapeCanvas.d.ts +1 -1
  18. package/dist/composites/cap-settings/MotionZonesSettings.d.ts +1 -1
  19. package/dist/composites/cap-settings/PrivacyMaskSettings.d.ts +1 -1
  20. package/dist/composites/cap-settings/PtzPanel.d.ts +1 -1
  21. package/dist/composites/cap-settings/RecordingPanel.d.ts +2 -0
  22. package/dist/composites/cap-settings/RecordingRulesEditor.d.ts +7 -0
  23. package/dist/composites/cap-settings/RecordingSettings.d.ts +8 -0
  24. package/dist/composites/cap-settings/RecordingTimeline.d.ts +5 -0
  25. package/dist/composites/cap-settings/ScheduleBandsEditor.d.ts +7 -0
  26. package/dist/composites/cap-settings/TimelineControls.d.ts +21 -0
  27. package/dist/composites/cap-settings/event-thumb.d.ts +11 -0
  28. package/dist/composites/cap-settings/index.d.ts +4 -0
  29. package/dist/composites/cap-settings/recording-config-form.d.ts +34 -0
  30. package/dist/composites/cap-settings/recording-spans.d.ts +22 -0
  31. package/dist/composites/cap-settings/recording-timeline-model.d.ts +84 -0
  32. package/dist/composites/code-block.d.ts +1 -1
  33. package/dist/composites/config-form-builder.d.ts +1 -1
  34. package/dist/composites/config-form-field.d.ts +3 -3
  35. package/dist/composites/confirm-action-button.d.ts +1 -1
  36. package/dist/composites/confirm-dialog.d.ts +1 -1
  37. package/dist/composites/data-table/data-table-header.d.ts +1 -1
  38. package/dist/composites/data-table/data-table-pagination.d.ts +1 -1
  39. package/dist/composites/data-table/data-table-row.d.ts +1 -1
  40. package/dist/composites/data-table/data-table.d.ts +1 -1
  41. package/dist/composites/data-table.d.ts +1 -1
  42. package/dist/composites/detection-canvas.d.ts +1 -1
  43. package/dist/composites/detection-overlay.d.ts +1 -1
  44. package/dist/composites/detection-result-tree.d.ts +1 -1
  45. package/dist/composites/dev-shell.d.ts +1 -1
  46. package/dist/composites/device-activity-panel.d.ts +5 -1
  47. package/dist/composites/device-card.d.ts +1 -1
  48. package/dist/composites/device-controls/alarm-hero-card.d.ts +24 -0
  49. package/dist/composites/device-controls/atoms.d.ts +81 -0
  50. package/dist/composites/device-controls/brightness-panel.d.ts +3 -0
  51. package/dist/composites/device-controls/button-control.d.ts +18 -0
  52. package/dist/composites/device-controls/climate-panel.d.ts +16 -0
  53. package/dist/composites/device-controls/container-primary-hero.d.ts +24 -0
  54. package/dist/composites/device-controls/container-primary.d.ts +46 -0
  55. package/dist/composites/device-controls/control-panel.d.ts +10 -0
  56. package/dist/composites/device-controls/control-registry.d.ts +74 -0
  57. package/dist/composites/device-controls/cover-hero-card.d.ts +27 -0
  58. package/dist/composites/device-controls/cover-inline.d.ts +27 -0
  59. package/dist/composites/device-controls/cover-panel.d.ts +3 -0
  60. package/dist/composites/device-controls/dummy-hero-card.d.ts +6 -0
  61. package/dist/composites/device-controls/fan-hero-card.d.ts +21 -0
  62. package/dist/composites/device-controls/fan-panel.d.ts +3 -0
  63. package/dist/composites/device-controls/humidifier-control.d.ts +37 -0
  64. package/dist/composites/device-controls/image-control.d.ts +24 -0
  65. package/dist/composites/device-controls/index.d.ts +33 -0
  66. package/dist/composites/device-controls/lawn-mower-control.d.ts +24 -0
  67. package/dist/composites/device-controls/light-hero-card.d.ts +16 -0
  68. package/dist/composites/device-controls/lock-hero-card.d.ts +15 -0
  69. package/dist/composites/device-controls/lock-panel.d.ts +3 -0
  70. package/dist/composites/device-controls/media-player-hero-card.d.ts +17 -0
  71. package/dist/composites/device-controls/media-player-panel.d.ts +9 -0
  72. package/dist/composites/device-controls/offline-badge.d.ts +11 -0
  73. package/dist/composites/device-controls/panel-controls.d.ts +17 -0
  74. package/dist/composites/device-controls/popover-row-action.d.ts +9 -0
  75. package/dist/composites/device-controls/primary-child.d.ts +18 -0
  76. package/dist/composites/device-controls/radial-gauge.d.ts +20 -0
  77. package/dist/composites/device-controls/sensor-hero-card.d.ts +10 -0
  78. package/dist/composites/device-controls/sensor-inline-control.d.ts +11 -0
  79. package/dist/composites/device-controls/sensor-value-atom.d.ts +106 -0
  80. package/dist/composites/device-controls/switch-hero-card.d.ts +13 -0
  81. package/dist/composites/device-controls/switch-panel.d.ts +3 -0
  82. package/dist/composites/device-controls/tap-toggle.d.ts +17 -0
  83. package/dist/composites/device-controls/thermostat-hero-card.d.ts +17 -0
  84. package/dist/composites/device-controls/types.d.ts +17 -0
  85. package/dist/composites/device-controls/update-control.d.ts +11 -0
  86. package/dist/composites/device-controls/vacuum-control.d.ts +36 -0
  87. package/dist/composites/device-controls/valve-control.d.ts +46 -0
  88. package/dist/composites/device-controls/water-heater-control.d.ts +41 -0
  89. package/dist/composites/device-controls/weather-control.d.ts +41 -0
  90. package/dist/composites/device-export-panel.d.ts +1 -1
  91. package/dist/composites/device-grid.d.ts +1 -1
  92. package/dist/composites/device-item/actions.d.ts +12 -1
  93. package/dist/composites/device-item/child-layout.d.ts +32 -0
  94. package/dist/composites/device-item/child-section-accordion.d.ts +9 -0
  95. package/dist/composites/device-item/children-accordion.d.ts +1 -1
  96. package/dist/composites/device-item/container-children-context.d.ts +15 -0
  97. package/dist/composites/device-item/device-delete-action.d.ts +8 -0
  98. package/dist/composites/device-item/header.d.ts +8 -1
  99. package/dist/composites/device-item/helpers.d.ts +108 -2
  100. package/dist/composites/device-item/index.d.ts +4 -0
  101. package/dist/composites/device-item/preview.d.ts +3 -3
  102. package/dist/composites/device-item/reboot-quick-action.d.ts +9 -0
  103. package/dist/composites/device-item/status-dot.d.ts +4 -1
  104. package/dist/composites/device-list/batch-actions-bar.d.ts +15 -0
  105. package/dist/composites/device-list/batch-toolbar.d.ts +15 -0
  106. package/dist/composites/device-list/cards-layout.d.ts +18 -0
  107. package/dist/composites/device-list/columns.d.ts +68 -0
  108. package/dist/composites/device-list/device-mode.d.ts +115 -0
  109. package/dist/composites/device-list/filter-chips.d.ts +15 -6
  110. package/dist/composites/device-list/fuzzy-match.d.ts +27 -0
  111. package/dist/composites/device-list/generic-mode.d.ts +81 -0
  112. package/dist/composites/device-list/group.d.ts +19 -0
  113. package/dist/composites/device-list/hardware-cell.d.ts +7 -0
  114. package/dist/composites/device-list/hardware.d.ts +26 -0
  115. package/dist/composites/device-list/icon-action.d.ts +17 -0
  116. package/dist/composites/device-list/index.d.ts +23 -39
  117. package/dist/composites/device-list/multi-select.d.ts +22 -0
  118. package/dist/composites/device-list/sort.d.ts +18 -0
  119. package/dist/composites/device-list/sortable-header.d.ts +10 -0
  120. package/dist/composites/device-list/table-layout.d.ts +25 -0
  121. package/dist/composites/device-list/type-filter.d.ts +19 -0
  122. package/dist/composites/device-list/url-state.d.ts +7 -2
  123. package/dist/composites/device-meta.d.ts +38 -0
  124. package/dist/composites/discovery-panel.d.ts +4 -4
  125. package/dist/composites/doorbell-recent-panel.d.ts +1 -1
  126. package/dist/composites/empty-state.d.ts +1 -1
  127. package/dist/composites/error-box.d.ts +1 -1
  128. package/dist/composites/event-stream.d.ts +2 -2
  129. package/dist/composites/filter-bar.d.ts +1 -1
  130. package/dist/composites/form-field.d.ts +1 -1
  131. package/dist/composites/hls-quality.d.ts +35 -0
  132. package/dist/composites/hls-video.d.ts +26 -0
  133. package/dist/composites/hover-zoom-image.d.ts +18 -0
  134. package/dist/composites/image-selector.d.ts +1 -1
  135. package/dist/composites/index.d.ts +15 -4
  136. package/dist/composites/inference-config-selector.d.ts +1 -1
  137. package/dist/composites/kebab-menu.d.ts +1 -1
  138. package/dist/composites/key-value-list.d.ts +1 -1
  139. package/dist/composites/log-stream-scroll.d.ts +32 -0
  140. package/dist/composites/log-stream.d.ts +9 -2
  141. package/dist/composites/login-form.d.ts +1 -1
  142. package/dist/composites/node-picker.d.ts +1 -1
  143. package/dist/composites/page-header.d.ts +1 -1
  144. package/dist/composites/phase-icon.d.ts +1 -1
  145. package/dist/composites/pipeline-builder.d.ts +1 -1
  146. package/dist/composites/pipeline-runtime-selector.d.ts +1 -1
  147. package/dist/composites/pipeline-step.d.ts +3 -3
  148. package/dist/composites/pipeline-tree-matrix.d.ts +1 -1
  149. package/dist/composites/provider-badge.d.ts +1 -1
  150. package/dist/composites/ptz-overlay.d.ts +1 -1
  151. package/dist/composites/qr-code.d.ts +1 -1
  152. package/dist/composites/response-log.d.ts +1 -1
  153. package/dist/composites/scope-picker.d.ts +1 -1
  154. package/dist/composites/slide-over-panel.d.ts +1 -1
  155. package/dist/composites/snapshot-button.d.ts +1 -1
  156. package/dist/composites/stat-card.d.ts +1 -1
  157. package/dist/composites/state-values-stream.d.ts +1 -1
  158. package/dist/composites/status-badge.d.ts +1 -1
  159. package/dist/composites/step-timings.d.ts +1 -1
  160. package/dist/composites/step-tree-master.d.ts +1 -1
  161. package/dist/composites/stream-panel.d.ts +45 -10
  162. package/dist/composites/timezone-selector.d.ts +18 -0
  163. package/dist/composites/version-badge.d.ts +2 -2
  164. package/dist/composites/widget-panel.d.ts +28 -0
  165. package/dist/contexts/custom-field-renderers.d.ts +1 -1
  166. package/dist/contexts/device-context.d.ts +1 -1
  167. package/dist/contexts/player-overlays.d.ts +1 -1
  168. package/dist/contexts/system-context.d.ts +1 -1
  169. package/dist/contexts/vod-playback.d.ts +17 -0
  170. package/dist/contexts/widget-registry.d.ts +1 -1
  171. package/dist/contexts/zone-editing.d.ts +1 -1
  172. package/dist/generated/system-hooks.d.ts +386 -56
  173. package/dist/hooks/index.d.ts +8 -2
  174. package/dist/hooks/use-device-cap-slice.d.ts +19 -0
  175. package/dist/hooks/use-device-capability.d.ts +12 -0
  176. package/dist/hooks/use-device-list-page-size.d.ts +14 -0
  177. package/dist/hooks/use-device-webrtc.d.ts +36 -4
  178. package/dist/hooks/use-optimistic-slice.d.ts +11 -0
  179. package/dist/index.cjs +54834 -11195
  180. package/dist/index.d.ts +1 -0
  181. package/dist/index.js +54483 -11136
  182. package/dist/lib/format-control-datetime.d.ts +18 -0
  183. package/dist/lib/format-last-seen.d.ts +12 -0
  184. package/dist/lib/format-numeric.d.ts +9 -0
  185. package/dist/lib/index.d.ts +3 -0
  186. package/dist/primitives/bottom-sheet.d.ts +1 -1
  187. package/dist/primitives/collapsible-card.d.ts +1 -1
  188. package/dist/primitives/dialog.d.ts +19 -6
  189. package/dist/primitives/dropdown.d.ts +3 -3
  190. package/dist/primitives/floating-panel.d.ts +1 -1
  191. package/dist/primitives/mobile-drawer.d.ts +1 -1
  192. package/dist/primitives/popover.d.ts +3 -3
  193. package/dist/primitives/tabs.d.ts +4 -4
  194. package/dist/primitives/tooltip.d.ts +11 -5
  195. package/dist/theme/index.cjs +0 -3
  196. package/dist/theme/index.js +0 -2
  197. package/dist/theme/theme-provider.d.ts +1 -1
  198. package/package.json +13 -5
  199. package/dist/index.cjs.map +0 -1
  200. package/dist/index.js.map +0 -1
  201. package/dist/theme/index.cjs.map +0 -1
  202. package/dist/theme/index.js.map +0 -1
@@ -0,0 +1,17 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /**
4
+ * MediaPlayerHeroCard — now-playing hero for MediaPlayer devices.
5
+ *
6
+ * Renders (Spec §3):
7
+ * - Now-playing block: cover glyph + title + artist/album.
8
+ * - Seek GripTrack with a native range overlay (gated on DeviceFeature
9
+ * MediaPlayerSeek + a known duration).
10
+ * - Transport row: SkipBack / Play-Pause / SkipForward (lucide icons). Skip
11
+ * buttons are gated on MediaPlayerPrevious / MediaPlayerNext.
12
+ * - Volume GripTrack with a native range overlay + mute toggle (gated on a
13
+ * non-null volume surface + DeviceFeature.MediaPlayerVolume).
14
+ *
15
+ * Cap-gated on `media-player` — returns null when the device lacks the cap.
16
+ */
17
+ export declare function MediaPlayerHeroCard({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ import { ControlPanelProps } from './types.js';
3
+ import { MediaPlayerState } from '@camstack/types';
4
+ /**
5
+ * Exhaustive colour map — one entry per MediaPlayerState.
6
+ * Build fails if a state is added and not reflected here.
7
+ */
8
+ export declare const STATE_COLOR: Record<MediaPlayerState, string>;
9
+ export declare function MediaPlayerPanel({ trpc, deviceId, features: _features, layout, optimistic, }: ControlPanelProps): ReactNode;
@@ -0,0 +1,11 @@
1
+ import { ReactElement } from 'react';
2
+ export interface OfflineBadgeProps {
3
+ /**
4
+ * Ms epoch of the last online→offline transition (device-status
5
+ * `lastChangedAt`). `null`/`undefined`/non-positive → no "last seen"
6
+ * suffix is shown.
7
+ */
8
+ readonly lastChangedAt?: number | null;
9
+ readonly className?: string;
10
+ }
11
+ export declare function OfflineBadge({ lastChangedAt, className }: OfflineBadgeProps): ReactElement;
@@ -0,0 +1,17 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ export { ThermostatHeroCard } from './thermostat-hero-card';
4
+ export { LockHeroCard } from './lock-hero-card';
5
+ export { LightHeroCard } from './light-hero-card';
6
+ export { FanHeroCard } from './fan-hero-card';
7
+ export { SwitchHeroCard } from './switch-hero-card';
8
+ export { MediaPlayerHeroCard } from './media-player-hero-card';
9
+ export { AlarmHeroCard, AlarmInlineControl as AlarmPanelInlineControl } from './alarm-hero-card';
10
+ export declare function SwitchInlineControl({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
11
+ export declare function LockInlineControl({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
12
+ export declare function LightInlineControl({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
13
+ export declare function FanInlineControl({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
14
+ export declare function ThermostatInlineControl({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
15
+ export declare function MediaPlayerInlineControl({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
16
+ export declare function ControlInlineControl({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
17
+ export declare function ControlHeroCard({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ import { LucideIcon } from 'lucide-react';
3
+ interface PopoverRowActionProps {
4
+ readonly icon: LucideIcon;
5
+ readonly title: string;
6
+ readonly children: ReactNode;
7
+ }
8
+ export declare function PopoverRowAction({ icon: Icon, title, children, }: PopoverRowActionProps): ReactNode;
9
+ export {};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Pure resolver: given a container's entity-children and an optional picker
3
+ * override (stored as an entityId string, re-sync-stable), returns the child
4
+ * that should be rendered as the container's PRIMARY child.
5
+ *
6
+ * Design note: ChildRef.type is kept as `string` (not `DeviceType`) to decouple
7
+ * this pure resolver from the types enum. The resolver is tolerant of unknown
8
+ * type names — they sort after all known PRIORITY entries. This avoids creating
9
+ * a hard import dependency between the resolver and the enum, and handles future
10
+ * entity types gracefully.
11
+ */
12
+ export declare const PRIORITY: readonly string[];
13
+ export interface ChildRef {
14
+ readonly id: number;
15
+ readonly entityId: string;
16
+ readonly type: string;
17
+ }
18
+ export declare function resolvePrimaryChild(children: readonly ChildRef[], overrideEntityId: string | undefined): ChildRef | null;
@@ -0,0 +1,20 @@
1
+ import { ReactNode } from 'react';
2
+ export interface RadialGaugeProps {
3
+ /** Fill fraction 0..1 (brightness or speed level). */
4
+ readonly fraction: number;
5
+ /** Hex/CSS colour for the fill arc + knob border. */
6
+ readonly color: string;
7
+ /** Centered content (icon + value readout). */
8
+ readonly children: ReactNode;
9
+ /**
10
+ * When provided, the ArcKnob becomes draggable: dragging it reports the new
11
+ * arc fraction (0..1) to the parent, which owns the value and re-renders the
12
+ * gauge at the new `fraction`. When omitted the knob is display-only.
13
+ */
14
+ readonly onDragFraction?: (fraction: number) => void;
15
+ }
16
+ /**
17
+ * Circular gauge with an ArcKnob at `fraction` and centered children.
18
+ * The knob wrapper carries `data-testid="arc-knob"` for test assertions.
19
+ */
20
+ export declare function RadialGauge({ fraction, color, children, onDragFraction, }: RadialGaugeProps): ReactNode;
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /**
4
+ * SensorHeroCard — full hero card for the device-detail area.
5
+ *
6
+ * Fetches the device row (name, role, sourceInfo) via React Query
7
+ * (`deviceManager.getDevice`), then delegates rendering to the inner
8
+ * component that reads the live state slice via the proxy.
9
+ */
10
+ export declare function SensorHeroCard({ trpc, deviceId }: DeviceControlProps): ReactNode;
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /**
4
+ * Registry InlineControl for Sensor and Presence device types.
5
+ *
6
+ * Fetches the device row once (React Query cache — no waterfall for
7
+ * repeated renders), maps to DeviceItemDevice, and delegates rendering
8
+ * to the existing SensorValueAtom so both the row special-case path and
9
+ * the registry path stay in sync.
10
+ */
11
+ export declare function SensorInlineControl({ trpc, deviceId }: DeviceControlProps): ReactNode;
@@ -0,0 +1,106 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceProxy, SliceHandle } from '@camstack/types';
3
+ import { UseDeviceProxyTrpc } from '../../hooks/use-device-proxy';
4
+ import { ControlDateTimeFormat } from '../../lib/format-control-datetime';
5
+ import { DeviceItemDevice } from '../device-item/helpers';
6
+ interface NumericDescriptor {
7
+ readonly kind: 'numeric';
8
+ /** Field in the slice that holds the numeric value. */
9
+ readonly field: string;
10
+ /** Canonical display unit for TYPED roles (temperature → '°C', humidity → '%',
11
+ * ambient-light → 'lx', pressure → 'hPa'). When present the UI renders this
12
+ * directly WITHOUT consulting the slice or sourceInfo.
13
+ * For the generic `numeric-sensor` role this is `undefined` — the unit is
14
+ * read live from the slice (`slice.unit`). */
15
+ readonly canonicalUnit?: string;
16
+ /** Accessor into DeviceProxy['state'] — returns a typed slice handle. */
17
+ readonly getSliceHandle: (state: DeviceProxy['state']) => SliceHandle<unknown>;
18
+ }
19
+ interface BinaryDescriptor {
20
+ readonly kind: 'binary';
21
+ /** Field in the slice that holds the boolean trigger. */
22
+ readonly field: string;
23
+ /** Label shown when the boolean field is truthy. */
24
+ readonly onLabel: string;
25
+ /** Label shown when the boolean field is falsy. */
26
+ readonly offLabel: string;
27
+ readonly getSliceHandle: (state: DeviceProxy['state']) => SliceHandle<unknown>;
28
+ }
29
+ interface PresenceDescriptor {
30
+ readonly kind: 'presence';
31
+ readonly getSliceHandle: (state: DeviceProxy['state']) => SliceHandle<unknown>;
32
+ }
33
+ /**
34
+ * String-valued sensor whose slice holds `{ value: string, lastFetchedAt }`.
35
+ * Used for HA free-text sensors (IP address, SSID, firmware version, …) that
36
+ * have no numeric device_class and whose state is a plain string.
37
+ */
38
+ interface StringDescriptor {
39
+ readonly kind: 'string';
40
+ readonly getSliceHandle: (state: DeviceProxy['state']) => SliceHandle<unknown>;
41
+ }
42
+ /**
43
+ * Date / timestamp sensor whose slice holds `{ value: string, format? }`
44
+ * carrying a raw ISO string (e.g. '2025-12-02T09:36:12+00:00'). Rendered
45
+ * locale-formatted via `formatControlDateTime`. The slice's own `format` (seeded
46
+ * by the HA provider from the entity's `device_class` — `timestamp` → 'datetime',
47
+ * `date` → 'date', time-only → 'time') selects the shape; `descriptor.format` is
48
+ * the fallback for slices adopted before the field was threaded ('datetime').
49
+ */
50
+ interface DateTimeDescriptor {
51
+ readonly kind: 'datetime';
52
+ readonly format: ControlDateTimeFormat;
53
+ readonly getSliceHandle: (state: DeviceProxy['state']) => SliceHandle<unknown>;
54
+ }
55
+ export type SensorDescriptor = NumericDescriptor | BinaryDescriptor | PresenceDescriptor | StringDescriptor | DateTimeDescriptor;
56
+ /**
57
+ * Binary fallback for the `battery-sensor` role.
58
+ *
59
+ * HA assigns role `battery-sensor` to BOTH numeric battery entities
60
+ * (`sensor.*` device_class=battery → cap `numeric-sensor`) AND binary battery
61
+ * entities (`binary_sensor.*` device_class=battery → cap `binary`, e.g. a
62
+ * HomeMatic `LOW_BAT` flag). When the numeric slice is absent the binary slice
63
+ * is rendered instead, with battery-specific polarity: `on:true` means LOW
64
+ * (bad → warning style + "Low"), `on:false` means Normal (neutral).
65
+ */
66
+ export declare const BINARY_BATTERY_DESCRIPTOR: BinaryDescriptor;
67
+ /** True for the role that can resolve to either a numeric or a binary slice. */
68
+ export declare function isBatterySensorRole(role: string | null | undefined): boolean;
69
+ /**
70
+ * A numeric slice counts as "present" only when it carries a real numeric value
71
+ * at the descriptor field — a cold-start slice (`{ value: 0, lastFetchedAt: 0 }`)
72
+ * or a binary slice (no numeric field) does NOT, so the binary fallback wins.
73
+ */
74
+ export declare function hasBatteryNumericValue(slice: unknown, field: string): boolean;
75
+ /**
76
+ * Keyed by DeviceRole string value. Every entry maps a role to the
77
+ * exact proxy state accessor + rendering instructions. The accessor
78
+ * avoids dynamic string indexing — each one is a concrete property
79
+ * access on the typed `DeviceProxy['state']` shape.
80
+ *
81
+ * Canonical unit rules:
82
+ * - Typed roles (temperature/humidity/ambient-light/pressure/power/energy)
83
+ * carry a `canonicalUnit` constant. The slice is normalised to one unit
84
+ * so no per-device lookup is needed.
85
+ * - Generic `numeric-sensor` has no `canonicalUnit`. The unit is read live
86
+ * from the slice (`slice.unit`) as populated by the provider from the
87
+ * upstream source on every state push.
88
+ */
89
+ export declare const ROLE_DESCRIPTOR: Record<string, SensorDescriptor>;
90
+ export interface StringSliceValue {
91
+ readonly value: string;
92
+ }
93
+ export declare function isStringSliceValue(v: unknown): v is StringSliceValue;
94
+ /**
95
+ * The enum-sensor slice carries an optional `format` ('date' | 'time' |
96
+ * 'datetime') for `DateTimeSensor`-role sensors. Read it off the live slice so
97
+ * each sensor renders its true shape; returns `undefined` when absent (older
98
+ * adoptions) so the caller falls back to the descriptor default.
99
+ */
100
+ export declare function readSliceDateTimeFormat(slice: unknown): ControlDateTimeFormat | undefined;
101
+ export interface SensorValueAtomProps {
102
+ readonly trpc: UseDeviceProxyTrpc;
103
+ readonly device: DeviceItemDevice;
104
+ }
105
+ export declare function SensorValueAtom({ trpc, device }: SensorValueAtomProps): ReactNode;
106
+ export {};
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /**
4
+ * SwitchHeroCard — large tap-toggle hero for Switch / Siren devices.
5
+ *
6
+ * Renders (Spec §3):
7
+ * - A power glyph tinted by on/off state (success when on, muted when off).
8
+ * - A large TAP TOGGLE for on/off — a single tap flips state. This is NOT a
9
+ * SlideToggle; slide-to-confirm is reserved for safety-gated locks ONLY.
10
+ *
11
+ * Cap-gated on `switch` — returns null when the device has no switch cap.
12
+ */
13
+ export declare function SwitchHeroCard({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
@@ -0,0 +1,3 @@
1
+ import { ReactNode } from 'react';
2
+ import { ControlPanelProps } from './types';
3
+ export declare function SwitchPanel({ trpc, deviceId, optimistic }: ControlPanelProps): ReactNode;
@@ -0,0 +1,17 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * TapToggle — plain tap-to-toggle on/off control.
4
+ *
5
+ * Spec §3: everyday on/off (switch, light, fan) uses a plain TAP TOGGLE.
6
+ * slide-to-confirm is reserved for safety-gated actions (lock) ONLY. This
7
+ * component must NEVER be built on `SlideToggle` — a single tap flips state.
8
+ *
9
+ * Presentational: the caller owns the cap mutation + optimistic update via
10
+ * `onToggle`. Mirrors the `SwitchPanel` button treatment at hero scale.
11
+ */
12
+ export interface TapToggleProps {
13
+ readonly on: boolean;
14
+ readonly busy: boolean;
15
+ readonly onToggle: () => void;
16
+ }
17
+ export declare function TapToggle({ on, busy, onToggle }: TapToggleProps): ReactNode;
@@ -0,0 +1,17 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /**
4
+ * ThermostatHeroCard — circular gauge hero for thermostat / HVAC devices.
5
+ *
6
+ * Renders:
7
+ * - SVG ring gauge: track + fill arc representing the setpoint fraction
8
+ * - Centered current temperature (large, prominent)
9
+ * - ArcKnob(s) at the setpoint fraction(s) riding the ring
10
+ * - Single knob for heat / cool / auto / dry modes
11
+ * - Two knobs (low + high) with a band fill for heat_cool mode
12
+ * - Stepper (single) or "Low / High" display (range) for the setpoint
13
+ * - Mode select
14
+ *
15
+ * Cap-gated: returns null when the device has no climateControl cap.
16
+ */
17
+ export declare function ThermostatHeroCard({ trpc, deviceId, features: _features, optimistic, }: DeviceControlProps): ReactNode;
@@ -0,0 +1,17 @@
1
+ import { UseDeviceProxyTrpc } from '../../hooks/use-device-proxy';
2
+ /** Shared props for every control panel. `layout` selects the compact (row
3
+ * popover) vs full (detail tab) presentation. `features` gates sub-controls
4
+ * (e.g. cover position/tilt, fan speed/preset). */
5
+ export interface ControlPanelProps {
6
+ readonly trpc: UseDeviceProxyTrpc;
7
+ readonly deviceId: number;
8
+ readonly features: readonly string[] | undefined;
9
+ readonly layout: 'compact' | 'full';
10
+ /** Enable optimistic UI mode (default: true). Set to false to disable. */
11
+ readonly optimistic?: boolean;
12
+ }
13
+ /** Cap names whose presence (bound on a device) means it has a control surface.
14
+ * Single source of truth shared by the row registry and the detail Controls tab
15
+ * so the two never drift. */
16
+ export declare const CONTROL_CAP_NAMES: readonly ["cover", "lock-control", "switch", "brightness", "fan-control", "climate-control", "media-player", "control"];
17
+ export type ControlCapName = (typeof CONTROL_CAP_NAMES)[number];
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /**
4
+ * UpdateInlineControl — compact firmware state for a device-list row.
5
+ */
6
+ export declare function UpdateInlineControl({ trpc, deviceId }: DeviceControlProps): ReactNode;
7
+ /**
8
+ * UpdateHeroCard — version transition + Install action for the device-detail
9
+ * hero area.
10
+ */
11
+ export declare function UpdateHeroCard({ trpc, deviceId }: DeviceControlProps): ReactNode;
@@ -0,0 +1,36 @@
1
+ import { ReactNode } from 'react';
2
+ import { VacuumState, TankStatus } from '@camstack/types';
3
+ import { DeviceControlProps } from './control-registry';
4
+ export interface VacuumStateMeta {
5
+ readonly label: string;
6
+ /** Tailwind text-colour token for the state label. */
7
+ readonly tone: string;
8
+ }
9
+ /**
10
+ * Pure helper — maps a VacuumState (or undefined when no slice yet) to a
11
+ * human label + colour tone. Exhaustive switch: a new enum member fails
12
+ * the build.
13
+ */
14
+ export declare function vacuumStateMeta(state: VacuumState | undefined): VacuumStateMeta;
15
+ /** The four intrinsic tanks a vacuum cap can expose. */
16
+ export type TankKind = 'cleanWater' | 'dirtyWater' | 'detergent' | 'dustBin';
17
+ export type TankAlert = 'ok' | 'warning';
18
+ /**
19
+ * Pure helper — classifies a tank as `ok` or `warning`.
20
+ *
21
+ * Clean-water + detergent are "supply" tanks: low is bad. Dirty-water +
22
+ * dust-bin are "waste" tanks: full is bad. Prefers the numeric `level`
23
+ * (thresholds 15 / 85), falling back to the discrete `status` flag. Both
24
+ * unknown → `ok`. Exhaustive over TankKind.
25
+ */
26
+ export declare function tankAlert(kind: TankKind, tank: TankStatus): TankAlert;
27
+ /**
28
+ * VacuumInlineControl — compact, state-aware vacuum controls for a
29
+ * device-list row. Start / Pause / Return-to-base + battery badge.
30
+ */
31
+ export declare function VacuumInlineControl({ trpc, deviceId }: DeviceControlProps): ReactNode;
32
+ /**
33
+ * VacuumHeroCard — full control surface for the device-detail hero area.
34
+ * State label + action buttons + fan-speed selector + battery readout.
35
+ */
36
+ export declare function VacuumHeroCard({ trpc, deviceId }: DeviceControlProps): ReactNode;
@@ -0,0 +1,46 @@
1
+ import { ReactNode } from 'react';
2
+ import { ValveState } from '@camstack/types';
3
+ import { DeviceControlProps } from './control-registry';
4
+ export interface ValveStateMeta {
5
+ readonly label: string;
6
+ /** Tailwind text-colour token for the state label. */
7
+ readonly tone: string;
8
+ }
9
+ /**
10
+ * Pure helper — maps a ValveState (or undefined when no slice yet) to a
11
+ * human label + colour tone. Exhaustive switch: a new enum member fails
12
+ * the build.
13
+ */
14
+ export declare function valveStateMeta(state: ValveState | undefined): ValveStateMeta;
15
+ /**
16
+ * ValveInlineControl — compact, state-aware valve controls for a
17
+ * device-list row. Open ▲ Stop ■ Close ▼ + optional position % badge.
18
+ * Mirrors `CoverInlineControl` exactly. Must NOT blow out the row width.
19
+ */
20
+ export declare function ValveInlineControl({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
21
+ /** Per-state visual tint tokens for the framed valve graphic. */
22
+ export interface ValveGraphicTint {
23
+ /** Tailwind class for the flow-glow wash over the pipe. */
24
+ readonly glow: string;
25
+ /** Tailwind text token driving the valve body via currentColor. */
26
+ readonly accent: string;
27
+ /** Whether to render the animated flow dashes (open / opening). */
28
+ readonly flowing: boolean;
29
+ /** Whether to render the transient-motion activity pulse. */
30
+ readonly active: boolean;
31
+ }
32
+ export declare function valveGraphicTint(state: ValveState | undefined): ValveGraphicTint;
33
+ /**
34
+ * ValveHeroCard — full control surface for the device-detail hero area.
35
+ *
36
+ * Bespoke pipe + gate-valve graphic (drag surface when positionable) +
37
+ * a large position % (or capitalized state when position is null) +
38
+ * Open / Stop / Close hero buttons + a position slider gated on
39
+ * `ValvePositionable` (additive to drag), and a read-only range when a
40
+ * position is reported but not settable.
41
+ *
42
+ * Position support is detected via `ValveStatus.position !== null`; the
43
+ * interactive setPosition surface is gated on `DeviceFeature.ValvePositionable`.
44
+ * Only hides when the device truly has no valve cap.
45
+ */
46
+ export declare function ValveHeroCard({ trpc, deviceId, features, optimistic, }: DeviceControlProps): ReactNode;
@@ -0,0 +1,41 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /** Heating phase derived from the slice — drives the tank tint + element glow. */
4
+ export type WaterHeaterPhase = 'heating' | 'idle' | 'off';
5
+ export interface WaterHeaterTint {
6
+ /** Tailwind class for the wash behind the tank. */
7
+ readonly glow: string;
8
+ /** Tailwind text token driving the tank via currentColor. */
9
+ readonly accent: string;
10
+ /** Whether to render the heating-element glow. */
11
+ readonly heating: boolean;
12
+ }
13
+ /**
14
+ * Pure helper — maps a phase to a tint. Exhaustive over WaterHeaterPhase
15
+ * (a new member fails the build).
16
+ *
17
+ * heating → warning (element glow)
18
+ * idle → primary (warm but settled)
19
+ * off → muted
20
+ */
21
+ export declare function waterHeaterTint(phase: WaterHeaterPhase): WaterHeaterTint;
22
+ /**
23
+ * Heuristic heating phase: off when the operation mode is 'off' (or null);
24
+ * heating when current < target (actively warming); otherwise idle.
25
+ */
26
+ export declare function waterHeaterPhase(operationMode: string | null, currentTemp: number | null, targetTemp: number | null): WaterHeaterPhase;
27
+ /**
28
+ * WaterHeaterInlineControl — compact current→target readout + mode hint
29
+ * for a device-list row. Must NOT blow out the row width.
30
+ */
31
+ export declare function WaterHeaterInlineControl({ trpc, deviceId, optimistic, }: DeviceControlProps): ReactNode;
32
+ /**
33
+ * WaterHeaterHeroCard — full control surface for the device-detail hero.
34
+ *
35
+ * Bespoke tank graphic + a large current-temp readout + a target-temp
36
+ * stepper (clamped to min/maxTemp) + an operation-mode `<select>` + an
37
+ * "Away" toggle (only when `away !== null`) + the current-temp readout.
38
+ * Gated on `available`. Only hides when the device truly has no
39
+ * water-heater cap.
40
+ */
41
+ export declare function WaterHeaterHeroCard({ trpc, deviceId, optimistic }: DeviceControlProps): ReactNode;
@@ -0,0 +1,41 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceControlProps } from './control-registry';
3
+ /** Which sky glyph a condition maps to. Drives both the inline icon and
4
+ * the hero scene's graphic branch. */
5
+ export type WeatherGlyph = 'sun' | 'clear-night' | 'partly-cloudy' | 'cloudy' | 'rain' | 'snow' | 'lightning' | 'fog' | 'wind' | 'hail';
6
+ export interface WeatherConditionMeta {
7
+ /** Human-readable label for the condition. */
8
+ readonly label: string;
9
+ /** Which glyph to render. */
10
+ readonly glyph: WeatherGlyph;
11
+ }
12
+ export interface WeatherTint {
13
+ /** Tailwind class for the wash behind the scene. */
14
+ readonly glow: string;
15
+ /** Tailwind text token driving the scene glyph via currentColor. */
16
+ readonly accent: string;
17
+ }
18
+ /**
19
+ * Pure helper — maps a verbatim HA condition string to a label + glyph.
20
+ * Covers the known HA `weather.*` condition set; an unknown / null
21
+ * condition falls back to a neutral cloud.
22
+ */
23
+ export declare function weatherConditionMeta(condition: string | null): WeatherConditionMeta;
24
+ /**
25
+ * Pure helper — maps a verbatim HA condition string to a tint. Exhaustive
26
+ * over the known condition set with a neutral default.
27
+ */
28
+ export declare function weatherTint(condition: string | null): WeatherTint;
29
+ /**
30
+ * WeatherInlineControl — compact condition icon + temperature readout
31
+ * for a device-list row. Read-only; must NOT blow out the row width.
32
+ */
33
+ export declare function WeatherInlineControl({ trpc, deviceId }: DeviceControlProps): ReactNode;
34
+ /**
35
+ * WeatherHeroCard — read-only weather panel for the device-detail hero.
36
+ *
37
+ * Bespoke state-tinted sky scene + a large temperature readout +
38
+ * humidity / wind-speed (with a bearing arrow) / pressure readouts.
39
+ * Pure — no controls (the cap is read-only).
40
+ */
41
+ export declare function WeatherHeroCard({ trpc, deviceId }: DeviceControlProps): ReactNode;
@@ -20,4 +20,4 @@ export interface DeviceExportPanelProps {
20
20
  * Generic device-export panel. Addon-agnostic — only the `device-export`
21
21
  * cap drives it.
22
22
  */
23
- export declare function DeviceExportPanel({ addonId, trpc, onOpenDevice }: DeviceExportPanelProps): ReactElement;
23
+ export declare function DeviceExportPanel({ addonId, trpc, onOpenDevice, }: DeviceExportPanelProps): ReactElement;
@@ -9,4 +9,4 @@ export interface DeviceGridProps {
9
9
  /** Additional CSS classes */
10
10
  className?: string;
11
11
  }
12
- export declare function DeviceGrid({ children, minCardWidth, gap, className, }: DeviceGridProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function DeviceGrid({ children, minCardWidth, gap, className }: DeviceGridProps): import("react").JSX.Element;
@@ -1,9 +1,20 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { UseDeviceProxyTrpc } from '../../hooks/use-device-proxy';
3
+ import { ControlPlacement } from '../device-controls/control-registry';
3
4
  import { DeviceItemDevice, VariantDefaults } from './helpers';
4
5
  export interface DeviceItemActionsProps {
5
6
  readonly trpc: UseDeviceProxyTrpc;
6
7
  readonly device: DeviceItemDevice;
7
8
  readonly flags: VariantDefaults;
9
+ /**
10
+ * Where the control sits and whether the column carries the fixed `w-[140px]`
11
+ * box. `'right'` (default) → flush right in the fixed column, for card /
12
+ * minimal views. `'center'` → centered in the fixed column. `'table'` → the
13
+ * column sizes to its content (no fixed width) so a wide control lays out in
14
+ * sequence with the trailing trash + status dot without overflowing/colliding
15
+ * — used by the main devices table. FILL controls stretch to the fixed column
16
+ * width under `'right'`/`'center'`; under `'table'` they take natural width.
17
+ */
18
+ readonly controlPlacement?: ControlPlacement;
8
19
  }
9
- export declare function DeviceItemActions({ trpc, device, flags }: DeviceItemActionsProps): ReactNode;
20
+ export declare function DeviceItemActions({ trpc, device, controlPlacement, }: DeviceItemActionsProps): ReactNode;
@@ -0,0 +1,32 @@
1
+ import { ChildLayout } from '@camstack/types';
2
+ import { DeviceItemDevice } from './helpers';
3
+ export interface ChildLayoutSection {
4
+ readonly title: string;
5
+ readonly children: readonly DeviceItemDevice[];
6
+ /** Whether the section renders open by default. `false` when any matched
7
+ * layout entry for this section declared `collapsed: true`. */
8
+ readonly defaultOpen: boolean;
9
+ /** When `true`, the section's children render visually de-emphasized
10
+ * (muted). Set only on the synthetic "Disabled" section. */
11
+ readonly muted?: boolean;
12
+ }
13
+ export interface GroupedChildren {
14
+ /** Sections ordered by first appearance in `childLayout`. */
15
+ readonly sections: readonly ChildLayoutSection[];
16
+ /** Children not referenced by any layout entry — render plainly, as today. */
17
+ readonly ungrouped: readonly DeviceItemDevice[];
18
+ /** Trailing synthetic section gathering every child whose CamStack-side
19
+ * `disabled` flag is set. Kept SEPARATE from `sections`/`ungrouped` so
20
+ * consumers render it LAST (after provider sections + ungrouped) and
21
+ * muted. Undefined when no child is disabled. Generic — driven purely by
22
+ * `child.disabled`, no provider/HA-specific input. */
23
+ readonly disabledSection?: ChildLayoutSection;
24
+ }
25
+ /**
26
+ * Partition a parent's children into provider-declared accordion sections +
27
+ * the remaining (ungrouped) children. A child matches an entry when
28
+ * `child.stableId === ${parentStableId}-${entry.childKey}`. Within a section,
29
+ * children sort by `order ?? Number.MAX_SAFE_INTEGER` then by name. Sections
30
+ * appear in first-seen order across `childLayout`. Pure — no I/O.
31
+ */
32
+ export declare function groupChildrenByLayout(parentStableId: string, children: readonly DeviceItemDevice[], childLayout: ChildLayout | undefined): GroupedChildren;
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ interface ChildSectionAccordionProps {
3
+ readonly title: string;
4
+ readonly count: number;
5
+ readonly children: ReactNode;
6
+ readonly defaultOpen?: boolean;
7
+ }
8
+ export declare function ChildSectionAccordion({ title, count, children, defaultOpen, }: ChildSectionAccordionProps): ReactNode;
9
+ export {};
@@ -7,5 +7,5 @@ interface ChildrenAccordionProps {
7
7
  readonly adoptedChildren: readonly DeviceItemDevice[];
8
8
  readonly onNavigate: (id: number) => void;
9
9
  }
10
- export declare function ChildrenAccordion({ trpc, accessoryChildren, adoptedChildren, onNavigate }: ChildrenAccordionProps): ReactNode;
10
+ export declare function ChildrenAccordion({ trpc, accessoryChildren, adoptedChildren, onNavigate, }: ChildrenAccordionProps): ReactNode;
11
11
  export {};
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceItemDevice } from './helpers';
3
+ export interface ContainerChildrenProviderProps {
4
+ /** `parentDeviceId → that parent's children`. Built once by the list. */
5
+ readonly value: ReadonlyMap<number, readonly DeviceItemDevice[]>;
6
+ readonly children: ReactNode;
7
+ }
8
+ export declare function ContainerChildrenProvider({ value, children, }: ContainerChildrenProviderProps): ReactNode;
9
+ /**
10
+ * The children of `deviceId` as published by the nearest
11
+ * `ContainerChildrenProvider`. Returns a stable empty array when there is no
12
+ * provider or the device has no children — so a non-container row, or a row
13
+ * rendered outside a list, degrades exactly like the old optional prop.
14
+ */
15
+ export declare function useContainerChildren(deviceId: number): readonly DeviceItemDevice[];
@@ -0,0 +1,8 @@
1
+ import { ReactNode } from 'react';
2
+ import { DeviceItemDevice } from './helpers';
3
+ export interface DeviceDeleteActionProps {
4
+ readonly device: DeviceItemDevice;
5
+ /** Called only after the operator confirms the destructive delete. */
6
+ readonly onDelete: (device: DeviceItemDevice) => void;
7
+ }
8
+ export declare function DeviceDeleteAction({ device, onDelete }: DeviceDeleteActionProps): ReactNode;
@@ -10,5 +10,12 @@ export interface DeviceItemHeaderProps {
10
10
  readonly expanded: boolean;
11
11
  readonly onToggleExpand: () => void;
12
12
  readonly onNavigateToParent: (id: number) => void;
13
+ /**
14
+ * Vertical-card layout. When true, the name is rendered as the card's
15
+ * primary anchor: bigger font, heavier weight, line-clamp-2 instead of
16
+ * single-line truncation, and the inline integrationIcon flips above
17
+ * the name as a small lead-line so the name's full width is name-only.
18
+ */
19
+ readonly stacked?: boolean;
13
20
  }
14
- export declare function DeviceItemHeader({ device, variant, flags, parent, integrationIcon, hasChildren, expanded, onToggleExpand, onNavigateToParent, }: DeviceItemHeaderProps): ReactNode;
21
+ export declare function DeviceItemHeader({ device, variant, flags, parent, integrationIcon, hasChildren, expanded, onToggleExpand, onNavigateToParent, stacked, }: DeviceItemHeaderProps): ReactNode;