@glasshome/widget-sdk 0.1.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/dist/create-entity.d.ts +22 -0
  4. package/dist/define-widget.d.ts +10 -0
  5. package/dist/framework/backgrounds/Glow.d.ts +24 -0
  6. package/dist/framework/backgrounds/WidgetSliderFill.d.ts +33 -0
  7. package/dist/framework/components/WidgetContent.d.ts +29 -0
  8. package/dist/framework/components/WidgetEmptyState.d.ts +34 -0
  9. package/dist/framework/components/WidgetIcon.d.ts +48 -0
  10. package/dist/framework/components/WidgetMetrics.d.ts +47 -0
  11. package/dist/framework/components/WidgetStatus.d.ts +26 -0
  12. package/dist/framework/components/WidgetSubtitle.d.ts +28 -0
  13. package/dist/framework/components/WidgetTitle.d.ts +29 -0
  14. package/dist/framework/components/WidgetValue.d.ts +31 -0
  15. package/dist/framework/core/Widget.d.ts +88 -0
  16. package/dist/framework/design-system/index.d.ts +8 -0
  17. package/dist/framework/design-system/spacing.d.ts +40 -0
  18. package/dist/framework/design-system/typography.d.ts +40 -0
  19. package/dist/framework/design-system/z-index.d.ts +23 -0
  20. package/dist/framework/dialogs/WidgetDialog.d.ts +47 -0
  21. package/dist/framework/dialogs/index.d.ts +4 -0
  22. package/dist/framework/gestures/cursors.d.ts +17 -0
  23. package/dist/framework/gestures/use-widget-gestures.d.ts +53 -0
  24. package/dist/framework/hooks/index.d.ts +15 -0
  25. package/dist/framework/hooks/use-debug-data.d.ts +46 -0
  26. package/dist/framework/hooks/use-widget-config.d.ts +48 -0
  27. package/dist/framework/hooks/use-widget-context.d.ts +46 -0
  28. package/dist/framework/hooks/use-widget-dialog.d.ts +45 -0
  29. package/dist/framework/hooks/use-widget-entity-group.d.ts +67 -0
  30. package/dist/framework/hooks/use-widget-entity.d.ts +53 -0
  31. package/dist/framework/hooks/use-widget-form.d.ts +79 -0
  32. package/dist/framework/hooks/use-widget-responsive.d.ts +41 -0
  33. package/dist/framework/index.d.ts +55 -0
  34. package/dist/framework/layout/WidgetStack.d.ts +28 -0
  35. package/dist/framework/theming/adaptive-color.d.ts +28 -0
  36. package/dist/framework/theming/colors.d.ts +59 -0
  37. package/dist/framework/theming/index.d.ts +8 -0
  38. package/dist/framework/types.d.ts +335 -0
  39. package/dist/framework/utils/cn.d.ts +28 -0
  40. package/dist/framework/utils/empty-state.d.ts +49 -0
  41. package/dist/framework/utils/entity-aggregation.d.ts +73 -0
  42. package/dist/framework/utils/entity-state.d.ts +103 -0
  43. package/dist/framework/utils/format-value.d.ts +22 -0
  44. package/dist/framework/utils/index.d.ts +12 -0
  45. package/dist/framework/utils/interpret-value.d.ts +17 -0
  46. package/dist/framework/variants/built-in-variants.d.ts +39 -0
  47. package/dist/framework/variants/index.d.ts +7 -0
  48. package/dist/framework/variants/variant-utils.d.ts +105 -0
  49. package/dist/index.d.ts +7 -0
  50. package/dist/index.js +1955 -0
  51. package/dist/theme.d.ts +21 -0
  52. package/dist/types.d.ts +51 -0
  53. package/dist/version.d.ts +5 -0
  54. package/dist/vite/index.d.ts +56 -0
  55. package/dist/vite/index.js +184 -0
  56. package/package.json +76 -0
  57. package/preview/host.tsx +89 -0
  58. package/preview/preview.html +49 -0
  59. package/tailwind-sources.css +1 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Adaptive Color Utilities
3
+ *
4
+ * Derives icon background and glow colors from a dynamic CSS color (rgb, hsl, hex).
5
+ * Used by widgets like lights where the color changes at runtime based on entity state.
6
+ */
7
+ /**
8
+ * Adaptive icon colors derived from a dynamic CSS color.
9
+ */
10
+ export interface AdaptiveIconColors {
11
+ /** Icon container background — contrasts against the widget fill */
12
+ background: string;
13
+ /** Box-shadow glow color */
14
+ glow: string;
15
+ }
16
+ /**
17
+ * Derive icon background and glow from a CSS color string.
18
+ *
19
+ * Strategy:
20
+ * - The widget fill is typically the color at ~30% opacity, so the perceived
21
+ * background sits around 15-25% lightness on a dark dashboard.
22
+ * - For the icon container we want a darker, richer version of the color that
23
+ * "pops" against that fill. We clamp lightness to a low range and boost
24
+ * saturation slightly so it reads as a tinted dark square.
25
+ * - For very dark colors (e.g. deep blues) we lift lightness a bit instead.
26
+ * - The glow uses the original color at moderate opacity.
27
+ */
28
+ export declare function deriveAdaptiveIconColors(cssColor: string): AdaptiveIconColors | null;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Gradient preset type
3
+ */
4
+ export type GradientPreset = "ocean" | "sunset" | "forest" | "lavender" | "golden" | "midnight" | "rose" | "mint" | "slate" | "coral" | "aurora" | "ember" | "steel" | "twilight" | "sage" | "copper" | "dusk";
5
+ /**
6
+ * 17 gradient presets as Tailwind class strings
7
+ * Using 20% opacity for subtle background that doesn't overpower content
8
+ */
9
+ export declare const GRADIENT_PRESETS: Record<GradientPreset, string>;
10
+ /** Human-readable gradient names for UI display */
11
+ export declare const GRADIENT_NAMES: Record<GradientPreset, string>;
12
+ /** Array of all gradient preset keys */
13
+ export declare const GRADIENT_PRESET_KEYS: GradientPreset[];
14
+ /**
15
+ * Get a consistent gradient preset for any string input
16
+ * Uses a simple hash to deterministically select a gradient
17
+ */
18
+ export declare function getGradientFromString(input: string): GradientPreset;
19
+ /**
20
+ * Get gradient string
21
+ * @param gradient - Preset name or gradient string
22
+ * @param fallbackString - String to hash for default gradient selection
23
+ */
24
+ export declare function getGradient(gradient: GradientPreset | string | undefined, fallbackString: string): string;
25
+ /**
26
+ * Widget color preset with gradient, icon, glow, and text colors
27
+ */
28
+ export interface WidgetColorPreset {
29
+ /** Gradient string (Tailwind classes) */
30
+ gradient: string;
31
+ /** Simple background color for pills/badges (Tailwind class) */
32
+ bg: string;
33
+ /** Icon background with dark: classes */
34
+ icon: string;
35
+ /** Glow/shadow with dark: classes */
36
+ glow: string | undefined;
37
+ /** Text colors */
38
+ text: {
39
+ primary: string;
40
+ muted: string;
41
+ };
42
+ }
43
+ /**
44
+ * Map gradient presets to full color presets
45
+ * Each preset includes gradient, icon, glow, and text colors
46
+ */
47
+ export declare const gradientColorPresets: Record<GradientPreset, WidgetColorPreset>;
48
+ /**
49
+ * Shared color palette for entity states
50
+ * Uses gradient presets for consistency
51
+ */
52
+ export declare const stateColors: {
53
+ readonly unavailable: WidgetColorPreset;
54
+ readonly active: WidgetColorPreset;
55
+ readonly inactive: WidgetColorPreset;
56
+ readonly success: WidgetColorPreset;
57
+ readonly warning: WidgetColorPreset;
58
+ readonly error: WidgetColorPreset;
59
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Widget Theming - Shared Colors
3
+ *
4
+ * Simple color palette for entity states.
5
+ * Widgets can use these or define their own logic.
6
+ */
7
+ export { type AdaptiveIconColors, deriveAdaptiveIconColors } from "./adaptive-color";
8
+ export { GRADIENT_NAMES, GRADIENT_PRESET_KEYS, GRADIENT_PRESETS, type GradientPreset, getGradient, getGradientFromString, gradientColorPresets, stateColors, type WidgetColorPreset, } from "./colors";
@@ -0,0 +1,335 @@
1
+ /** Core type definitions for the Widget Framework. */
2
+ import type { JSX } from "solid-js";
3
+ /**
4
+ * Widget size classification based on grid dimensions
5
+ * - xs: Small widgets (1x1, 1x2)
6
+ * - sm: Medium-small widgets (2x1, 2x2)
7
+ * - md: Medium widgets (2x3, 2x4)
8
+ * - lg: Large widgets (2x6, 3x6, 4x2)
9
+ * - xl: Extra large widgets (4x4)
10
+ */
11
+ export type WidgetSize = "xs" | "sm" | "md" | "lg" | "xl";
12
+ /**
13
+ * Widget orientation based on aspect ratio
14
+ * - horizontal: width > height
15
+ * - vertical: height > width
16
+ * - square: width === height
17
+ */
18
+ export type WidgetOrientation = "horizontal" | "vertical" | "square";
19
+ /**
20
+ * Widget dimensions in both pixels and grid units
21
+ */
22
+ export interface WidgetDimensions {
23
+ /** Pixel width */
24
+ width: number;
25
+ /** Pixel height */
26
+ height: number;
27
+ /** Grid columns (1-4) */
28
+ gridWidth: number;
29
+ /** Grid rows (1-6) */
30
+ gridHeight: number;
31
+ }
32
+ /**
33
+ * Widget context provided to all child components.
34
+ * Values are plain (not accessors) -- the context provider
35
+ * will supply reactive accessors wrapping these.
36
+ */
37
+ export interface WidgetContextValue {
38
+ /** Widget size classification */
39
+ size: WidgetSize;
40
+ /** Widget orientation (for gestures - pure aspect ratio) */
41
+ orientation: WidgetOrientation;
42
+ /** Content layout direction (for UI arrangement - considers height) */
43
+ contentLayout: WidgetOrientation;
44
+ /** Widget dimensions */
45
+ dimensions: WidgetDimensions;
46
+ /** Whether widget is in edit mode */
47
+ isEditMode: boolean;
48
+ }
49
+ /**
50
+ * Gradient configuration
51
+ */
52
+ export interface GradientConfig {
53
+ /** Tailwind gradient from class (e.g., "from-blue-500") */
54
+ from: string;
55
+ /** Tailwind gradient to class (e.g., "to-blue-700") */
56
+ to: string;
57
+ }
58
+ /**
59
+ * Slide gesture configuration
60
+ */
61
+ export interface SlideGestureConfig {
62
+ /** Current value */
63
+ value: number;
64
+ /** Value change handler */
65
+ onChange: (value: number) => void;
66
+ /** Minimum value (default: 0) */
67
+ min?: number;
68
+ /** Maximum value (default: 100) */
69
+ max?: number;
70
+ /** Slide orientation (default: "auto" - detects from widget orientation) */
71
+ orientation?: "auto" | "horizontal" | "vertical";
72
+ /** Delay before slide activates in ms (prevents scroll conflicts, default: 0) */
73
+ activationDelay?: number;
74
+ }
75
+ /**
76
+ * Hold gesture configuration
77
+ */
78
+ export interface HoldGestureConfig {
79
+ /** Action to perform on hold */
80
+ action: () => void;
81
+ /** Hold delay in ms (default: 300) */
82
+ delay?: number;
83
+ }
84
+ /**
85
+ * Combined gesture configuration
86
+ */
87
+ export interface GestureConfig {
88
+ /** Simple tap handler */
89
+ tap?: () => void;
90
+ /** Hold gesture configuration */
91
+ hold?: HoldGestureConfig;
92
+ /** Slide gesture configuration */
93
+ slide?: SlideGestureConfig;
94
+ }
95
+ /**
96
+ * Spacing scale
97
+ */
98
+ export type SpacingScale = "S1" | "S2" | "S3" | "S4";
99
+ /**
100
+ * Color variants for common components
101
+ */
102
+ export type ColorVariant = "blue" | "green" | "red" | "yellow" | "purple" | "gray" | string;
103
+ /**
104
+ * Image overlay types
105
+ */
106
+ export type ImageOverlay = "gradient" | "dark" | "none";
107
+ /**
108
+ * Base props that all framework components accept
109
+ */
110
+ export interface BaseComponentProps {
111
+ /** Additional CSS classes (SolidJS uses `class` instead of `className`) */
112
+ class?: string;
113
+ /** Child elements */
114
+ children?: JSX.Element;
115
+ }
116
+ /**
117
+ * CSS variable-based styling configuration
118
+ * Replaces brittle Tailwind class strings with type-safe CSS variables
119
+ */
120
+ export interface WidgetStyles {
121
+ /** Inline CSS properties applied to widget container */
122
+ container?: JSX.CSSProperties;
123
+ /** Tailwind utility classes (for simple styling) */
124
+ class?: string;
125
+ /** CSS custom properties for themeable values */
126
+ cssVars?: Record<`--widget-${string}`, string | number>;
127
+ }
128
+ /**
129
+ * Flex layout strategy with type-safe configuration
130
+ */
131
+ export interface FlexLayoutStrategy {
132
+ type: "flex";
133
+ /** Flex direction */
134
+ direction: "row" | "column" | "row-reverse" | "column-reverse";
135
+ /** Align items */
136
+ align: "start" | "center" | "end" | "stretch";
137
+ /** Justify content */
138
+ justify: "start" | "center" | "end" | "between" | "around";
139
+ /** Flex wrap */
140
+ wrap?: boolean;
141
+ /** Gap between items (CSS value) */
142
+ gap?: string;
143
+ /** Custom order for specific elements */
144
+ order?: Partial<Record<WidgetElement, number>>;
145
+ }
146
+ /**
147
+ * Grid layout strategy with type-safe configuration
148
+ */
149
+ export interface GridLayoutStrategy {
150
+ type: "grid";
151
+ /** Grid template areas string */
152
+ areas: string;
153
+ /** Grid template columns */
154
+ columns?: string;
155
+ /** Grid template rows */
156
+ rows?: string;
157
+ /** Gap between items (CSS value) */
158
+ gap?: string;
159
+ /** Grid area assignments for elements */
160
+ elementAreas: Partial<Record<WidgetElement, string>>;
161
+ }
162
+ /**
163
+ * Absolute positioning strategy with type-safe configuration
164
+ */
165
+ export interface AbsoluteLayoutStrategy {
166
+ type: "absolute";
167
+ /** Position configurations for specific elements */
168
+ positions: Partial<Record<WidgetElement, PositionConfig>>;
169
+ }
170
+ /**
171
+ * Custom layout strategy (escape hatch)
172
+ */
173
+ export interface CustomLayoutStrategy {
174
+ type: "custom";
175
+ /** Custom render function or component */
176
+ renderer: string;
177
+ }
178
+ /**
179
+ * Discriminated union of all layout strategies
180
+ * Ensures type safety - can't use gridArea with flex, etc.
181
+ */
182
+ export type LayoutStrategy = FlexLayoutStrategy | GridLayoutStrategy | AbsoluteLayoutStrategy | CustomLayoutStrategy;
183
+ /**
184
+ * Position configuration for absolute layout
185
+ */
186
+ export interface PositionConfig {
187
+ top?: string;
188
+ right?: string;
189
+ bottom?: string;
190
+ left?: string;
191
+ transform?: string;
192
+ }
193
+ /**
194
+ * Widget element identifiers for layout configuration
195
+ */
196
+ export type WidgetElement = "icon" | "title" | "subtitle" | "status" | "value" | "metrics" | "content" | "background" | "overlay" | "decorations";
197
+ /**
198
+ * Element-specific configuration
199
+ */
200
+ export interface ElementConfig {
201
+ /** Show/hide specific elements */
202
+ visible?: Partial<Record<WidgetElement, boolean>>;
203
+ /** Element-specific styling */
204
+ styles?: Partial<Record<WidgetElement, JSX.CSSProperties>>;
205
+ /** Element-specific CSS classes */
206
+ classNames?: Partial<Record<WidgetElement, string>>;
207
+ }
208
+ /**
209
+ * Plugin system configuration (serializable)
210
+ * Uses string IDs instead of JSX.Element for serializability
211
+ */
212
+ export interface VariantPlugins {
213
+ /** Background plugin ID */
214
+ background?: string;
215
+ /** Overlay plugin ID */
216
+ overlay?: string;
217
+ /** Decoration plugin IDs */
218
+ decorations?: string[];
219
+ }
220
+ /**
221
+ * Interaction configuration
222
+ */
223
+ export interface InteractionConfig {
224
+ /** Enable hover effects */
225
+ hover?: boolean;
226
+ /** Enable active/pressed state */
227
+ active?: boolean;
228
+ /** Enable focus ring */
229
+ focus?: boolean;
230
+ /** Custom hover scale (e.g., 1.02) */
231
+ hoverScale?: number;
232
+ /** Custom active scale (e.g., 0.98) */
233
+ activeScale?: number;
234
+ }
235
+ /**
236
+ * Complete variant configuration
237
+ * This is the core of the variant system
238
+ */
239
+ export interface WidgetVariantConfig {
240
+ /** Unique variant identifier */
241
+ id: string;
242
+ /** Human-readable name */
243
+ name: string;
244
+ /** Description of variant purpose/style */
245
+ description?: string;
246
+ /** Styling configuration */
247
+ styles?: WidgetStyles;
248
+ /** Layout strategy */
249
+ layout?: LayoutStrategy;
250
+ /** Element-specific configuration */
251
+ elements?: ElementConfig;
252
+ /** Plugin configurations */
253
+ plugins?: VariantPlugins;
254
+ /** Interaction behavior */
255
+ interactions?: InteractionConfig;
256
+ /** Base variant to extend (enables composition) */
257
+ extends?: string;
258
+ }
259
+ /**
260
+ * Variant type union (built-in + custom)
261
+ */
262
+ export type WidgetVariant = "classic-glass" | "minimal" | "compact-horizontal" | (string & {});
263
+ /**
264
+ * Variant registry type
265
+ */
266
+ export type VariantRegistry = Record<string, WidgetVariantConfig>;
267
+ /** Unique entity identifier (e.g., "light.living_room") */
268
+ export type EntityId = string;
269
+ /** Entity domain (e.g., "light", "sensor", "switch") */
270
+ export type EntityDomain = string;
271
+ /** Area identifier */
272
+ export type AreaId = string;
273
+ /** Device identifier */
274
+ export type DeviceId = string;
275
+ /** Label identifier */
276
+ export type LabelId = string;
277
+ /** Entity category for filtering */
278
+ export type EntityCategory = "config" | "diagnostic" | null;
279
+ /**
280
+ * Full entity view interface representing a Home Assistant entity.
281
+ * This is the canonical type used by SDK framework utils and widgets.
282
+ * sync-layer produces EntityView objects; widgets consume them.
283
+ */
284
+ export interface EntityView {
285
+ /** Unique entity identifier (e.g., "light.living_room") */
286
+ id: EntityId;
287
+ /** Entity domain (e.g., "light", "sensor") */
288
+ domain: EntityDomain;
289
+ /** Current state value */
290
+ state: string;
291
+ /** Entity attributes */
292
+ attributes: Record<string, any>;
293
+ /** When the state last changed */
294
+ lastChanged: Date;
295
+ /** When the state was last updated (even if unchanged) */
296
+ lastUpdated: Date;
297
+ /** Context of the last state change */
298
+ context: {
299
+ id: string;
300
+ parentId: string | null;
301
+ userId: string | null;
302
+ };
303
+ /** Internal entity name (object_id) */
304
+ name: string;
305
+ /** Friendly display name */
306
+ friendlyName: string;
307
+ /** Area this entity belongs to */
308
+ areaId: AreaId | null;
309
+ /** Device this entity belongs to */
310
+ deviceId: DeviceId | null;
311
+ /** Integration platform (e.g., "hue", "zwave") */
312
+ platform: string;
313
+ /** Unique ID from the integration */
314
+ uniqueId: string | null;
315
+ /** Whether the entity is disabled */
316
+ isDisabled: boolean;
317
+ /** Whether the entity is hidden from the UI */
318
+ isHidden: boolean;
319
+ /** Icon identifier (e.g., "mdi:lightbulb") */
320
+ icon: string | null;
321
+ /** Source of the icon */
322
+ iconSource: "registry" | "attribute" | "default";
323
+ /** Entity category for filtering */
324
+ entityCategory: EntityCategory;
325
+ /** Labels assigned to this entity */
326
+ labels: LabelId[];
327
+ /** User-defined aliases */
328
+ aliases: string[];
329
+ /** Device class (e.g., "temperature", "motion") */
330
+ deviceClass?: string | null;
331
+ /** Unit of measurement (for sensors) */
332
+ unitOfMeasurement?: string | null;
333
+ /** Bitmask of supported features */
334
+ supportedFeatures?: number;
335
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Class Name Utility
3
+ *
4
+ * Combines clsx and tailwind-merge for conditional class management.
5
+ * This is a pure function of its inputs -- it does no internal caching.
6
+ *
7
+ * **Memoization guidance for callers:** In hot render paths where inputs
8
+ * change frequently (e.g., reactive signals driving class names), wrap
9
+ * the call in `createMemo` to avoid redundant `twMerge` computation:
10
+ *
11
+ * ```tsx
12
+ * const classes = createMemo(() => cn("base", dynamicSignal()));
13
+ * ```
14
+ *
15
+ * In static JSX expressions with constant strings, no memoization is needed
16
+ * since Solid's fine-grained reactivity only re-evaluates when dependencies change.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * <div class={cn(
21
+ * "base-class",
22
+ * condition && "conditional-class",
23
+ * customClass
24
+ * )}>
25
+ * ```
26
+ */
27
+ import { type ClassValue } from "clsx";
28
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Empty State Utilities
3
+ *
4
+ * Helper functions for creating consistent empty state configurations across widgets.
5
+ */
6
+ import type { JSX } from "solid-js";
7
+ /**
8
+ * Empty state configuration for a widget
9
+ */
10
+ export interface WidgetEmptyStateConfig {
11
+ /** Icon to display */
12
+ icon: JSX.Element;
13
+ /** Main title/heading */
14
+ title: string;
15
+ /** Descriptive message */
16
+ message: string;
17
+ }
18
+ export interface EmptyStateConfigOptions {
19
+ /** Icon to display */
20
+ icon: JSX.Element;
21
+ /** Main title/heading */
22
+ title: string;
23
+ /** Descriptive message (optional - uses default template if not provided) */
24
+ message?: string;
25
+ /** Entity type for default message template (e.g., "a light", "a lock") */
26
+ entityType?: string;
27
+ }
28
+ /**
29
+ * Creates a consistent empty state configuration for widgets
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // Custom message
34
+ * const config = createEmptyStateConfig({
35
+ * icon: <Thermometer />,
36
+ * title: "No climate entity",
37
+ * message: "Configure this widget to add a climate device",
38
+ * });
39
+ *
40
+ * // Default message template
41
+ * const config = createEmptyStateConfig({
42
+ * icon: <Lock />,
43
+ * title: "No lock entity",
44
+ * entityType: "a lock",
45
+ * });
46
+ * // Result: message = "Configure this widget to add a lock"
47
+ * ```
48
+ */
49
+ export declare function createEmptyStateConfig(options: EmptyStateConfigOptions): WidgetEmptyStateConfig;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Entity Aggregation Utilities
3
+ *
4
+ * Generic entity aggregation patterns for multi-entity widgets.
5
+ * These utilities aggregate state across multiple entities to produce
6
+ * a single representative state for the group.
7
+ */
8
+ import type { EntityView } from "../types";
9
+ /**
10
+ * Sensor group calculation modes
11
+ */
12
+ export type SensorGroupType = "min" | "max" | "mean" | "median" | "sum" | "last" | "range" | "product" | "std_dev";
13
+ /**
14
+ * Light/switch/binary-sensor group aggregation result
15
+ */
16
+ export interface LightGroupResult {
17
+ /** Is this a group or single entity */
18
+ isGroup: boolean;
19
+ /** Collective state */
20
+ state: string;
21
+ /** Is on */
22
+ isOn: boolean;
23
+ /** Is unavailable */
24
+ isUnavailable: boolean;
25
+ /** Average brightness (0-255) */
26
+ brightness: number;
27
+ /** Brightness percentage (0-100) */
28
+ brightnessPercent: number;
29
+ /** RGB color for display */
30
+ color: string;
31
+ /** Number of entities on */
32
+ onCount: number;
33
+ /** Total entities */
34
+ totalCount: number;
35
+ /** Human readable description */
36
+ description: string;
37
+ }
38
+ /**
39
+ * Sensor group aggregation result
40
+ */
41
+ export interface SensorGroupResult {
42
+ /** Is this a group or single entity */
43
+ isGroup: boolean;
44
+ /** Collective state/value */
45
+ state: string;
46
+ /** Numeric value (if applicable) */
47
+ numericValue: number | null;
48
+ /** Is unavailable */
49
+ isUnavailable: boolean;
50
+ /** Unit of measurement */
51
+ unit?: string;
52
+ /** Human readable description */
53
+ description: string;
54
+ /** All member values (for debugging/display) */
55
+ memberValues?: Array<{
56
+ entityId: string;
57
+ value: string | number;
58
+ friendly_name?: string;
59
+ }>;
60
+ }
61
+ /**
62
+ * Calculate light/switch/binary-sensor group state and properties
63
+ *
64
+ * Works for any binary state entities (lights, switches, binary sensors)
65
+ * Aggregates on/off state, brightness (if applicable), and color.
66
+ */
67
+ export declare function calculateLightGroup(entities: EntityView[], allEntitiesMode?: boolean): LightGroupResult;
68
+ /**
69
+ * Calculate sensor group state and value
70
+ *
71
+ * Aggregates numeric sensor values using various calculation modes.
72
+ */
73
+ export declare function calculateSensorGroup(entities: EntityView[], groupType?: SensorGroupType, ignoreNonNumeric?: boolean): SensorGroupResult;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Entity State Utilities
3
+ *
4
+ * Common entity state calculations that widgets repeatedly implement.
5
+ * These utilities provide domain-agnostic state checking and counting.
6
+ *
7
+ * For aggregation of multiple entities, see entity-aggregation.ts utilities
8
+ * (calculateLightGroup, calculateSensorGroup).
9
+ */
10
+ import type { EntityView } from "../types";
11
+ /**
12
+ * Check if entity is available (not unavailable/unknown)
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const isAvailable = isEntityAvailable(entity);
17
+ * if (isAvailable) {
18
+ * // Entity is available, can display state
19
+ * }
20
+ * ```
21
+ */
22
+ export declare function isEntityAvailable(entity: EntityView | null | undefined): boolean;
23
+ /**
24
+ * Check if entity is active (on, open, locked, etc.)
25
+ * Domain-aware state checking
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const isActive = isEntityActive(entity);
30
+ * const statusColor = isActive ? "green" : "gray";
31
+ * ```
32
+ */
33
+ export declare function isEntityActive(entity: EntityView | null | undefined): boolean;
34
+ /**
35
+ * Get entity state with fallback
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const state = getEntityState(entity, "unknown");
40
+ * return <Widget.Status>{state}</Widget.Status>;
41
+ * ```
42
+ */
43
+ export declare function getEntityState(entity: EntityView | null | undefined, fallback?: string): string;
44
+ /**
45
+ * Get entity attribute with type safety
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const brightness = getEntityAttribute<number>(entity, "brightness", 0);
50
+ * const temperature = getEntityAttribute<number>(entity, "current_temperature");
51
+ * ```
52
+ */
53
+ export declare function getEntityAttribute<T = unknown>(entity: EntityView | null | undefined, attributeName: string, fallback?: T): T | undefined;
54
+ /**
55
+ * Count entities by state
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const onCount = countEntitiesByState(entities, "on");
60
+ * return <Widget.Status>{onCount} lights on</Widget.Status>;
61
+ * ```
62
+ */
63
+ export declare function countEntitiesByState(entities: EntityView[], state: string): number;
64
+ /**
65
+ * Count available entities (excludes unavailable/unknown)
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const availableCount = countAvailableEntities(entities);
70
+ * const unavailableCount = entities.length - availableCount;
71
+ * ```
72
+ */
73
+ export declare function countAvailableEntities(entities: EntityView[]): number;
74
+ /**
75
+ * Count active entities (on, open, etc.)
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const activeCount = countActiveEntities(entities);
80
+ * return <Widget.Status>{activeCount} active</Widget.Status>;
81
+ * ```
82
+ */
83
+ export declare function countActiveEntities(entities: EntityView[]): number;
84
+ /**
85
+ * Check if all entities match state
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const allOn = allEntitiesInState(entities, "on");
90
+ * const statusText = allOn ? "All on" : "Mixed";
91
+ * ```
92
+ */
93
+ export declare function allEntitiesInState(entities: EntityView[], state: string): boolean;
94
+ /**
95
+ * Check if any entity matches state
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const anyOpen = anyEntityInState(entities, "open");
100
+ * const iconColor = anyOpen ? "red" : "green";
101
+ * ```
102
+ */
103
+ export declare function anyEntityInState(entities: EntityView[], state: string): boolean;