@camstack/addon-provider-homeassistant 1.2.18 → 1.2.20
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/dist/addon.js +1 -1
- package/dist/addon.mjs +1 -1
- package/dist/{dist-UfT_fD3-.mjs → dist-BSQpoIVg.mjs} +73 -16
- package/dist/{dist-DuKJzmrJ.js → dist-Bz5ENMVc.js} +73 -16
- package/dist/ha-export/ha-export.addon.js +425 -25
- package/dist/ha-export/ha-export.addon.mjs +425 -25
- package/package.json +2 -3
package/dist/addon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-Bz5ENMVc.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_zlib = require("node:zlib");
|
|
5
5
|
//#region src/ha-device-source.ts
|
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as tamperCapability, A as eventEmitterCapability, B as motionCapability, C as connectivityCapability, Ct as url, D as deviceAdoptionCapability, E as coverCapability, F as humiditySensorCapability, H as notificationOutputCapability, I as imageCapability, J as presenceCapability, K as powerMeterCapability, L as lawnMowerControlCapability, M as floodCapability, N as gasCapability, P as humidifierCapability, Q as switchCapability, R as lockControlCapability, S as colorCapability, St as unknown, T as controlCapability, U as notifierCapability, V as normalizeUnit, W as numericSensorCapability, X as scriptRunnerCapability, Y as pressureSensorCapability, Z as smokeCapability, _ as brokerCapability, _t as number, a as EnumSensorDateTimeFormatSchema, at as waterHeaterCapability, b as carbonMonoxideCapability, bt as string, c as addonRoutesCapability, d as ambientLightSensorCapability, dt as DeviceType, et as temperatureSensorCapability, f as automationControlCapability, ft as createEvent, g as brightnessCapability, gt as literal, h as binaryCapability, ht as discriminatedUnion, it as vibrationCapability, j as fanControlCapability, k as enumSensorCapability, l as airQualitySensorCapability, lt as DeviceFeature, m as bestLocationMatch, mt as array, n as BaseDeviceProvider, nt as vacuumControlCapability, o as TargetSchema, ot as weatherCapability, p as batteryCapability, pt as _enum, q as prepareNotification, rt as valveCapability, s as accessoriesCapability, st as errMsg, t as BaseDevice, tt as updateCapability, u as alarmPanelCapability, ut as DeviceRole, v as buildAddonRouteProvider, vt as object, w as contactCapability, wt as EventCategory, x as climateControlCapability, xt as union, y as buttonCapability, yt as record, z as mediaPlayerCapability } from "./dist-
|
|
1
|
+
import { $ as tamperCapability, A as eventEmitterCapability, B as motionCapability, C as connectivityCapability, Ct as url, D as deviceAdoptionCapability, E as coverCapability, F as humiditySensorCapability, H as notificationOutputCapability, I as imageCapability, J as presenceCapability, K as powerMeterCapability, L as lawnMowerControlCapability, M as floodCapability, N as gasCapability, P as humidifierCapability, Q as switchCapability, R as lockControlCapability, S as colorCapability, St as unknown, T as controlCapability, U as notifierCapability, V as normalizeUnit, W as numericSensorCapability, X as scriptRunnerCapability, Y as pressureSensorCapability, Z as smokeCapability, _ as brokerCapability, _t as number, a as EnumSensorDateTimeFormatSchema, at as waterHeaterCapability, b as carbonMonoxideCapability, bt as string, c as addonRoutesCapability, d as ambientLightSensorCapability, dt as DeviceType, et as temperatureSensorCapability, f as automationControlCapability, ft as createEvent, g as brightnessCapability, gt as literal, h as binaryCapability, ht as discriminatedUnion, it as vibrationCapability, j as fanControlCapability, k as enumSensorCapability, l as airQualitySensorCapability, lt as DeviceFeature, m as bestLocationMatch, mt as array, n as BaseDeviceProvider, nt as vacuumControlCapability, o as TargetSchema, ot as weatherCapability, p as batteryCapability, pt as _enum, q as prepareNotification, rt as valveCapability, s as accessoriesCapability, st as errMsg, t as BaseDevice, tt as updateCapability, u as alarmPanelCapability, ut as DeviceRole, v as buildAddonRouteProvider, vt as object, w as contactCapability, wt as EventCategory, x as climateControlCapability, xt as union, y as buttonCapability, yt as record, z as mediaPlayerCapability } from "./dist-BSQpoIVg.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
|
|
4
4
|
//#region src/ha-device-source.ts
|
|
@@ -15351,21 +15351,32 @@ object({
|
|
|
15351
15351
|
* core, and no per-integration branch anywhere: `/api/oauth2/authorize` and
|
|
15352
15352
|
* `/api/oauth2/integrations` are built from this collection alone.
|
|
15353
15353
|
*
|
|
15354
|
-
* **Scopes
|
|
15355
|
-
*
|
|
15356
|
-
*
|
|
15357
|
-
*
|
|
15358
|
-
*
|
|
15354
|
+
* **Scopes. `requestedScopes` has exactly ONE meaning: what the integration
|
|
15355
|
+
* NEEDS to function.** Not a blast radius, not a conservative
|
|
15356
|
+
* under-declaration, not a description of some other path the addon happens to
|
|
15357
|
+
* have. Derive it from what the client actually calls **with this token** —
|
|
15358
|
+
* every tRPC path against `METHOD_ACCESS_MAP`, plus an `addon:` grant for every
|
|
15359
|
+
* addon HTTP route it posts to — and write the call that justifies each entry
|
|
15360
|
+
* next to it. Two integrations once used this field to mean two different
|
|
15361
|
+
* things; the operator ruled there is one meaning, and any third integration
|
|
15362
|
+
* inherits it (2026-08-09).
|
|
15363
|
+
*
|
|
15364
|
+
* This is not documentation, it is the ENFORCEMENT INPUT. Since
|
|
15365
|
+
* [D103](../../../../docs/decisions/adr-0103.md) the `/addon/:addonId/*` gate
|
|
15366
|
+
* checks an integration token's grant before letting it reach an
|
|
15367
|
+
* `access: 'authenticated'` route, so an **under-declaration is an integration
|
|
15368
|
+
* that stops working** — a missing `addon:` entry means `403 Token scope
|
|
15369
|
+
* mismatch` on every control the client tries to actuate. Widen the descriptor
|
|
15370
|
+
* honestly rather than weakening a check to make a route pass.
|
|
15371
|
+
*
|
|
15372
|
+
* Prefer a narrow `capability:` scope to a `category:` one unless the client
|
|
15373
|
+
* genuinely needs a whole family; a category scope grants every future member
|
|
15359
15374
|
* of that category too. `category:system [create]` has been rejected once and
|
|
15360
15375
|
* should stay rejected: it hands `addons.installPackage` to an integration.
|
|
15361
15376
|
*
|
|
15362
|
-
*
|
|
15363
|
-
*
|
|
15364
|
-
*
|
|
15365
|
-
* the addon does the work, while the Home Assistant component calls tRPC
|
|
15366
|
-
* directly with the token. So `requestedScopes` describes the blast radius of
|
|
15367
|
-
* the GRANT, not the reach of the integration; do not widen one to describe the
|
|
15368
|
-
* other.
|
|
15377
|
+
* Calls the ADDON itself makes over `ctx.api` run as the addon and are not
|
|
15378
|
+
* scope-checked, so they are not what this field describes — but reaching the
|
|
15379
|
+
* addon's route in the first place IS, and that is the entry to declare.
|
|
15369
15380
|
*
|
|
15370
15381
|
* **The boot window.** An addon registers its provider after its runner forks
|
|
15371
15382
|
* and initialises, so between hub start and that moment this collection is
|
|
@@ -15406,7 +15417,30 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
15406
15417
|
* present, /api/oauth2/authorize bakes THIS into the code instead of the
|
|
15407
15418
|
* hub-global `publicHubUrl()`, so a forked exporter addon (which can't set
|
|
15408
15419
|
* the hub's env) drives the claim that its cloud Lambda routes back on. */
|
|
15409
|
-
hubUrl: string().optional()
|
|
15420
|
+
hubUrl: string().optional(),
|
|
15421
|
+
/**
|
|
15422
|
+
* How long a REFRESH token issued for this integration lives — seconds, or
|
|
15423
|
+
* `'never'` for a token minted with no `exp` claim at all. Omit to keep the
|
|
15424
|
+
* 30-day default, which is what every link used before this field existed.
|
|
15425
|
+
*
|
|
15426
|
+
* Declared here for the same reason `requestedScopes` is: the integration
|
|
15427
|
+
* knows what it needs. Amazon's account linking and a Home Assistant config
|
|
15428
|
+
* entry are both meant to survive indefinitely, and re-linking is a manual
|
|
15429
|
+
* user action, so a 30-day expiry silently unlinks a working integration.
|
|
15430
|
+
*
|
|
15431
|
+
* **The security posture, stated so it is owned deliberately.** A refresh
|
|
15432
|
+
* token that never expires is permanent access if it leaks. What bounds it is
|
|
15433
|
+
* revocation, not time: `oauthRefresh` re-reads the session on every use and
|
|
15434
|
+
* returns `null` once `revokedAt` is set, as does `oauthVerifyAccessToken`.
|
|
15435
|
+
* The one gap is the ACCESS token — it is a plain signed JWT that nothing
|
|
15436
|
+
* re-checks against the session on the `/trpc` and `/addon/*` paths, so
|
|
15437
|
+
* revoking a link takes effect there only after its remaining hour. That hour
|
|
15438
|
+
* is why the access TTL is not configurable.
|
|
15439
|
+
*
|
|
15440
|
+
* The value is baked into the authorization code at `/authorize` and travels
|
|
15441
|
+
* on the tokens, so editing this field changes FUTURE links only.
|
|
15442
|
+
*/
|
|
15443
|
+
refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
15410
15444
|
});
|
|
15411
15445
|
var oauthIntegrationCapability = {
|
|
15412
15446
|
name: "oauth-integration",
|
|
@@ -18549,11 +18583,29 @@ var SsoBridgeClaimsSchema = object({
|
|
|
18549
18583
|
codeChallenge: string().optional(),
|
|
18550
18584
|
/** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
|
|
18551
18585
|
* tokens so the verify path can check the session is not revoked. */
|
|
18552
|
-
sessionId: string().optional()
|
|
18586
|
+
sessionId: string().optional(),
|
|
18587
|
+
/**
|
|
18588
|
+
* The refresh lifetime this LINK was created with, in seconds, or `'never'`.
|
|
18589
|
+
* Baked into the code at `/authorize` from the integration's descriptor and
|
|
18590
|
+
* carried forward so `oauthRefresh` re-mints with the same lifetime. It rides
|
|
18591
|
+
* on the token rather than being re-read from the descriptor on purpose:
|
|
18592
|
+
* editing a descriptor must not retroactively extend or shorten a link the
|
|
18593
|
+
* operator already consented to.
|
|
18594
|
+
*/
|
|
18595
|
+
refreshTtl: union([number().int().positive(), literal("never")]).optional()
|
|
18553
18596
|
});
|
|
18554
18597
|
method(object({
|
|
18555
18598
|
claims: SsoBridgeClaimsSchema,
|
|
18556
|
-
|
|
18599
|
+
/**
|
|
18600
|
+
* Seconds, or `'never'` for a token minted with NO `exp` claim.
|
|
18601
|
+
*
|
|
18602
|
+
* `'never'` is a literal rather than `undefined`/`0` because omitting
|
|
18603
|
+
* this field already means "the 5-minute SSO hand-off default", and
|
|
18604
|
+
* `jwt.sign` THROWS on `{ expiresIn: undefined }` — a "no expiry" that
|
|
18605
|
+
* went through the numeric path would fail at mint time and break
|
|
18606
|
+
* linking rather than produce an eternal token.
|
|
18607
|
+
*/
|
|
18608
|
+
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
18557
18609
|
}), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
|
|
18558
18610
|
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
18559
18611
|
providerId: string().min(1),
|
|
@@ -25834,7 +25886,12 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
|
|
|
25834
25886
|
hubUrl: string(),
|
|
25835
25887
|
/** PKCE (RFC 7636) S256 challenge. Baked into the signed code; a code
|
|
25836
25888
|
* that carries one can ONLY be exchanged with the matching verifier. */
|
|
25837
|
-
codeChallenge: string().optional()
|
|
25889
|
+
codeChallenge: string().optional(),
|
|
25890
|
+
/** The integration's declared refresh lifetime — seconds, or `'never'`.
|
|
25891
|
+
* From `OauthIntegrationDescriptor.refreshTokenTtlSec`. Baked into the
|
|
25892
|
+
* code so the link carries its own lifetime; omit for the 30-day
|
|
25893
|
+
* default. */
|
|
25894
|
+
refreshTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
25838
25895
|
}), object({ code: string() }), {
|
|
25839
25896
|
kind: "mutation",
|
|
25840
25897
|
access: "create"
|
|
@@ -15351,21 +15351,32 @@ object({
|
|
|
15351
15351
|
* core, and no per-integration branch anywhere: `/api/oauth2/authorize` and
|
|
15352
15352
|
* `/api/oauth2/integrations` are built from this collection alone.
|
|
15353
15353
|
*
|
|
15354
|
-
* **Scopes
|
|
15355
|
-
*
|
|
15356
|
-
*
|
|
15357
|
-
*
|
|
15358
|
-
*
|
|
15354
|
+
* **Scopes. `requestedScopes` has exactly ONE meaning: what the integration
|
|
15355
|
+
* NEEDS to function.** Not a blast radius, not a conservative
|
|
15356
|
+
* under-declaration, not a description of some other path the addon happens to
|
|
15357
|
+
* have. Derive it from what the client actually calls **with this token** —
|
|
15358
|
+
* every tRPC path against `METHOD_ACCESS_MAP`, plus an `addon:` grant for every
|
|
15359
|
+
* addon HTTP route it posts to — and write the call that justifies each entry
|
|
15360
|
+
* next to it. Two integrations once used this field to mean two different
|
|
15361
|
+
* things; the operator ruled there is one meaning, and any third integration
|
|
15362
|
+
* inherits it (2026-08-09).
|
|
15363
|
+
*
|
|
15364
|
+
* This is not documentation, it is the ENFORCEMENT INPUT. Since
|
|
15365
|
+
* [D103](../../../../docs/decisions/adr-0103.md) the `/addon/:addonId/*` gate
|
|
15366
|
+
* checks an integration token's grant before letting it reach an
|
|
15367
|
+
* `access: 'authenticated'` route, so an **under-declaration is an integration
|
|
15368
|
+
* that stops working** — a missing `addon:` entry means `403 Token scope
|
|
15369
|
+
* mismatch` on every control the client tries to actuate. Widen the descriptor
|
|
15370
|
+
* honestly rather than weakening a check to make a route pass.
|
|
15371
|
+
*
|
|
15372
|
+
* Prefer a narrow `capability:` scope to a `category:` one unless the client
|
|
15373
|
+
* genuinely needs a whole family; a category scope grants every future member
|
|
15359
15374
|
* of that category too. `category:system [create]` has been rejected once and
|
|
15360
15375
|
* should stay rejected: it hands `addons.installPackage` to an integration.
|
|
15361
15376
|
*
|
|
15362
|
-
*
|
|
15363
|
-
*
|
|
15364
|
-
*
|
|
15365
|
-
* the addon does the work, while the Home Assistant component calls tRPC
|
|
15366
|
-
* directly with the token. So `requestedScopes` describes the blast radius of
|
|
15367
|
-
* the GRANT, not the reach of the integration; do not widen one to describe the
|
|
15368
|
-
* other.
|
|
15377
|
+
* Calls the ADDON itself makes over `ctx.api` run as the addon and are not
|
|
15378
|
+
* scope-checked, so they are not what this field describes — but reaching the
|
|
15379
|
+
* addon's route in the first place IS, and that is the entry to declare.
|
|
15369
15380
|
*
|
|
15370
15381
|
* **The boot window.** An addon registers its provider after its runner forks
|
|
15371
15382
|
* and initialises, so between hub start and that moment this collection is
|
|
@@ -15406,7 +15417,30 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
15406
15417
|
* present, /api/oauth2/authorize bakes THIS into the code instead of the
|
|
15407
15418
|
* hub-global `publicHubUrl()`, so a forked exporter addon (which can't set
|
|
15408
15419
|
* the hub's env) drives the claim that its cloud Lambda routes back on. */
|
|
15409
|
-
hubUrl: string().optional()
|
|
15420
|
+
hubUrl: string().optional(),
|
|
15421
|
+
/**
|
|
15422
|
+
* How long a REFRESH token issued for this integration lives — seconds, or
|
|
15423
|
+
* `'never'` for a token minted with no `exp` claim at all. Omit to keep the
|
|
15424
|
+
* 30-day default, which is what every link used before this field existed.
|
|
15425
|
+
*
|
|
15426
|
+
* Declared here for the same reason `requestedScopes` is: the integration
|
|
15427
|
+
* knows what it needs. Amazon's account linking and a Home Assistant config
|
|
15428
|
+
* entry are both meant to survive indefinitely, and re-linking is a manual
|
|
15429
|
+
* user action, so a 30-day expiry silently unlinks a working integration.
|
|
15430
|
+
*
|
|
15431
|
+
* **The security posture, stated so it is owned deliberately.** A refresh
|
|
15432
|
+
* token that never expires is permanent access if it leaks. What bounds it is
|
|
15433
|
+
* revocation, not time: `oauthRefresh` re-reads the session on every use and
|
|
15434
|
+
* returns `null` once `revokedAt` is set, as does `oauthVerifyAccessToken`.
|
|
15435
|
+
* The one gap is the ACCESS token — it is a plain signed JWT that nothing
|
|
15436
|
+
* re-checks against the session on the `/trpc` and `/addon/*` paths, so
|
|
15437
|
+
* revoking a link takes effect there only after its remaining hour. That hour
|
|
15438
|
+
* is why the access TTL is not configurable.
|
|
15439
|
+
*
|
|
15440
|
+
* The value is baked into the authorization code at `/authorize` and travels
|
|
15441
|
+
* on the tokens, so editing this field changes FUTURE links only.
|
|
15442
|
+
*/
|
|
15443
|
+
refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
15410
15444
|
});
|
|
15411
15445
|
var oauthIntegrationCapability = {
|
|
15412
15446
|
name: "oauth-integration",
|
|
@@ -18549,11 +18583,29 @@ var SsoBridgeClaimsSchema = object({
|
|
|
18549
18583
|
codeChallenge: string().optional(),
|
|
18550
18584
|
/** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
|
|
18551
18585
|
* tokens so the verify path can check the session is not revoked. */
|
|
18552
|
-
sessionId: string().optional()
|
|
18586
|
+
sessionId: string().optional(),
|
|
18587
|
+
/**
|
|
18588
|
+
* The refresh lifetime this LINK was created with, in seconds, or `'never'`.
|
|
18589
|
+
* Baked into the code at `/authorize` from the integration's descriptor and
|
|
18590
|
+
* carried forward so `oauthRefresh` re-mints with the same lifetime. It rides
|
|
18591
|
+
* on the token rather than being re-read from the descriptor on purpose:
|
|
18592
|
+
* editing a descriptor must not retroactively extend or shorten a link the
|
|
18593
|
+
* operator already consented to.
|
|
18594
|
+
*/
|
|
18595
|
+
refreshTtl: union([number().int().positive(), literal("never")]).optional()
|
|
18553
18596
|
});
|
|
18554
18597
|
method(object({
|
|
18555
18598
|
claims: SsoBridgeClaimsSchema,
|
|
18556
|
-
|
|
18599
|
+
/**
|
|
18600
|
+
* Seconds, or `'never'` for a token minted with NO `exp` claim.
|
|
18601
|
+
*
|
|
18602
|
+
* `'never'` is a literal rather than `undefined`/`0` because omitting
|
|
18603
|
+
* this field already means "the 5-minute SSO hand-off default", and
|
|
18604
|
+
* `jwt.sign` THROWS on `{ expiresIn: undefined }` — a "no expiry" that
|
|
18605
|
+
* went through the numeric path would fail at mint time and break
|
|
18606
|
+
* linking rather than produce an eternal token.
|
|
18607
|
+
*/
|
|
18608
|
+
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
18557
18609
|
}), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
|
|
18558
18610
|
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
18559
18611
|
providerId: string().min(1),
|
|
@@ -25834,7 +25886,12 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
|
|
|
25834
25886
|
hubUrl: string(),
|
|
25835
25887
|
/** PKCE (RFC 7636) S256 challenge. Baked into the signed code; a code
|
|
25836
25888
|
* that carries one can ONLY be exchanged with the matching verifier. */
|
|
25837
|
-
codeChallenge: string().optional()
|
|
25889
|
+
codeChallenge: string().optional(),
|
|
25890
|
+
/** The integration's declared refresh lifetime — seconds, or `'never'`.
|
|
25891
|
+
* From `OauthIntegrationDescriptor.refreshTokenTtlSec`. Baked into the
|
|
25892
|
+
* code so the link carries its own lifetime; omit for the 30-day
|
|
25893
|
+
* default. */
|
|
25894
|
+
refreshTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
25838
25895
|
}), object({ code: string() }), {
|
|
25839
25896
|
kind: "mutation",
|
|
25840
25897
|
access: "create"
|