@camstack/addon-provider-homeassistant 1.2.71 → 1.2.73

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("./dist-VXqwogoH.js");
2
+ const require_dist = require("./dist-BiCYEtvX.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 presenceCapability, A as controlCapability, At as EventCategory, B as humiditySensorCapability, C as buildAddonRouteProvider, Ct as number, D as colorCapability, Dt as union, E as climateControlCapability, Et as string, F as eventEmitterCapability, G as motionCapability, H as lawnMowerControlCapability, I as fanControlCapability, J as notifierCapability, K as normalizeUnit, L as floodCapability, M as deviceAdoptionCapability, O as connectivityCapability, Ot as unknown, P as enumSensorCapability, Q as prepareNotification, R as gasCapability, S as brokerCapability, St as literal, T as carbonMonoxideCapability, Tt as record, U as lockControlCapability, V as imageCapability, W as mediaPlayerCapability, Y as numericSensorCapability, Z as powerMeterCapability, _ as automationControlCapability, _t as createEvent, at as temperatureSensorCapability, b as binaryCapability, bt as array, ct as valveCapability, d as TargetSchema, dt as weatherCapability, et as pressureSensorCapability, f as accessoriesCapability, ft as errMsg, g as ambientLightSensorCapability, gt as DeviceType, h as alarmPanelCapability, ht as DeviceRole, it as tamperCapability, j as coverCapability, k as contactCapability, kt as url, lt as vibrationCapability, m as airQualitySensorCapability, mt as DeviceFeature, n as BaseDevice, nt as smokeCapability, ot as updateCapability, p as addonRoutesCapability, q as notificationOutputCapability, r as BaseDeviceProvider, rt as switchCapability, s as EnumSensorDateTimeFormatSchema, st as vacuumControlCapability, tt as scriptRunnerCapability, ut as waterHeaterCapability, v as batteryCapability, w as buttonCapability, wt as object, x as brightnessCapability, xt as discriminatedUnion, y as bestLocationMatch, yt as _enum, z as humidifierCapability } from "./dist-CiUJy917.mjs";
1
+ import { $ as presenceCapability, A as controlCapability, At as EventCategory, B as humiditySensorCapability, C as buildAddonRouteProvider, Ct as number, D as colorCapability, Dt as union, E as climateControlCapability, Et as string, F as eventEmitterCapability, G as motionCapability, H as lawnMowerControlCapability, I as fanControlCapability, J as notifierCapability, K as normalizeUnit, L as floodCapability, M as deviceAdoptionCapability, O as connectivityCapability, Ot as unknown, P as enumSensorCapability, Q as prepareNotification, R as gasCapability, S as brokerCapability, St as literal, T as carbonMonoxideCapability, Tt as record, U as lockControlCapability, V as imageCapability, W as mediaPlayerCapability, Y as numericSensorCapability, Z as powerMeterCapability, _ as automationControlCapability, _t as createEvent, at as temperatureSensorCapability, b as binaryCapability, bt as array, ct as valveCapability, d as TargetSchema, dt as weatherCapability, et as pressureSensorCapability, f as accessoriesCapability, ft as errMsg, g as ambientLightSensorCapability, gt as DeviceType, h as alarmPanelCapability, ht as DeviceRole, it as tamperCapability, j as coverCapability, k as contactCapability, kt as url, lt as vibrationCapability, m as airQualitySensorCapability, mt as DeviceFeature, n as BaseDevice, nt as smokeCapability, ot as updateCapability, p as addonRoutesCapability, q as notificationOutputCapability, r as BaseDeviceProvider, rt as switchCapability, s as EnumSensorDateTimeFormatSchema, st as vacuumControlCapability, tt as scriptRunnerCapability, ut as waterHeaterCapability, v as batteryCapability, w as buttonCapability, wt as object, x as brightnessCapability, xt as discriminatedUnion, y as bestLocationMatch, yt as _enum, z as humidifierCapability } from "./dist-BQ3x3HG3.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
@@ -10749,6 +10749,89 @@ var LocationStatSchema = object({
10749
10749
  fileCount: number(),
10750
10750
  present: boolean()
10751
10751
  });
10752
+ /** Lifecycle of a backup run. Terminal states: succeeded / failed / cancelled. */
10753
+ var BackupRunStateSchema = _enum([
10754
+ "queued",
10755
+ "running",
10756
+ "succeeded",
10757
+ "failed",
10758
+ "cancelled"
10759
+ ]);
10760
+ /**
10761
+ * Where a running backup currently is. `queued` before it starts,
10762
+ * `building` while the tar.gz is being staged, `uploading` during the
10763
+ * per-destination fan-out, `done` once terminal.
10764
+ */
10765
+ var BackupRunPhaseSchema = _enum([
10766
+ "queued",
10767
+ "building",
10768
+ "uploading",
10769
+ "done"
10770
+ ]);
10771
+ /**
10772
+ * Observable state of one backup run — readable WHILE it runs via
10773
+ * `backup.listRuns`. This is what makes the execution queue and
10774
+ * `backup.cancel` usable: the 2026-09-04 incident (two concurrent
10775
+ * multi-GB builds, staging 5.1 GB → 18 GB, load 62) was only
10776
+ * diagnosable with `du` because nothing reported that runs existed or
10777
+ * how large the staged archive had grown.
10778
+ */
10779
+ var BackupRunSchema = object({
10780
+ /** Stable run id — the handle `backup.cancel` takes. */
10781
+ id: string(),
10782
+ state: BackupRunStateSchema,
10783
+ phase: BackupRunPhaseSchema,
10784
+ /**
10785
+ * Resolved destination location ids. Empty while queued (targets are
10786
+ * resolved when the run starts, against the then-current policies).
10787
+ */
10788
+ destinationIds: array(string()).readonly(),
10789
+ label: string().optional(),
10790
+ /** ms-epoch when the run was submitted (trigger call / schedule fire). */
10791
+ requestedAt: number(),
10792
+ /** ms-epoch when the run left the queue and started building. */
10793
+ startedAt: number().optional(),
10794
+ /** ms-epoch when the run reached a terminal state. */
10795
+ finishedAt: number().optional(),
10796
+ /** Compressed bytes of the staging archive written so far. */
10797
+ stagedBytes: number(),
10798
+ /** Final staged archive size, once the build phase completes. */
10799
+ archiveSizeBytes: number().optional(),
10800
+ /** Bytes pushed to the destination currently uploading. */
10801
+ uploadedBytes: number(),
10802
+ /** Destinations where the archive fully landed (uploaded + indexed). */
10803
+ completedDestinationIds: array(string()).readonly(),
10804
+ /** Destinations that failed during the fan-out. */
10805
+ failedDestinationIds: array(string()).readonly(),
10806
+ /** Failure message when `state === 'failed'`. */
10807
+ error: string().optional(),
10808
+ /**
10809
+ * 1-based place in the execution queue — 1 = runs next. Present only
10810
+ * while `state === 'queued'`. Stamped by the orchestrator from the
10811
+ * queue's OWN pending order, never derived from timestamps, so the
10812
+ * UI cannot show an order the executor will not honour.
10813
+ */
10814
+ queuePosition: number().int().min(1).optional()
10815
+ });
10816
+ /**
10817
+ * Result of `backup.trigger`. The call still resolves when the run
10818
+ * terminates (compat with schedule-driven runs and the admin UI), but
10819
+ * it now names the run and says whether it had to WAIT: a trigger that
10820
+ * arrives while another run is in flight is enqueued (or joined onto
10821
+ * an identical already-queued run), never started concurrently.
10822
+ */
10823
+ var BackupTriggerResultSchema = object({
10824
+ /** The run this trigger mapped to — poll it via `listRuns`, stop it via `cancel`. */
10825
+ runId: string(),
10826
+ /** True when the run waited behind an in-flight run instead of starting immediately. */
10827
+ queued: boolean(),
10828
+ /** True when this trigger was coalesced onto an identical already-queued run. */
10829
+ joined: boolean(),
10830
+ /** True when the run was cancelled before completing every destination. */
10831
+ cancelled: boolean(),
10832
+ /** One entry per destination the archive landed at (partial on cancel). */
10833
+ entries: array(BackupEntrySchema).readonly()
10834
+ });
10752
10835
  /**
10753
10836
  * A backup schedule — the N:M "entry" that binds one cron cadence to a
10754
10837
  * SET of destination locations. Supersedes the per-location cron on
@@ -10796,7 +10879,10 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10796
10879
  * retention (manual runs).
10797
10880
  */
10798
10881
  retentionCount: number().int().min(1).max(1e3).optional()
10799
- }).optional(), array(BackupEntrySchema).readonly(), {
10882
+ }).optional(), BackupTriggerResultSchema, {
10883
+ kind: "mutation",
10884
+ auth: "admin"
10885
+ }), method(_void(), array(BackupRunSchema).readonly(), { auth: "admin" }), method(object({ runId: string() }), object({ cancelled: boolean() }), {
10800
10886
  kind: "mutation",
10801
10887
  auth: "admin"
10802
10888
  }), method(_void(), array(BackupEntrySchema).readonly(), { auth: "admin" }), method(_void(), array(LocationStatSchema).readonly(), { auth: "admin" }), method(object({
@@ -11562,6 +11648,14 @@ method(object({
11562
11648
  }), object({ success: literal(true) }), {
11563
11649
  kind: "mutation",
11564
11650
  auth: "admin"
11651
+ }), method(object({ deviceId: number().int().nonnegative() }), object({
11652
+ derivedStreamsDeleted: array(string()).readonly(),
11653
+ assignmentsPurged: boolean(),
11654
+ probeSnapshotsDropped: number().int().nonnegative(),
11655
+ rtspTokenRowsDeleted: number().int().nonnegative()
11656
+ }), {
11657
+ kind: "mutation",
11658
+ auth: "admin"
11565
11659
  }), method(object({
11566
11660
  deviceId: number(),
11567
11661
  /** Absent = the LOWEST assigned profile — a notification attachment is
@@ -19571,7 +19665,20 @@ var RecentTracksQueryInput = object({
19571
19665
  projection: TrackProjectionSchema.optional(),
19572
19666
  /** Include stationary-promoted rows (parked objects). Default false: the
19573
19667
  * feed lists passages; parking records live on the stationary registry. */
19574
- includeStationary: boolean().optional()
19668
+ includeStationary: boolean().optional(),
19669
+ /**
19670
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
19671
+ *
19672
+ * The same filter `listTracks` takes, because the timeline's class chips must
19673
+ * mean the same thing whether the scope is one camera or twelve. Until this
19674
+ * existed the scoped feed downloaded a page and narrowed it on the phone
19675
+ * while the single-camera path narrowed the read — one filter, two costs.
19676
+ *
19677
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
19678
+ * whose class list is unreadable are kept, and the client's rule stays the
19679
+ * exact one.
19680
+ */
19681
+ classes: array(string()).optional()
19575
19682
  });
19576
19683
  var RecentTracksPageSchema = object({
19577
19684
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -23813,8 +23920,22 @@ var automationControlCapability = {
23813
23920
  * threshold.
23814
23921
  */
23815
23922
  var BatteryStatusSchema = object({
23816
- /** 0..100 inclusive. Firmware-reported. */
23817
- percentage: number().min(0).max(100),
23923
+ /**
23924
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
23925
+ * provider has registered the capability but no reading has landed.
23926
+ *
23927
+ * It is nullable because it was not, and the only value a provider could
23928
+ * seed with was `0`. A battery camera behind an NVR therefore announced
23929
+ * itself at 0% on every start and corrected itself a moment later, which is
23930
+ * indistinguishable from a real flat battery: it fires the low-battery alert
23931
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
23932
+ * reading the difference is an alarm.
23933
+ *
23934
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
23935
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
23936
+ * declines to band a non-finite reading, which is the correct shape.
23937
+ */
23938
+ percentage: number().min(0).max(100).nullable(),
23818
23939
  /**
23819
23940
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
23820
23941
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -33592,6 +33713,12 @@ Object.freeze({
33592
33713
  addonId: null,
33593
33714
  access: "create"
33594
33715
  },
33716
+ "backup.cancel": {
33717
+ capName: "backup",
33718
+ capScope: "system",
33719
+ addonId: null,
33720
+ access: "create"
33721
+ },
33595
33722
  "backup.delete": {
33596
33723
  capName: "backup",
33597
33724
  capScope: "system",
@@ -33634,6 +33761,12 @@ Object.freeze({
33634
33761
  addonId: null,
33635
33762
  access: "view"
33636
33763
  },
33764
+ "backup.listRuns": {
33765
+ capName: "backup",
33766
+ capScope: "system",
33767
+ addonId: null,
33768
+ access: "view"
33769
+ },
33637
33770
  "backup.listSchedules": {
33638
33771
  capName: "backup",
33639
33772
  capScope: "system",
@@ -38200,6 +38333,12 @@ Object.freeze({
38200
38333
  addonId: null,
38201
38334
  access: "create"
38202
38335
  },
38336
+ "streamBroker.forgetDeviceHardware": {
38337
+ capName: "stream-broker",
38338
+ capScope: "system",
38339
+ addonId: null,
38340
+ access: "delete"
38341
+ },
38203
38342
  "streamBroker.getAllRtspEntries": {
38204
38343
  capName: "stream-broker",
38205
38344
  capScope: "system",
@@ -40658,6 +40797,11 @@ Object.freeze({
40658
40797
  form: "single",
40659
40798
  optional: false
40660
40799
  }],
40800
+ "streamBroker.forgetDeviceHardware": [{
40801
+ name: "deviceId",
40802
+ form: "single",
40803
+ optional: false
40804
+ }],
40661
40805
  "streamBroker.getDeviceAudioMute": [{
40662
40806
  name: "deviceId",
40663
40807
  form: "single",
@@ -10749,6 +10749,89 @@ var LocationStatSchema = object({
10749
10749
  fileCount: number(),
10750
10750
  present: boolean()
10751
10751
  });
10752
+ /** Lifecycle of a backup run. Terminal states: succeeded / failed / cancelled. */
10753
+ var BackupRunStateSchema = _enum([
10754
+ "queued",
10755
+ "running",
10756
+ "succeeded",
10757
+ "failed",
10758
+ "cancelled"
10759
+ ]);
10760
+ /**
10761
+ * Where a running backup currently is. `queued` before it starts,
10762
+ * `building` while the tar.gz is being staged, `uploading` during the
10763
+ * per-destination fan-out, `done` once terminal.
10764
+ */
10765
+ var BackupRunPhaseSchema = _enum([
10766
+ "queued",
10767
+ "building",
10768
+ "uploading",
10769
+ "done"
10770
+ ]);
10771
+ /**
10772
+ * Observable state of one backup run — readable WHILE it runs via
10773
+ * `backup.listRuns`. This is what makes the execution queue and
10774
+ * `backup.cancel` usable: the 2026-09-04 incident (two concurrent
10775
+ * multi-GB builds, staging 5.1 GB → 18 GB, load 62) was only
10776
+ * diagnosable with `du` because nothing reported that runs existed or
10777
+ * how large the staged archive had grown.
10778
+ */
10779
+ var BackupRunSchema = object({
10780
+ /** Stable run id — the handle `backup.cancel` takes. */
10781
+ id: string(),
10782
+ state: BackupRunStateSchema,
10783
+ phase: BackupRunPhaseSchema,
10784
+ /**
10785
+ * Resolved destination location ids. Empty while queued (targets are
10786
+ * resolved when the run starts, against the then-current policies).
10787
+ */
10788
+ destinationIds: array(string()).readonly(),
10789
+ label: string().optional(),
10790
+ /** ms-epoch when the run was submitted (trigger call / schedule fire). */
10791
+ requestedAt: number(),
10792
+ /** ms-epoch when the run left the queue and started building. */
10793
+ startedAt: number().optional(),
10794
+ /** ms-epoch when the run reached a terminal state. */
10795
+ finishedAt: number().optional(),
10796
+ /** Compressed bytes of the staging archive written so far. */
10797
+ stagedBytes: number(),
10798
+ /** Final staged archive size, once the build phase completes. */
10799
+ archiveSizeBytes: number().optional(),
10800
+ /** Bytes pushed to the destination currently uploading. */
10801
+ uploadedBytes: number(),
10802
+ /** Destinations where the archive fully landed (uploaded + indexed). */
10803
+ completedDestinationIds: array(string()).readonly(),
10804
+ /** Destinations that failed during the fan-out. */
10805
+ failedDestinationIds: array(string()).readonly(),
10806
+ /** Failure message when `state === 'failed'`. */
10807
+ error: string().optional(),
10808
+ /**
10809
+ * 1-based place in the execution queue — 1 = runs next. Present only
10810
+ * while `state === 'queued'`. Stamped by the orchestrator from the
10811
+ * queue's OWN pending order, never derived from timestamps, so the
10812
+ * UI cannot show an order the executor will not honour.
10813
+ */
10814
+ queuePosition: number().int().min(1).optional()
10815
+ });
10816
+ /**
10817
+ * Result of `backup.trigger`. The call still resolves when the run
10818
+ * terminates (compat with schedule-driven runs and the admin UI), but
10819
+ * it now names the run and says whether it had to WAIT: a trigger that
10820
+ * arrives while another run is in flight is enqueued (or joined onto
10821
+ * an identical already-queued run), never started concurrently.
10822
+ */
10823
+ var BackupTriggerResultSchema = object({
10824
+ /** The run this trigger mapped to — poll it via `listRuns`, stop it via `cancel`. */
10825
+ runId: string(),
10826
+ /** True when the run waited behind an in-flight run instead of starting immediately. */
10827
+ queued: boolean(),
10828
+ /** True when this trigger was coalesced onto an identical already-queued run. */
10829
+ joined: boolean(),
10830
+ /** True when the run was cancelled before completing every destination. */
10831
+ cancelled: boolean(),
10832
+ /** One entry per destination the archive landed at (partial on cancel). */
10833
+ entries: array(BackupEntrySchema).readonly()
10834
+ });
10752
10835
  /**
10753
10836
  * A backup schedule — the N:M "entry" that binds one cron cadence to a
10754
10837
  * SET of destination locations. Supersedes the per-location cron on
@@ -10796,7 +10879,10 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10796
10879
  * retention (manual runs).
10797
10880
  */
10798
10881
  retentionCount: number().int().min(1).max(1e3).optional()
10799
- }).optional(), array(BackupEntrySchema).readonly(), {
10882
+ }).optional(), BackupTriggerResultSchema, {
10883
+ kind: "mutation",
10884
+ auth: "admin"
10885
+ }), method(_void(), array(BackupRunSchema).readonly(), { auth: "admin" }), method(object({ runId: string() }), object({ cancelled: boolean() }), {
10800
10886
  kind: "mutation",
10801
10887
  auth: "admin"
10802
10888
  }), method(_void(), array(BackupEntrySchema).readonly(), { auth: "admin" }), method(_void(), array(LocationStatSchema).readonly(), { auth: "admin" }), method(object({
@@ -11562,6 +11648,14 @@ method(object({
11562
11648
  }), object({ success: literal(true) }), {
11563
11649
  kind: "mutation",
11564
11650
  auth: "admin"
11651
+ }), method(object({ deviceId: number().int().nonnegative() }), object({
11652
+ derivedStreamsDeleted: array(string()).readonly(),
11653
+ assignmentsPurged: boolean(),
11654
+ probeSnapshotsDropped: number().int().nonnegative(),
11655
+ rtspTokenRowsDeleted: number().int().nonnegative()
11656
+ }), {
11657
+ kind: "mutation",
11658
+ auth: "admin"
11565
11659
  }), method(object({
11566
11660
  deviceId: number(),
11567
11661
  /** Absent = the LOWEST assigned profile — a notification attachment is
@@ -19571,7 +19665,20 @@ var RecentTracksQueryInput = object({
19571
19665
  projection: TrackProjectionSchema.optional(),
19572
19666
  /** Include stationary-promoted rows (parked objects). Default false: the
19573
19667
  * feed lists passages; parking records live on the stationary registry. */
19574
- includeStationary: boolean().optional()
19668
+ includeStationary: boolean().optional(),
19669
+ /**
19670
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
19671
+ *
19672
+ * The same filter `listTracks` takes, because the timeline's class chips must
19673
+ * mean the same thing whether the scope is one camera or twelve. Until this
19674
+ * existed the scoped feed downloaded a page and narrowed it on the phone
19675
+ * while the single-camera path narrowed the read — one filter, two costs.
19676
+ *
19677
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
19678
+ * whose class list is unreadable are kept, and the client's rule stays the
19679
+ * exact one.
19680
+ */
19681
+ classes: array(string()).optional()
19575
19682
  });
19576
19683
  var RecentTracksPageSchema = object({
19577
19684
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -23813,8 +23920,22 @@ var automationControlCapability = {
23813
23920
  * threshold.
23814
23921
  */
23815
23922
  var BatteryStatusSchema = object({
23816
- /** 0..100 inclusive. Firmware-reported. */
23817
- percentage: number().min(0).max(100),
23923
+ /**
23924
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
23925
+ * provider has registered the capability but no reading has landed.
23926
+ *
23927
+ * It is nullable because it was not, and the only value a provider could
23928
+ * seed with was `0`. A battery camera behind an NVR therefore announced
23929
+ * itself at 0% on every start and corrected itself a moment later, which is
23930
+ * indistinguishable from a real flat battery: it fires the low-battery alert
23931
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
23932
+ * reading the difference is an alarm.
23933
+ *
23934
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
23935
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
23936
+ * declines to band a non-finite reading, which is the correct shape.
23937
+ */
23938
+ percentage: number().min(0).max(100).nullable(),
23818
23939
  /**
23819
23940
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
23820
23941
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -33592,6 +33713,12 @@ Object.freeze({
33592
33713
  addonId: null,
33593
33714
  access: "create"
33594
33715
  },
33716
+ "backup.cancel": {
33717
+ capName: "backup",
33718
+ capScope: "system",
33719
+ addonId: null,
33720
+ access: "create"
33721
+ },
33595
33722
  "backup.delete": {
33596
33723
  capName: "backup",
33597
33724
  capScope: "system",
@@ -33634,6 +33761,12 @@ Object.freeze({
33634
33761
  addonId: null,
33635
33762
  access: "view"
33636
33763
  },
33764
+ "backup.listRuns": {
33765
+ capName: "backup",
33766
+ capScope: "system",
33767
+ addonId: null,
33768
+ access: "view"
33769
+ },
33637
33770
  "backup.listSchedules": {
33638
33771
  capName: "backup",
33639
33772
  capScope: "system",
@@ -38200,6 +38333,12 @@ Object.freeze({
38200
38333
  addonId: null,
38201
38334
  access: "create"
38202
38335
  },
38336
+ "streamBroker.forgetDeviceHardware": {
38337
+ capName: "stream-broker",
38338
+ capScope: "system",
38339
+ addonId: null,
38340
+ access: "delete"
38341
+ },
38203
38342
  "streamBroker.getAllRtspEntries": {
38204
38343
  capName: "stream-broker",
38205
38344
  capScope: "system",
@@ -40658,6 +40797,11 @@ Object.freeze({
40658
40797
  form: "single",
40659
40798
  optional: false
40660
40799
  }],
40800
+ "streamBroker.forgetDeviceHardware": [{
40801
+ name: "deviceId",
40802
+ form: "single",
40803
+ optional: false
40804
+ }],
40661
40805
  "streamBroker.getDeviceAudioMute": [{
40662
40806
  name: "deviceId",
40663
40807
  form: "single",
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  //#endregion
6
- const require_dist = require("../dist-VXqwogoH.js");
6
+ const require_dist = require("../dist-BiCYEtvX.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  //#region src/ha-export/topics.ts
9
9
  /**
@@ -4971,8 +4971,12 @@ function projectBatterySlice(deviceKey, slice) {
4971
4971
  * `binary: true` means the device reports "low" or "not low" and the
4972
4972
  * percentage is a stand-in. Publishing it would put a fabricated
4973
4973
  * number on a battery gauge, which is worse than an empty one.
4974
+ *
4975
+ * A `null` percentage is the same judgement for a different reason: the
4976
+ * level is NOT YET KNOWN. `String(null)` would publish the text "null" onto
4977
+ * that gauge, and coercing it to 0 would publish a flat battery.
4974
4978
  */
4975
- if (slice.binary !== true) values.push({
4979
+ if (slice.binary !== true && slice.percentage !== null) values.push({
4976
4980
  topic: stateTopic(deviceKey, "battery"),
4977
4981
  value: String(slice.percentage)
4978
4982
  });
@@ -1,4 +1,4 @@
1
- import { At as EventCategory, C as buildAddonRouteProvider, Et as string, N as deviceExportCapability, X as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, i as CAMERA_SWITCH_ORDER, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, pt as BaseAddon, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, vt as nodePin } from "../dist-CiUJy917.mjs";
1
+ import { At as EventCategory, C as buildAddonRouteProvider, Et as string, N as deviceExportCapability, X as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, i as CAMERA_SWITCH_ORDER, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, pt as BaseAddon, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, vt as nodePin } from "../dist-BQ3x3HG3.mjs";
2
2
  import { createHmac, timingSafeEqual } from "node:crypto";
3
3
  //#region src/ha-export/topics.ts
4
4
  /**
@@ -4966,8 +4966,12 @@ function projectBatterySlice(deviceKey, slice) {
4966
4966
  * `binary: true` means the device reports "low" or "not low" and the
4967
4967
  * percentage is a stand-in. Publishing it would put a fabricated
4968
4968
  * number on a battery gauge, which is worse than an empty one.
4969
+ *
4970
+ * A `null` percentage is the same judgement for a different reason: the
4971
+ * level is NOT YET KNOWN. `String(null)` would publish the text "null" onto
4972
+ * that gauge, and coercing it to 0 would publish a flat battery.
4969
4973
  */
4970
- if (slice.binary !== true) values.push({
4974
+ if (slice.binary !== true && slice.percentage !== null) values.push({
4971
4975
  topic: stateTopic(deviceKey, "battery"),
4972
4976
  value: String(slice.percentage)
4973
4977
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homeassistant",
3
- "version": "1.2.71",
3
+ "version": "1.2.73",
4
4
  "description": "Home Assistant device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",