@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.
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/create-entity.d.ts +22 -0
- package/dist/define-widget.d.ts +10 -0
- package/dist/framework/backgrounds/Glow.d.ts +24 -0
- package/dist/framework/backgrounds/WidgetSliderFill.d.ts +33 -0
- package/dist/framework/components/WidgetContent.d.ts +29 -0
- package/dist/framework/components/WidgetEmptyState.d.ts +34 -0
- package/dist/framework/components/WidgetIcon.d.ts +48 -0
- package/dist/framework/components/WidgetMetrics.d.ts +47 -0
- package/dist/framework/components/WidgetStatus.d.ts +26 -0
- package/dist/framework/components/WidgetSubtitle.d.ts +28 -0
- package/dist/framework/components/WidgetTitle.d.ts +29 -0
- package/dist/framework/components/WidgetValue.d.ts +31 -0
- package/dist/framework/core/Widget.d.ts +88 -0
- package/dist/framework/design-system/index.d.ts +8 -0
- package/dist/framework/design-system/spacing.d.ts +40 -0
- package/dist/framework/design-system/typography.d.ts +40 -0
- package/dist/framework/design-system/z-index.d.ts +23 -0
- package/dist/framework/dialogs/WidgetDialog.d.ts +47 -0
- package/dist/framework/dialogs/index.d.ts +4 -0
- package/dist/framework/gestures/cursors.d.ts +17 -0
- package/dist/framework/gestures/use-widget-gestures.d.ts +53 -0
- package/dist/framework/hooks/index.d.ts +15 -0
- package/dist/framework/hooks/use-debug-data.d.ts +46 -0
- package/dist/framework/hooks/use-widget-config.d.ts +48 -0
- package/dist/framework/hooks/use-widget-context.d.ts +46 -0
- package/dist/framework/hooks/use-widget-dialog.d.ts +45 -0
- package/dist/framework/hooks/use-widget-entity-group.d.ts +67 -0
- package/dist/framework/hooks/use-widget-entity.d.ts +53 -0
- package/dist/framework/hooks/use-widget-form.d.ts +79 -0
- package/dist/framework/hooks/use-widget-responsive.d.ts +41 -0
- package/dist/framework/index.d.ts +55 -0
- package/dist/framework/layout/WidgetStack.d.ts +28 -0
- package/dist/framework/theming/adaptive-color.d.ts +28 -0
- package/dist/framework/theming/colors.d.ts +59 -0
- package/dist/framework/theming/index.d.ts +8 -0
- package/dist/framework/types.d.ts +335 -0
- package/dist/framework/utils/cn.d.ts +28 -0
- package/dist/framework/utils/empty-state.d.ts +49 -0
- package/dist/framework/utils/entity-aggregation.d.ts +73 -0
- package/dist/framework/utils/entity-state.d.ts +103 -0
- package/dist/framework/utils/format-value.d.ts +22 -0
- package/dist/framework/utils/index.d.ts +12 -0
- package/dist/framework/utils/interpret-value.d.ts +17 -0
- package/dist/framework/variants/built-in-variants.d.ts +39 -0
- package/dist/framework/variants/index.d.ts +7 -0
- package/dist/framework/variants/variant-utils.d.ts +105 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1955 -0
- package/dist/theme.d.ts +21 -0
- package/dist/types.d.ts +51 -0
- package/dist/version.d.ts +5 -0
- package/dist/vite/index.d.ts +56 -0
- package/dist/vite/index.js +184 -0
- package/package.json +76 -0
- package/preview/host.tsx +89 -0
- package/preview/preview.html +49 -0
- 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;
|