@nebulahome/package-sdk 1.1.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -3,6 +3,40 @@
3
3
  The SDK's version is the version of the package API the platform implements: a minor version adds
4
4
  to it, a major one breaks it, and a patch leaves it as it is.
5
5
 
6
+ ## 1.3.0
7
+
8
+ - **The page's actions in its header.** `<PageActions>` from `@nebulahome/package-sdk/ui` draws
9
+ its children in the header of the package's page, beside the title, in place of the platform's
10
+ own "Details": the buttons a page would otherwise give a row of their own. On a card or an
11
+ entity view it draws nothing.
12
+ - **Changing the areas.** `useAreaActions()` adds a floor or a room to the house, renames one,
13
+ moves a floor to another storey or removes an area, with the viewer's permissions (`area:write`,
14
+ which `canEdit` says): a package that draws the house keeps its floors and rooms the house's own.
15
+
16
+ ## 1.2.0
17
+
18
+ - **The past of the house.** `ctx.history.states()` and `ctx.history.telemetry()` on the server
19
+ read the recorded changes and the numeric readings of up to 64 entities over a window, with the
20
+ house's authority as `ctx.entities` has; `useStateHistory()` and `useTelemetry()` in
21
+ `@nebulahome/package-sdk/ui` read them with the viewer's, for a chart, an activity list or a
22
+ replay. Each entity's points come oldest first; a window that holds more than a read returns
23
+ loses its oldest points and names the entity in `truncated`. A window is two moments, or in the
24
+ interface the last so many milliseconds, which moves with each read; telemetry is read raw, or
25
+ in buckets of a minute, an hour or a day, `auto` picking by the window's length.
26
+ `createTestContext({ history })` and `test.setHistory()` give a unit test a past.
27
+ - **The areas.** `useAreas()` lists the house, its floors (with their storey) and its rooms, each
28
+ naming its parent by key - the key `EntityView.areaId` holds - and the house its position.
29
+ - **What an entity can do.** `EntityView.capabilities` holds the capabilities an entity declares,
30
+ with the device's limits: a cover's tilt, a thermostat's range and modes, a select's options.
31
+ - **Pictures.** `useEntityPicture()` gives a camera's latest still, fetched again every so often,
32
+ or an image entity's picture, as an address an `<img>` shows.
33
+ - **The sun.** `@nebulahome/package-sdk/sun`: `sunPosition`, `sunTimes`, `sunState` and
34
+ `sunPhaseLabel`, the platform's own formulas, for a moment the sun entities did not report.
35
+ - A package's id may start with a digit (`3d-home`); its entities' object ids then do too.
36
+ - **The MQTT broker is the platform's.** The hub runs the house's broker: `requires.services:
37
+ ["mqtt-broker"]` is always met, `{platform.mqtt.*}` and `NEBULA_PLATFORM_MQTT_*` name it, and a
38
+ package that `provides` `mqtt-broker` is refused at install.
39
+
6
40
  ## 1.1.0
7
41
 
8
42
  - `useTranslation()` in `@nebulahome/package-sdk/ui`: a package's page, cards and entity views
package/README.md CHANGED
@@ -116,17 +116,42 @@ A string a catalogue lacks is said in English, and `nebulahome-package build` li
116
116
  `nebula.config.ts` declares (the package's name, its cards' and settings' names, its entity types)
117
117
  stays English: the platform shows it the same way to everyone.
118
118
 
119
+ ## The past, the areas and the sun
120
+
121
+ ```tsx
122
+ // a sensor's chart: the last 6 hours, in minutes
123
+ const day = useTelemetry({ entityIds: [entityId], window: { lastMs: 6 * 3_600_000 } });
124
+ const points = day.series[entityId] ?? []; // oldest first: { time, value, min, max }
125
+
126
+ // what a door did today, and the rooms of the house
127
+ const today = useStateHistory({ entityIds: [door], window: { from: midnight, to: now } });
128
+ const rooms = useAreas().filter((area) => area.kind === 'room');
129
+ ```
130
+
131
+ ```ts
132
+ // on the server, with the house's authority
133
+ const { series } = await ctx.history.states({ entityIds, from: yesterday, to: new Date() });
134
+ ```
135
+
136
+ ```ts
137
+ // where the sun was, for a moment the sun entities did not report
138
+ import { sunPosition } from '@nebulahome/package-sdk/sun';
139
+
140
+ const { elevation, azimuth } = sunPosition(at, { latitude, longitude });
141
+ ```
142
+
119
143
  ## Entry points
120
144
 
121
- | Import | What |
122
- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
123
- | `@nebulahome/package-sdk` | `definePackage`, `defineSettings`, `defineCard`, `defineEntityType`, `definePage`, `defineContainer`, `defineCompose`, `field`, entity id helpers, `SDK_VERSION`, the value types. |
124
- | `/server` | `defineServer`, and the context `start` is given: settings (live or restart), devices and entities, typed custom entities, the rest of the house, methods for the interface, services between packages. |
125
- | `/ui` | The props of a page, a card and an entity view; hooks for the package, its methods, its settings, the house, the viewer, the theme, its strings; navigation inside the package's page and the platform's. |
126
- | `/components` | The part of the interface's design system a package may draw with. |
127
- | `/vite` | `nebula()`, the Vite plugin that builds both halves: the interface in chunks with its CSS scoped to the package, the server bundled whole. |
128
- | `/testing` | `createTestContext()`: run `start` in a unit test, every effect recorded. |
129
- | `/styles.css` | The interface's Tailwind tokens, to `@reference`. |
145
+ | Import | What |
146
+ | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
147
+ | `@nebulahome/package-sdk` | `definePackage`, `defineSettings`, `defineCard`, `defineEntityType`, `definePage`, `defineContainer`, `defineCompose`, `field`, entity id helpers, `SDK_VERSION`, the value types. |
148
+ | `/server` | `defineServer`, and the context `start` is given: settings (live or restart), devices and entities, typed custom entities, the rest of the house and its past, methods for the interface, services between packages. |
149
+ | `/ui` | The props of a page, a card and an entity view; hooks for the package, its methods, its settings, the house, its areas, its past and its pictures, the viewer, the theme, its strings; navigation inside the package's page and the platform's. |
150
+ | `/sun` | Where the sun is at any moment, by the platform's own formulas. |
151
+ | `/components` | The part of the interface's design system a package may draw with. |
152
+ | `/vite` | `nebula()`, the Vite plugin that builds both halves: the interface in chunks with its CSS scoped to the package, the server bundled whole. |
153
+ | `/testing` | `createTestContext()`: run `start` in a unit test, every effect recorded. |
154
+ | `/styles.css` | The interface's Tailwind tokens, to `@reference`. |
130
155
 
131
156
  The `nebulahome-package` command checks, builds and packs a package:
132
157
 
@@ -1,4 +1,5 @@
1
1
  import { G as CapabilityState, V as CapabilityCommand, X as EntityStateClass, Y as DeclaredCapabilities, a as SettingsOf, ct as EntityDomain, d as EntityTypeDefinition, et as ReportedCapabilityState, f as ValuesOf, i as SettingsDefinition, lt as EntityId, r as JsonValue, s as ArgsOf, t as JsonObject, u as CommandsOf } from "./json.js";
2
+ import { a as TelemetryResolution, i as TelemetryPoint, n as HistorySeries, r as StateHistoryPoint } from "./history.js";
2
3
  //#region src/schema.d.ts
3
4
  /**
4
5
  * What checks a value that crosses into a package - a method's input, a service's answer: any
@@ -163,6 +164,28 @@ type EntityFilter = {
163
164
  readonly domain?: EntityDomain;
164
165
  readonly entityIds?: readonly EntityId[];
165
166
  };
167
+ /** Which entities' past `ctx.history` reads, over which window. Since 1.2. */
168
+ type HistoryRead = {
169
+ /** At most 64. */
170
+ readonly entityIds: readonly EntityId[];
171
+ /** Inclusive: a `Date` or ISO 8601. */
172
+ readonly from: Date | string;
173
+ /** Exclusive. */
174
+ readonly to: Date | string;
175
+ /** The most points of one entity, at most 10 000 (the default): the newest are kept. */
176
+ readonly limit?: number;
177
+ };
178
+ /** Which entities' readings `ctx.history.telemetry` reads: `auto` picks by the window's length. */
179
+ type TelemetryRead = HistoryRead & {
180
+ readonly resolution?: TelemetryResolution | 'auto';
181
+ };
182
+ /** What a read of the past found. */
183
+ type HistoryAnswer<TPoint> = {
184
+ /** Each entity's points, oldest first. */
185
+ readonly series: HistorySeries<TPoint>;
186
+ /** The entities whose window held more than the limit: their oldest points are gone. */
187
+ readonly truncated: readonly EntityId[];
188
+ };
166
189
  /** Who may call a method: whoever may read packages, or whoever may configure them. */
167
190
  type RpcAccess = 'read' | 'configure';
168
191
  /** Who called a method of the package's interface. */
@@ -354,6 +377,28 @@ type ServerContext<TSettings = undefined> = {
354
377
  */
355
378
  readonly subscribe: (filter: EntityFilter, handler: (change: StateChange) => Awaitable<void>) => Unsubscribe;
356
379
  };
380
+ /**
381
+ * The past of the house's entities, read only, with the house's authority as `entities` has.
382
+ * Since 1.2.
383
+ */
384
+ readonly history: {
385
+ /**
386
+ * The recorded changes of some entities over a window.
387
+ *
388
+ * @param read - Which entities, which window, how many points each at most.
389
+ * @returns Each entity's changes, oldest first.
390
+ */
391
+ readonly states: (read: HistoryRead) => Promise<HistoryAnswer<StateHistoryPoint>>;
392
+ /**
393
+ * The numeric readings of some entities over a window, raw or in buckets.
394
+ *
395
+ * @param read - Which entities, which window, which resolution.
396
+ * @returns Each entity's readings, oldest first, and the resolution read.
397
+ */
398
+ readonly telemetry: (read: TelemetryRead) => Promise<HistoryAnswer<TelemetryPoint> & {
399
+ readonly resolution: TelemetryResolution;
400
+ }>;
401
+ };
357
402
  readonly rpc: {
358
403
  /** Registers a method its interface calls; a name registered twice throws. */
359
404
  readonly method: RpcMethod;
@@ -366,4 +411,4 @@ type ServerContext<TSettings = undefined> = {
366
411
  };
367
412
  };
368
413
  //#endregion
369
- export { StandardSchemaV1 as A, StateChange as C, Schema as D, SafeParser as E, SchemaIssue as O, SettingsChange as S, Unsubscribe as T, ServerContext as _, CustomEntityInput as a, ServiceCallFunction as b, EntityInput as c, Logger as d, ReportOptions as f, RpcMethod as g, RpcCaller as h, CustomEntities as i, StandardResult as k, EntitySnapshot as l, RpcCall as m, CapabilityEntityInput as n, DeviceInput as o, RpcAccess as p, CustomCommand as r, EntityFilter as s, Awaitable as t, LogMethod as u, ServerSettings as v, SyncResult as w, ServiceProvide as x, ServiceCall as y };
414
+ export { Schema as A, ServiceProvide as C, TelemetryRead as D, SyncResult as E, StandardResult as M, StandardSchemaV1 as N, Unsubscribe as O, ServiceCallFunction as S, StateChange as T, RpcCaller as _, CustomEntityInput as a, ServerSettings as b, EntityInput as c, HistoryRead as d, LogMethod as f, RpcCall as g, RpcAccess as h, CustomEntities as i, SchemaIssue as j, SafeParser as k, EntitySnapshot as l, ReportOptions as m, CapabilityEntityInput as n, DeviceInput as o, Logger as p, CustomCommand as r, EntityFilter as s, Awaitable as t, HistoryAnswer as u, RpcMethod as v, SettingsChange as w, ServiceCall as x, ServerContext as y };
@@ -0,0 +1,39 @@
1
+ import { G as CapabilityState, lt as EntityId, t as JsonObject } from "./json.js";
2
+ //#region src/history.d.ts
3
+ /** How finely readings are read: every one, or a bucket of a minute, an hour or a day. */
4
+ type TelemetryResolution = 'raw' | '1m' | '1h' | '1d';
5
+ /** What caused a change, when the platform knows it. */
6
+ type ChangeCause = {
7
+ readonly type: 'automation' | 'scene' | 'user' | 'device' | 'alias' | 'startup';
8
+ /** Which run, scene or person; absent for a device and at startup. */
9
+ readonly id?: string;
10
+ };
11
+ /** One change of an entity's state. */
12
+ type StateHistoryPoint = {
13
+ /** When it changed, ISO 8601. */
14
+ readonly time: string;
15
+ /** What it became. */
16
+ readonly state: CapabilityState;
17
+ /** What it was; null for the first report the platform recorded. */
18
+ readonly previousState: CapabilityState | null;
19
+ readonly attributes: JsonObject;
20
+ readonly isAvailable: boolean;
21
+ /** Which package, integration or engine reported it. */
22
+ readonly source: string;
23
+ readonly cause?: ChangeCause;
24
+ };
25
+ /** One reading, or one bucket of readings. */
26
+ type TelemetryPoint = {
27
+ /** The reading's time, or the bucket's start, ISO 8601. */
28
+ readonly time: string;
29
+ /** The reading, or the bucket's average. */
30
+ readonly value: number;
31
+ /** A bucket's lowest, highest and how many readings it holds; absent for raw readings. */
32
+ readonly min?: number;
33
+ readonly max?: number;
34
+ readonly count?: number;
35
+ };
36
+ /** Each entity's points, oldest first: an entity never recorded, or not readable, has none. */
37
+ type HistorySeries<TPoint> = Readonly<Record<EntityId, readonly TPoint[]>>;
38
+ //#endregion
39
+ export { TelemetryResolution as a, TelemetryPoint as i, HistorySeries as n, StateHistoryPoint as r, ChangeCause as t };
@@ -33,7 +33,7 @@ declare function isEntityId(value: unknown): value is EntityId;
33
33
  //#region src/capabilities.d.ts
34
34
  /** Every capability an entity may declare. */
35
35
  type CapabilityName = 'on_off' | 'level' | 'color_hs' | 'color_temp' | 'cover' | 'lock' | 'thermostat' | 'sensor_numeric' | 'sensor_binary' | 'sensor_text' | 'media_player' | 'camera' | 'button' | 'update' | 'lawn_mower' | 'select' | 'number' | 'vacuum' | 'image' | 'custom';
36
- /** How a sensor's readings add up: Home Assistant's state classes, for statistics. */
36
+ /** How a sensor's readings add up, for statistics: a reading, a running total, or a meter. */
37
37
  type EntityStateClass = 'measurement' | 'total' | 'total_increasing';
38
38
  /** The modes a thermostat may be set to. */
39
39
  type ThermostatMode = 'off' | 'heat' | 'cool' | 'auto' | 'fan_only' | 'dry';
@@ -1,5 +1,5 @@
1
1
  import { t as PackageConfigError } from "./node-problems.js";
2
- import { a as isRecord$1, b as strictObject, i as generatePackage, l as packageIdSchema, n as keepingNodeEnv, r as RELEASE_PATHS, u as DEPENDENCY_ID_PATTERN, x as string, y as record } from "./node-config.js";
2
+ import { a as isRecord$1, b as strictObject, i as generatePackage, l as DEPENDENCY_ID_PATTERN, n as keepingNodeEnv, p as packageIdSchema, r as RELEASE_PATHS, x as string, y as record } from "./node-config.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
5
5
  import path from "node:path";
@@ -6850,7 +6850,7 @@ function composeFileProblems(text, settingKeys) {
6850
6850
  * the two are compared before a package is installed or started (PACKAGES_DOCS.md 11).
6851
6851
  */
6852
6852
  /** The SDK's own version, `1.0.0`. */
6853
- const SDK_VERSION = "1.1.0";
6853
+ const SDK_VERSION = "1.3.0";
6854
6854
  //#endregion
6855
6855
  //#region src/vite/catalogues.ts
6856
6856
  /**
@@ -5594,6 +5594,39 @@ function superRefine(fn, params) {
5594
5594
  function number(params) {
5595
5595
  return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
5596
5596
  }
5597
+ //#endregion
5598
+ //#region ../contracts/dist/package-id.js
5599
+ /**
5600
+ * A package's identifier, apart from the manifest: the integrations' instance ids share its
5601
+ * pattern, and the modules that need only the pattern must not pull the manifest's whole grammar
5602
+ * - and what that imports - in with it.
5603
+ */
5604
+ /**
5605
+ * A package identifier: `mqtt`, `nvr`, `3d-home`. It may start with a digit; its entities' object
5606
+ * ids then do too (`sensor.3d_home_kitchen_temperature`).
5607
+ *
5608
+ * Mirrors `packages_id_format` in DATABASE.md. It shares one namespace with the integrations'
5609
+ * instance ids, since both answer `svc.<id>.command`.
5610
+ */
5611
+ const PACKAGE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
5612
+ /** The reserved ids, for a quick look-up. */
5613
+ const RESERVED = /* @__PURE__ */ new Set([
5614
+ "api",
5615
+ "backup",
5616
+ "core",
5617
+ "derived",
5618
+ "hub",
5619
+ "logger",
5620
+ "nebula",
5621
+ "packet-manager",
5622
+ "platform",
5623
+ "proxy",
5624
+ "supervisor",
5625
+ "system",
5626
+ "web-ui"
5627
+ ]);
5628
+ /** Validates a package identifier. */
5629
+ const packageIdSchema = string().regex(PACKAGE_ID_PATTERN, "Must be a package id").refine((id) => !RESERVED.has(id), { message: "Is the name of a part of the platform, which no package may take" });
5597
5630
  /** Validates an icon identifier. */
5598
5631
  const iconIdSchema = string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "Must be an icon id");
5599
5632
  /**
@@ -6102,38 +6135,6 @@ const integrationSetupSchema = strictObject({
6102
6135
  });
6103
6136
  });
6104
6137
  //#endregion
6105
- //#region ../contracts/dist/package-id.js
6106
- /**
6107
- * A package's identifier, apart from the manifest: the integrations' instance ids share its
6108
- * pattern, and the modules that need only the pattern must not pull the manifest's whole grammar
6109
- * - and what that imports - in with it.
6110
- */
6111
- /**
6112
- * A package identifier: `mqtt`, `nvr`.
6113
- *
6114
- * Mirrors `packages_id_format` in DATABASE.md. It shares one namespace with the integrations'
6115
- * instance ids, since both answer `svc.<id>.command`.
6116
- */
6117
- const PACKAGE_ID_PATTERN = /^[a-z][a-z0-9-]{1,63}$/;
6118
- /** The reserved ids, for a quick look-up. */
6119
- const RESERVED = /* @__PURE__ */ new Set([
6120
- "api",
6121
- "backup",
6122
- "core",
6123
- "derived",
6124
- "hub",
6125
- "logger",
6126
- "nebula",
6127
- "packet-manager",
6128
- "platform",
6129
- "proxy",
6130
- "supervisor",
6131
- "system",
6132
- "web-ui"
6133
- ]);
6134
- /** Validates a package identifier. */
6135
- const packageIdSchema = string().regex(PACKAGE_ID_PATTERN, "Must be a package id").refine((id) => !RESERVED.has(id), { message: "Is the name of a part of the platform, which no package may take" });
6136
- //#endregion
6137
6138
  //#region ../contracts/dist/package-requires.js
6138
6139
  /**
6139
6140
  * What a package - or a vendor, in its `manifest.json` - needs besides itself (PACKAGES.md 7).
@@ -6141,7 +6142,7 @@ const packageIdSchema = string().regex(PACKAGE_ID_PATTERN, "Must be a package id
6141
6142
  * Apart from the manifest so that the vendors' grammar can name it without pulling the package
6142
6143
  * manifest's whole grammar - and what that imports - in with it.
6143
6144
  */
6144
- /** A service a package provides and others require: `mqtt-broker`, `object-detection`. */
6145
+ /** A service others require: `object-detection`, or `mqtt-broker`, which the platform runs. */
6145
6146
  const packageServiceNameSchema = string().regex(/^[a-z][a-z0-9-]{1,63}$/, "Must be a lower-case service name");
6146
6147
  /**
6147
6148
  * What a package - or a vendor, in its `manifest.json` - needs besides itself: the platform, by
@@ -6173,8 +6174,9 @@ const packageRequiresSchema = strictObject({
6173
6174
  * - **`ui`** is what it adds to the interface: a page of its own, cards for the dashboard, views
6174
6175
  * for its own kinds of entity - one ES module, served same-origin and loaded on demand.
6175
6176
  * - **`requires`** and **`provides`** are its dependencies: packages by id and version range, and
6176
- * services by name - `mqtt-broker` - that whichever package provides them satisfies. A vendor's
6177
- * `manifest.json` writes `requires` the same way.
6177
+ * services by name - `object-detection` - that whichever package provides them satisfies, or
6178
+ * that the platform runs itself (`mqtt-broker`). A vendor's `manifest.json` writes `requires`
6179
+ * the same way.
6178
6180
  * - **`entityTypes`** are kinds of entity no capability describes - a recording, a playlist - with
6179
6181
  * the fields of their state and the commands they take; an entity of one has the `custom`
6180
6182
  * capability, and the package's `entityViews` draw it.
@@ -6883,4 +6885,4 @@ async function keepingNodeEnv(run) {
6883
6885
  }
6884
6886
  }
6885
6887
  //#endregion
6886
- export { url as C, union as S, date as _, isRecord as a, strictObject as b, packageServiceNameSchema as c, semverRangeSchema as d, semverSchema as f, array as g, _enum as h, generatePackage as i, packageIdSchema as l, ZodISODateTime as m, keepingNodeEnv as n, PACKAGE_CATEGORIES as o, iconIdSchema as p, RELEASE_PATHS as r, packageRequiresSchema as s, githubEnvironment as t, DEPENDENCY_ID_PATTERN as u, int as v, _isoDateTime as w, string as x, record as y };
6888
+ export { url as C, union as S, date as _, isRecord as a, strictObject as b, packageServiceNameSchema as c, semverSchema as d, iconIdSchema as f, array as g, _enum as h, generatePackage as i, DEPENDENCY_ID_PATTERN as l, ZodISODateTime as m, keepingNodeEnv as n, PACKAGE_CATEGORIES as o, packageIdSchema as p, RELEASE_PATHS as r, packageRequiresSchema as s, githubEnvironment as t, semverRangeSchema as u, int as v, _isoDateTime as w, string as x, record as y };
@@ -1,5 +1,5 @@
1
1
  import { t as PackageConfigError } from "./node-problems.js";
2
- import { C as url, S as union, _ as date, a as isRecord, b as strictObject, c as packageServiceNameSchema, d as semverRangeSchema, f as semverSchema, g as array, h as _enum, l as packageIdSchema, m as ZodISODateTime, o as PACKAGE_CATEGORIES, p as iconIdSchema, r as RELEASE_PATHS, s as packageRequiresSchema, t as githubEnvironment, v as int, w as _isoDateTime, x as string } from "./node-config.js";
2
+ import { C as url, S as union, _ as date, a as isRecord, b as strictObject, c as packageServiceNameSchema, d as semverSchema, f as iconIdSchema, g as array, h as _enum, m as ZodISODateTime, o as PACKAGE_CATEGORIES, p as packageIdSchema, r as RELEASE_PATHS, s as packageRequiresSchema, t as githubEnvironment, u as semverRangeSchema, v as int, w as _isoDateTime, x as string } from "./node-config.js";
3
3
  import ro, { readFile, readdir, writeFile } from "node:fs/promises";
4
4
  import path, { basename, join, posix, win32 } from "node:path";
5
5
  import { Buffer as Buffer$1 } from "buffer";
@@ -7,6 +7,6 @@
7
7
  * the two are compared before a package is installed or started (PACKAGES_DOCS.md 11).
8
8
  */
9
9
  /** The SDK's own version, `1.0.0`. */
10
- const SDK_VERSION = "1.1.0";
10
+ const SDK_VERSION = "1.3.0";
11
11
  //#endregion
12
12
  export { SDK_VERSION as t };
package/dist/index.d.ts CHANGED
@@ -152,7 +152,7 @@ type PackageUi<TCards extends readonly CardDefinition[], TTypes extends readonly
152
152
  };
153
153
  /** What every package says, whatever it runs. */
154
154
  type PackageBase<TCards extends readonly CardDefinition[], TTypes extends readonly EntityTypeDefinition[]> = {
155
- /** `^[a-z][a-z0-9-]{1,63}$`, not one of the platform's names. */
155
+ /** `^[a-z0-9][a-z0-9-]{1,63}$`, not one of the platform's names. */
156
156
  readonly id: string;
157
157
  readonly name: string;
158
158
  readonly description: string;
package/dist/index.js CHANGED
@@ -130,7 +130,7 @@ const ENTITY_DOMAINS = [
130
130
  "alias"
131
131
  ];
132
132
  /** The shape of an entity identifier: a domain, a dot, an object id. */
133
- const ENTITY_ID_PATTERN = /^([a-z][a-z0-9_]{0,31})\.([a-z][a-z0-9_]{0,63})$/;
133
+ const ENTITY_ID_PATTERN = /^([a-z][a-z0-9_]{0,31})\.([a-z0-9][a-z0-9_]{0,63})$/;
134
134
  /**
135
135
  * Says whether a word is one of the platform's domains.
136
136
  *
package/dist/server.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { i as SettingsDefinition } from "./chunks/json.js";
2
- import { A as StandardSchemaV1, C as StateChange, D as Schema, E as SafeParser, O as SchemaIssue, S as SettingsChange, T as Unsubscribe, _ as ServerContext, a as CustomEntityInput, b as ServiceCallFunction, c as EntityInput, d as Logger, f as ReportOptions, g as RpcMethod, h as RpcCaller, i as CustomEntities, k as StandardResult, l as EntitySnapshot, m as RpcCall, n as CapabilityEntityInput, o as DeviceInput, p as RpcAccess, r as CustomCommand, s as EntityFilter, t as Awaitable, u as LogMethod, v as ServerSettings, w as SyncResult, x as ServiceProvide, y as ServiceCall } from "./chunks/context.js";
2
+ import { a as TelemetryResolution, i as TelemetryPoint, n as HistorySeries, r as StateHistoryPoint, t as ChangeCause } from "./chunks/history.js";
3
+ import { A as Schema, C as ServiceProvide, D as TelemetryRead, E as SyncResult, M as StandardResult, N as StandardSchemaV1, O as Unsubscribe, S as ServiceCallFunction, T as StateChange, _ as RpcCaller, a as CustomEntityInput, b as ServerSettings, c as EntityInput, d as HistoryRead, f as LogMethod, g as RpcCall, h as RpcAccess, i as CustomEntities, j as SchemaIssue, k as SafeParser, l as EntitySnapshot, m as ReportOptions, n as CapabilityEntityInput, o as DeviceInput, p as Logger, r as CustomCommand, s as EntityFilter, t as Awaitable, u as HistoryAnswer, v as RpcMethod, w as SettingsChange, x as ServiceCall, y as ServerContext } from "./chunks/context.js";
3
4
  //#region src/server/index.d.ts
4
5
  /** What stops what `start` opened. */
5
6
  export type Stop = () => Awaitable<void>;
@@ -27,4 +28,4 @@ export type ServerDefinition<TSettings extends SettingsDefinition | undefined =
27
28
  */
28
29
  export declare function defineServer<TSettings extends SettingsDefinition | undefined = undefined>(server: Omit<ServerDefinition<TSettings>, 'kind' | 'sdk'>): ServerDefinition<TSettings>;
29
30
  //#endregion
30
- export type { Awaitable, CapabilityEntityInput, CustomCommand, CustomEntities, CustomEntityInput, DeviceInput, EntityFilter, EntityInput, EntitySnapshot, LogMethod, Logger, ReportOptions, RpcAccess, RpcCall, RpcCaller, RpcMethod, SafeParser, Schema, SchemaIssue, ServerContext, ServerSettings, ServiceCall, ServiceCallFunction, ServiceProvide, SettingsChange, StandardResult, StandardSchemaV1, StateChange, SyncResult, Unsubscribe };
31
+ export type { Awaitable, CapabilityEntityInput, ChangeCause, CustomCommand, CustomEntities, CustomEntityInput, DeviceInput, EntityFilter, EntityInput, EntitySnapshot, HistoryAnswer, HistoryRead, HistorySeries, LogMethod, Logger, ReportOptions, RpcAccess, RpcCall, RpcCaller, RpcMethod, SafeParser, Schema, SchemaIssue, ServerContext, ServerSettings, ServiceCall, ServiceCallFunction, ServiceProvide, SettingsChange, StandardResult, StandardSchemaV1, StateChange, StateHistoryPoint, SyncResult, TelemetryPoint, TelemetryRead, TelemetryResolution, Unsubscribe };
package/dist/styles.css CHANGED
@@ -116,9 +116,12 @@
116
116
  /** Scenes, aliases and computed things; the If lane of an automation. */
117
117
  --color-violet: #a78bfa;
118
118
 
119
- /** Sensors, charts, live scanning; the When lane of an automation. */
119
+ /** Sensors, charts, live scanning; the When lane of an automation. Wi-Fi on a network map. */
120
120
  --color-cyan: #4fd0ee;
121
121
 
122
+ /** Thread, on a network map: its links, its routers. */
123
+ --color-thread: #e879f9;
124
+
122
125
  /* --- states ------------------------------------------------------------------------------ */
123
126
 
124
127
  --color-success: #52cd86;
@@ -258,6 +261,16 @@
258
261
  --text-headline--line-height: 1.25;
259
262
  --text-headline--letter-spacing: -0.02em;
260
263
 
264
+ /** A settings page's own title, beside its icon chip under the breadcrumb. */
265
+ --text-headline-lg: 24px;
266
+ --text-headline-lg--line-height: 1.2;
267
+ --text-headline-lg--letter-spacing: -0.02em;
268
+
269
+ /** The title of a section with its own column of pages, such as Settings. */
270
+ --text-display-sm: 28px;
271
+ --text-display-sm--line-height: 1.15;
272
+ --text-display-sm--letter-spacing: -0.03em;
273
+
261
274
  /** The page title, one per screen, with its meta beside it in `mono-ui`. */
262
275
  --text-display: 30px;
263
276
  --text-display--line-height: 1.15;
@@ -329,14 +342,15 @@
329
342
  /* --- depth -------------------------------------------------------------------------------- */
330
343
  /*
331
344
  * Flat. A panel has no resting shadow; an entity card has the faint lit top edge of `edge`. A
332
- * dialog and the sign-in card take `dialog`, a menu and a dragged card `float`, a focused
333
- * field `focus`.
345
+ * dialog and the sign-in card take `dialog`, a menu and a dragged card `float`, a popover
346
+ * (search results, the icon picker) `popover`, a focused field `focus`.
334
347
  */
335
348
 
336
349
  --shadow-edge: inset 0 1px 0 rgb(255 255 255 / 0.04);
337
350
  --shadow-focus: 0 0 0 3px rgb(124 134 255 / 0.25);
338
351
  --shadow-float: 0 24px 48px -12px rgb(0 0 0 / 0.7);
339
352
  --shadow-dialog: 0 30px 80px -20px rgb(0 0 0 / 0.9);
353
+ --shadow-popover: 0 28px 60px -20px rgb(0 0 0 / 0.95);
340
354
 
341
355
  /* --- motion ---------------------------------------------------------------------------- */
342
356
 
@@ -547,3 +561,15 @@
547
561
  @utility tabular {
548
562
  font-variant-numeric: tabular-nums;
549
563
  }
564
+
565
+ /*
566
+ * The Matter mark's square, lit violet from its top-left corner: the broker's hero and its
567
+ * dialogs' lead. Over the square's own ground, so it sits on `bg-surface-raised`.
568
+ */
569
+ @utility matter-glow {
570
+ background-image: radial-gradient(
571
+ 120% 120% at 0% 0%,
572
+ color-mix(in srgb, var(--color-violet) 22%, transparent),
573
+ transparent 60%
574
+ );
575
+ }
package/dist/sun.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ //#region src/sun.d.ts
2
+ /** A place on Earth, in degrees: north and east positive. */
3
+ export type GeoPosition = {
4
+ readonly latitude: number;
5
+ readonly longitude: number;
6
+ };
7
+ /** Where the sun stands, in degrees. */
8
+ export type SunPosition = {
9
+ /** Above the horizon, positive; below it, negative. */
10
+ readonly elevation: number;
11
+ /** Clockwise from true north: 90 east, 180 south. */
12
+ readonly azimuth: number;
13
+ };
14
+ /** A day's sunrise, solar noon and sunset; no rise or set on a polar day or night. */
15
+ export type SunTimes = {
16
+ readonly sunrise: Date | null;
17
+ readonly solarNoon: Date;
18
+ readonly sunset: Date | null;
19
+ };
20
+ /** Where the sun stands, and which way it goes. */
21
+ export type SunState = SunPosition & {
22
+ readonly isRising: boolean;
23
+ readonly isMidnightSun: boolean;
24
+ readonly isPolarNight: boolean;
25
+ };
26
+ /** The light the sun gives, in a word. */
27
+ export type SunPhaseLabel = 'day' | 'golden_hour' | 'blue_hour' | 'night' | 'polar_night';
28
+ /** The platform's sun entities, reported every minute while the house has a position. */
29
+ export declare const SUN_ELEVATION_ENTITY_ID = "sensor.system_sun_elevation";
30
+ export declare const SUN_AZIMUTH_ENTITY_ID = "sensor.system_sun_azimuth";
31
+ /**
32
+ * Where the sun stands at a moment, seen from a place.
33
+ *
34
+ * @param at - The moment.
35
+ * @param position - The place.
36
+ * @returns Its elevation and azimuth, in degrees.
37
+ */
38
+ export declare function sunPosition(at: Date, position: GeoPosition): SunPosition;
39
+ /**
40
+ * The sunrise, solar noon and sunset of the day a moment falls in, at a place.
41
+ *
42
+ * @param at - A moment of the day.
43
+ * @param position - The place.
44
+ * @returns Them; no rise or set on a polar day or night.
45
+ */
46
+ export declare function sunTimes(at: Date, position: GeoPosition): SunTimes;
47
+ /**
48
+ * Where the sun stands at a moment, and whether it rises.
49
+ *
50
+ * @param at - The moment.
51
+ * @param position - The place.
52
+ * @returns Its position, and how its day goes.
53
+ */
54
+ export declare function sunState(at: Date, position: GeoPosition): SunState;
55
+ /**
56
+ * The light of a moment, in a word: day, golden hour, blue hour, night.
57
+ *
58
+ * @param state - Where the sun stands.
59
+ * @returns The word.
60
+ */
61
+ export declare function sunPhaseLabel(state: SunState): SunPhaseLabel;
62
+ //#endregion