preact-homeassistant 0.2.3 → 0.4.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/README.md +30 -3
- package/dist/index.d.ts +49 -22
- package/dist/index.js +206 -143
- package/dist/index.js.map +1 -1
- package/package.json +32 -15
- package/src/HACard.tsx +58 -0
- package/src/HAContext.tsx +162 -120
- package/src/__tests__/cacheUtils.test.ts +23 -48
- package/src/__tests__/registerPreactCard.test.tsx +75 -0
- package/src/__tests__/testHelpers.tsx +11 -1
- package/src/__tests__/useCachedFetch.test.tsx +57 -33
- package/src/__tests__/{useMultiCalendarEvents.test.tsx → useCalendarEvents.test.tsx} +3 -4
- package/src/__tests__/useEntity.test.tsx +11 -17
- package/src/__tests__/useHassValue.test.tsx +99 -0
- package/src/cacheUtils.ts +12 -24
- package/src/index.ts +4 -2
- package/src/registerPreactCard.tsx +101 -32
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ pnpm add preact preact-homeassistant
|
|
|
15
15
|
## Quick start
|
|
16
16
|
|
|
17
17
|
```tsx
|
|
18
|
-
import { registerPreactCard, useEntity, css } from 'preact-homeassistant';
|
|
18
|
+
import { registerPreactCard, HACard, useEntity, css } from 'preact-homeassistant';
|
|
19
19
|
|
|
20
20
|
css`
|
|
21
21
|
.my-card { padding: 16px; }
|
|
@@ -26,11 +26,11 @@ function MyCardContent({ config }: { config: { entity: string } }) {
|
|
|
26
26
|
const weather = useEntity(config.entity);
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
|
-
<
|
|
29
|
+
<HACard>
|
|
30
30
|
<div class="card-content my-card">
|
|
31
31
|
<span class="temperature">{weather?.state ?? '...'}</span>
|
|
32
32
|
</div>
|
|
33
|
-
</
|
|
33
|
+
</HACard>
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -87,6 +87,33 @@ prop and uses hooks for everything else.
|
|
|
87
87
|
The card renders into a Shadow DOM root. The editor renders into the light DOM
|
|
88
88
|
(required for HA's own custom elements like `<ha-select>` to work).
|
|
89
89
|
|
|
90
|
+
## `<HACard>`
|
|
91
|
+
|
|
92
|
+
Use `HACard` as the root of your card instead of a raw `<ha-card>`. It makes the
|
|
93
|
+
card fill the height Home Assistant assigns it.
|
|
94
|
+
|
|
95
|
+
In HA's **sections (grid)** layout, when a card is resized (e.g. to 3 rows) HA
|
|
96
|
+
gives the card's host element a definite height. A plain `<ha-card>` collapses to
|
|
97
|
+
its natural content height and renders slightly short, leaving a gap. `HACard`
|
|
98
|
+
sets the host and the `ha-card` to fill that height, so the card matches the slot
|
|
99
|
+
exactly. In layouts with no fixed height (masonry, auto rows) it safely collapses
|
|
100
|
+
back to natural height, so it's a drop-in replacement everywhere.
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<HACard class="size-large" align="space-between">
|
|
104
|
+
<div class="card-content my-card">…</div>
|
|
105
|
+
</HACard>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
| Prop | Type | Default | Description |
|
|
109
|
+
|---|---|---|---|
|
|
110
|
+
| `align` | `HACardAlign` | `'top'` | How content is distributed vertically when the slot is taller than the content. Friendly aliases `top` / `center` / `bottom`, or any flex `justify-content` value (`space-between`, `space-around`, `space-evenly`, `flex-start`, `flex-end`). |
|
|
111
|
+
| `class` | `string` | — | Class applied to the underlying `ha-card`. |
|
|
112
|
+
| `children` | `ComponentChildren` | — | Card contents. |
|
|
113
|
+
|
|
114
|
+
`align` only positions content as a block. To make an inner section *stretch* to
|
|
115
|
+
absorb the extra height, give it `flex: 1` in your card's CSS.
|
|
116
|
+
|
|
90
117
|
## Hooks
|
|
91
118
|
|
|
92
119
|
### `useEntity(entityId)`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ComponentChildren } from 'preact';
|
|
2
2
|
import { ComponentType } from 'preact';
|
|
3
3
|
import { Connection } from 'home-assistant-js-websocket';
|
|
4
|
+
import { createElement } from 'preact';
|
|
4
5
|
import { HassConfig } from 'home-assistant-js-websocket';
|
|
5
6
|
import { HassEntities } from 'home-assistant-js-websocket';
|
|
6
7
|
import { HassEntity } from 'home-assistant-js-websocket';
|
|
@@ -10,6 +11,12 @@ import { HassServices } from 'home-assistant-js-websocket';
|
|
|
10
11
|
import { JSX } from 'preact';
|
|
11
12
|
import { RefObject } from 'preact';
|
|
12
13
|
|
|
14
|
+
declare type Cache_2 = Map<string, CacheEntry<unknown>>;
|
|
15
|
+
|
|
16
|
+
declare interface CacheEntry<T> {
|
|
17
|
+
data: T;
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
/**
|
|
14
21
|
* Calendar entity - only exposes the current/next event.
|
|
15
22
|
* Use useCalendarEvents() to fetch a list of events.
|
|
@@ -147,11 +154,23 @@ export declare type ForecastType = 'daily' | 'hourly' | 'twice_daily';
|
|
|
147
154
|
*/
|
|
148
155
|
export declare function getAllStyles(): string;
|
|
149
156
|
|
|
150
|
-
export declare function
|
|
157
|
+
export declare function HACard({ align, class: className, children }: HACardProps): createElement.JSX.Element;
|
|
158
|
+
|
|
159
|
+
export declare type HACardAlign = 'top' | 'center' | 'bottom' | 'flex-start' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly';
|
|
160
|
+
|
|
161
|
+
declare interface HACardProps {
|
|
162
|
+
align?: HACardAlign;
|
|
163
|
+
class?: string;
|
|
164
|
+
children?: ComponentChildren;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export declare function HAProvider({ hass, subscribeToEntity, subscribeToHass, cache, children, }: HAProviderProps): JSX.Element;
|
|
151
168
|
|
|
152
169
|
declare interface HAProviderProps {
|
|
153
170
|
hass: HomeAssistant | undefined;
|
|
154
171
|
subscribeToEntity: (entityId: string, callback: (entity: any) => void) => () => void;
|
|
172
|
+
subscribeToHass?: SubscribeToHass;
|
|
173
|
+
cache?: Cache_2;
|
|
155
174
|
children: ComponentChildren;
|
|
156
175
|
}
|
|
157
176
|
|
|
@@ -174,8 +193,6 @@ declare type KnownDomain = keyof DomainEntityMap;
|
|
|
174
193
|
|
|
175
194
|
declare type KnownServiceDomain = keyof DomainServiceMap;
|
|
176
195
|
|
|
177
|
-
export declare function loadFromCache<T>(key: string): T | undefined;
|
|
178
|
-
|
|
179
196
|
export declare function registerPreactCard<TConfig>(options: RegisterPreactCardOptions<TConfig>): void;
|
|
180
197
|
|
|
181
198
|
declare interface RegisterPreactCardOptions<TConfig> {
|
|
@@ -202,8 +219,6 @@ export declare function registerRawStyles(styles: string): void;
|
|
|
202
219
|
|
|
203
220
|
export declare type ResizeCallback = (size: ElementSize) => void;
|
|
204
221
|
|
|
205
|
-
export declare function saveToCache<T>(key: string, data: T): void;
|
|
206
|
-
|
|
207
222
|
declare type ServiceCaller<T extends string> = <S extends keyof ServicesForId<T> & string>(service: S, ...args: ServicesForId<T>[S] extends undefined ? [] : Record<string, never> extends Exclude<ServicesForId<T>[S], undefined> ? [data?: ServicesForId<T>[S]] : [data: ServicesForId<T>[S]]) => Promise<void>;
|
|
208
223
|
|
|
209
224
|
/**
|
|
@@ -214,6 +229,8 @@ declare type ServiceCaller<T extends string> = <S extends keyof ServicesForId<T>
|
|
|
214
229
|
*/
|
|
215
230
|
export declare type ServicesForId<T extends string> = T extends `${infer D}.${string}` ? D extends KnownServiceDomain ? DomainServiceMap[D] : Record<string, Record<string, unknown> | undefined> : Record<string, Record<string, unknown> | undefined>;
|
|
216
231
|
|
|
232
|
+
declare type SubscribeToHass = (callback: () => void) => () => void;
|
|
233
|
+
|
|
217
234
|
/**
|
|
218
235
|
* Sun entity - provides sunrise/sunset and elevation information.
|
|
219
236
|
*/
|
|
@@ -247,18 +264,29 @@ declare interface UseCachedFetchResult<T> {
|
|
|
247
264
|
}
|
|
248
265
|
|
|
249
266
|
/**
|
|
250
|
-
* Fetch
|
|
267
|
+
* Fetch events from one or more calendars for a date range, with in-memory
|
|
268
|
+
* (per-card) caching and stale-while-revalidate behavior. Events are tagged
|
|
269
|
+
* with their source calendar ID. Returns `prefetch` to warm adjacent ranges.
|
|
251
270
|
*/
|
|
252
|
-
export declare function useCalendarEvents(
|
|
271
|
+
export declare function useCalendarEvents(entityIds: `calendar.${string}`[], options: {
|
|
253
272
|
start: Date;
|
|
254
273
|
end: Date;
|
|
255
274
|
}): UseCalendarEventsResult;
|
|
256
275
|
|
|
257
276
|
declare interface UseCalendarEventsResult {
|
|
258
|
-
events:
|
|
259
|
-
|
|
277
|
+
events: CalendarEventWithSource[] | undefined;
|
|
278
|
+
status: FetchStatus;
|
|
260
279
|
error: Error | undefined;
|
|
261
280
|
refetch: () => void;
|
|
281
|
+
/**
|
|
282
|
+
* Warm the cache for an arbitrary range (e.g. adjacent months) without
|
|
283
|
+
* touching component state. Best-effort: skips ranges already cached and
|
|
284
|
+
* swallows failures.
|
|
285
|
+
*/
|
|
286
|
+
prefetch: (range: {
|
|
287
|
+
start: Date;
|
|
288
|
+
end: Date;
|
|
289
|
+
}) => void;
|
|
262
290
|
}
|
|
263
291
|
|
|
264
292
|
/**
|
|
@@ -270,6 +298,9 @@ declare interface UseCalendarEventsResult {
|
|
|
270
298
|
*/
|
|
271
299
|
export declare function useCallbackStable<T extends (...args: never[]) => unknown>(callback: T): T;
|
|
272
300
|
|
|
301
|
+
/** Re-renders when the active theme's dark mode flips. */
|
|
302
|
+
export declare function useDarkMode(): boolean;
|
|
303
|
+
|
|
273
304
|
/**
|
|
274
305
|
* Subscribe to a specific entity by ID. Re-renders only when that entity changes.
|
|
275
306
|
*
|
|
@@ -289,21 +320,17 @@ export declare function useHass(): {
|
|
|
289
320
|
getHass: () => HomeAssistant | undefined;
|
|
290
321
|
};
|
|
291
322
|
|
|
323
|
+
/** Re-renders when `hass.config` changes (units, latitude/longitude, etc.). */
|
|
324
|
+
export declare function useHassConfig(): HomeAssistant['config'] | undefined;
|
|
325
|
+
|
|
292
326
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
327
|
+
* Subscribe to a derived slice of the `hass` object (e.g. config, themes) and
|
|
328
|
+
* re-render only when that slice changes. Use this for non-entity values —
|
|
329
|
+
* entity state goes through `useEntity`. The selector runs on every hass update
|
|
330
|
+
* but only re-renders the consumer when `isEqual` reports a change, so it's
|
|
331
|
+
* cheap for rarely-changing values like config/themes.
|
|
295
332
|
*/
|
|
296
|
-
export declare function
|
|
297
|
-
start: Date;
|
|
298
|
-
end: Date;
|
|
299
|
-
}): UseMultiCalendarEventsResult;
|
|
300
|
-
|
|
301
|
-
declare interface UseMultiCalendarEventsResult {
|
|
302
|
-
events: CalendarEventWithSource[] | undefined;
|
|
303
|
-
status: FetchStatus;
|
|
304
|
-
error: Error | undefined;
|
|
305
|
-
refetch: () => void;
|
|
306
|
-
}
|
|
333
|
+
export declare function useHassValue<T>(selector: (hass: HomeAssistant | undefined) => T, isEqual?: (a: T, b: T) => boolean): T;
|
|
307
334
|
|
|
308
335
|
/**
|
|
309
336
|
* Observe an element's size via ResizeObserver. The callback fires:
|