@camstack/addon-provider-bosch 0.1.15 → 0.1.17

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.
@@ -1,4 +1,4 @@
1
- import { b as errMsg } from "./dist-hm2fhp2t.mjs";
1
+ import { b as errMsg } from "./dist-C8QaBSuL.mjs";
2
2
  //#region src/shared/oauth-callback.ts
3
3
  /** A code on its own: no scheme, no query, no separators. */
4
4
  var BARE_CODE = /^[A-Za-z0-9._~-]+$/;
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-ChJRdNGo.js");
1
+ const require_dist = require("./dist-D6D2JO8x.js");
2
2
  //#region src/shared/oauth-callback.ts
3
3
  /** A code on its own: no scheme, no query, no separators. */
4
4
  var BARE_CODE = /^[A-Za-z0-9._~-]+$/;
@@ -1,5 +1,5 @@
1
1
  import "node:crypto";
2
- //#region ../types/dist/event-category-7pZEBmVg.mjs
2
+ //#region ../types/dist/event-category-BfJwUd2S.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -316,6 +316,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
316
316
  */
317
317
  EventCategory["PipelineDetectionBlind"] = "pipeline.detection-blind";
318
318
  /**
319
+ * A camera's motion source has fired constantly with no subject for an hour
320
+ * (motion duty or session count over the bar, zero detections) — it is not
321
+ * blind, its motion zones or sensitivity want tuning (D907). Paired with
322
+ * {@link PipelineMotionCalm}.
323
+ */
324
+ EventCategory["PipelineMotionNoisy"] = "pipeline.motion-noisy";
325
+ /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
326
+ EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
327
+ /**
319
328
  * Per-camera pipeline config was mutated by the orchestrator
320
329
  * (3-level settings change via `setAgentAddonDefaults` /
321
330
  * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
@@ -5422,7 +5431,7 @@ var ZodIssueCode = {
5422
5431
  var ZodFirstPartyTypeKind;
5423
5432
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5424
5433
  //#endregion
5425
- //#region ../types/dist/sleep-CueztlR1.mjs
5434
+ //#region ../types/dist/sleep-DJFUfKvA.mjs
5426
5435
  /**
5427
5436
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5428
5437
  * window to float samples (D455).
@@ -7955,7 +7964,7 @@ var EntryDescriptorSchema = object({
7955
7964
  /** JSON Schema of the entry body; `secret: true` marks a password field. */
7956
7965
  fields: record(string(), unknown())
7957
7966
  }).strict();
7958
- /** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
7967
+ /** What crosses the wire from an addon: JSON only, no Zod. An unknown key is refused. */
7959
7968
  var RecordKindDeclarationSchema = object({
7960
7969
  database: _enum(RECORD_DATABASES).default("settings"),
7961
7970
  class: _enum(RECORD_CLASSES),
@@ -7974,14 +7983,6 @@ var RecordKindDeclarationSchema = object({
7974
7983
  secretPaths: array(string().min(1)).default([]),
7975
7984
  /** The id key of each array that carries a secret (`{ headers: 'name' }`). */
7976
7985
  secretArrayKeys: record(string(), string()).default({}),
7977
- /**
7978
- * RETIRED (D900): the legacy table a kind adopted from during the records
7979
- * migration. Nothing declares it any more and nothing reads it; the schema
7980
- * still ACCEPTS it because the catalog rows the migration wrote carry it
7981
- * (an orphaned kind is never redeclared) and an addon built before the
7982
- * removal still sends it. Never set it.
7983
- */
7984
- adoptsFrom: string().min(1).optional(),
7985
7986
  /** Kinds that must agree through `transact` sit in one file under one family name. */
7986
7987
  transactFamily: string().min(1).optional(),
7987
7988
  entry: EntryDescriptorSchema.optional()
@@ -12681,17 +12682,6 @@ object({
12681
12682
  createdAt: number(),
12682
12683
  updatedAt: number()
12683
12684
  });
12684
- object({
12685
- id: string(),
12686
- label: string(),
12687
- isAdmin: boolean().default(false),
12688
- allowedProviders: AllowedProviderSchema,
12689
- allowedDevices: AllowedDevicesSchema,
12690
- tokenHash: string(),
12691
- tokenPrefix: string(),
12692
- createdAt: number(),
12693
- lastUsedAt: number().optional()
12694
- });
12695
12685
  object({
12696
12686
  id: string(),
12697
12687
  userId: string(),
@@ -16673,16 +16663,36 @@ var RecordKindStateSchema = _enum([
16673
16663
  var DeclareKindResultSchema = object({
16674
16664
  kindId: number().int(),
16675
16665
  state: RecordKindStateSchema,
16676
- createdIndexes: array(string()),
16677
- /**
16678
- * RETIRED (D900): always `null`. It carried the records-migration boot guard;
16679
- * it stays on the wire, null, because an addon built before the removal reads
16680
- * an ABSENT field as "adoption pending" and refuses to start.
16681
- */
16682
- adoptionPending: _null()
16666
+ createdIndexes: array(string())
16683
16667
  });
16684
16668
  /** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
16685
16669
  var SecretRefsList = array(string());
16670
+ /**
16671
+ * One op of a `transact`: the single-row writes, each on a kind of the caller.
16672
+ * All ops of one call share ONE database file and ONE `transactFamily`.
16673
+ */
16674
+ var RecordTransactOpSchema = discriminatedUnion("op", [
16675
+ object({
16676
+ op: literal("put"),
16677
+ kind: RecordKindNameSchema,
16678
+ record: RecordInputSchema,
16679
+ ifRev: number().int().min(0).optional()
16680
+ }),
16681
+ object({
16682
+ op: literal("patch"),
16683
+ kind: RecordKindNameSchema,
16684
+ id: RecordIdSchema,
16685
+ data: RecordBodySchema,
16686
+ at: number().int().nullable().optional(),
16687
+ expiresAt: number().int().nullable().optional(),
16688
+ ifRev: number().int().min(1).optional()
16689
+ }),
16690
+ object({
16691
+ op: literal("delete"),
16692
+ kind: RecordKindNameSchema,
16693
+ id: RecordIdSchema
16694
+ })
16695
+ ]);
16686
16696
  var RecordSecretRefsSchema = record(string(), record(string(), string()));
16687
16697
  method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
16688
16698
  kind: "mutation",
@@ -16789,6 +16799,16 @@ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema)
16789
16799
  }), {
16790
16800
  kind: "mutation",
16791
16801
  auth: "admin"
16802
+ }), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
16803
+ results: array(object({
16804
+ rev: number().int().nullable(),
16805
+ deleted: boolean()
16806
+ })),
16807
+ releasedRefs: SecretRefsList,
16808
+ secretRefs: SecretRefsList
16809
+ }), {
16810
+ kind: "mutation",
16811
+ auth: "admin"
16792
16812
  }), method(object({
16793
16813
  deviceId: number().int(),
16794
16814
  kinds: array(RecordKindNameSchema).optional()
@@ -42400,9 +42420,13 @@ var NcSystemEventKindSchema = _enum([
42400
42420
  "node-updating",
42401
42421
  "node-inference-unavailable",
42402
42422
  "detection-blind",
42423
+ "motion-noisy",
42424
+ "motion-calm",
42403
42425
  "recording-stalled",
42404
42426
  "recording-healthy",
42405
42427
  "recording-degraded",
42428
+ "device-attention-required",
42429
+ "device-attention-resolved",
42406
42430
  "storage-location-low",
42407
42431
  "storage-location-critical",
42408
42432
  "storage-location-ok",
@@ -47963,7 +47987,6 @@ var RecommendationReasonSchema = discriminatedUnion("kind", [
47963
47987
  modelId: string()
47964
47988
  }),
47965
47989
  object({ kind: literal("cpu-device") }),
47966
- object({ kind: literal("coral-untouched") }),
47967
47990
  object({ kind: literal("device-disabled") }),
47968
47991
  object({ kind: literal("device-unavailable") }),
47969
47992
  object({ kind: literal("node-offline") }),
@@ -48061,11 +48084,6 @@ var AutoTuneWarningSchema = discriminatedUnion("kind", [
48061
48084
  kind: literal("detector-store-unreadable"),
48062
48085
  error: string()
48063
48086
  }),
48064
- object({
48065
- kind: literal("coral-enabled"),
48066
- nodeId: string(),
48067
- deviceKey: string()
48068
- }),
48069
48087
  object({
48070
48088
  kind: literal("custom-pin-kept"),
48071
48089
  nodeId: string(),
@@ -55734,6 +55752,16 @@ var DishwasherStatusSchema = object({
55734
55752
  powerState: string().nullable(),
55735
55753
  saltLevel: DishwasherConsumableLevelSchema.nullable(),
55736
55754
  rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
55755
+ /**
55756
+ * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
55757
+ * combined care-and-filter reminder raised); false when every such reminder reads clear; null
55758
+ * when none is reported. A cleared reminder is not an unknown one.
55759
+ */
55760
+ filterCleaningDue: boolean().nullable(),
55761
+ /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
55762
+ machineCareDue: boolean().nullable(),
55763
+ /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
55764
+ machineCareRemainingRuns: number().int().nonnegative().nullable(),
55737
55765
  /** Option states: null when the appliance does not expose the option or has not said. */
55738
55766
  halfLoad: boolean().nullable(),
55739
55767
  hygienePlus: boolean().nullable(),
@@ -64384,16 +64412,6 @@ var UpdateUserInputSchema = object({
64384
64412
  allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
64385
64413
  scopes: array(TokenScopeSchema).optional()
64386
64414
  });
64387
- var ApiKeySummarySchema = object({
64388
- id: string(),
64389
- label: string(),
64390
- isAdmin: boolean().default(false),
64391
- allowedProviders: union([array(string()), literal("*")]).optional(),
64392
- allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
64393
- tokenPrefix: string(),
64394
- createdAt: number(),
64395
- lastUsedAt: number().optional()
64396
- });
64397
64415
  var OauthSessionSummarySchema = object({
64398
64416
  id: string(),
64399
64417
  userId: string(),
@@ -64415,8 +64433,7 @@ var TotpStatusSchema = object({
64415
64433
  confirmedAt: number().nullable()
64416
64434
  });
64417
64435
  /**
64418
- * user-management — singleton capability for user accounts and the
64419
- * legacy API-key rows.
64436
+ * user-management — singleton capability for user accounts.
64420
64437
  *
64421
64438
  * v2 model: every method gated by `auth: 'admin'`. Admin-only delegation
64422
64439
  * — the subset check on `setUserScopes` is moot
@@ -64472,10 +64489,6 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
64472
64489
  }), UserSummarySchema.extend({ passwordHash: string() }).nullable(), {
64473
64490
  kind: "mutation",
64474
64491
  access: "view"
64475
- }), method(_void(), array(ApiKeySummarySchema), { auth: "admin" }), method(object({ id: string() }), object({ success: literal(true) }), {
64476
- kind: "mutation",
64477
- auth: "admin",
64478
- access: "delete"
64479
64492
  }), method(object({ userId: string() }), TotpSetupResultSchema, {
64480
64493
  kind: "mutation",
64481
64494
  auth: "admin",
@@ -73931,6 +73944,12 @@ Object.freeze({
73931
73944
  addonId: null,
73932
73945
  access: "view"
73933
73946
  },
73947
+ "records.transact": {
73948
+ capName: "records",
73949
+ capScope: "system",
73950
+ addonId: null,
73951
+ access: "create"
73952
+ },
73934
73953
  "sceneMonitor.captureReference": {
73935
73954
  capName: "scene-monitor",
73936
73955
  capScope: "device",
@@ -75263,12 +75282,6 @@ Object.freeze({
75263
75282
  addonId: null,
75264
75283
  access: "view"
75265
75284
  },
75266
- "userManagement.listApiKeys": {
75267
- capName: "user-management",
75268
- capScope: "system",
75269
- addonId: null,
75270
- access: "view"
75271
- },
75272
75285
  "userManagement.listExternalIdentities": {
75273
75286
  capName: "user-management",
75274
75287
  capScope: "system",
@@ -75323,12 +75336,6 @@ Object.freeze({
75323
75336
  addonId: null,
75324
75337
  access: "create"
75325
75338
  },
75326
- "userManagement.revokeApiKey": {
75327
- capName: "user-management",
75328
- capScope: "system",
75329
- addonId: null,
75330
- access: "delete"
75331
- },
75332
75339
  "userManagement.revokeOauthSession": {
75333
75340
  capName: "user-management",
75334
75341
  capScope: "system",
@@ -78693,9 +78700,13 @@ new Map(Object.entries({
78693
78700
  "stream-online": "device-events",
78694
78701
  "stream-offline": "device-events",
78695
78702
  "detection-blind": "device-events",
78703
+ "motion-noisy": "device-events",
78704
+ "motion-calm": "device-events",
78696
78705
  "recording-stalled": "device-events",
78697
78706
  "recording-healthy": "device-events",
78698
78707
  "recording-degraded": "device-events",
78708
+ "device-attention-required": "device-events",
78709
+ "device-attention-resolved": "device-events",
78699
78710
  "node-online": "system",
78700
78711
  "node-offline": "system",
78701
78712
  "node-restarting": "system",
@@ -78778,6 +78789,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
78778
78789
  label: "Detecting nothing",
78779
78790
  kinds: ["detection-blind"]
78780
78791
  },
78792
+ {
78793
+ id: "motion-noise",
78794
+ family: "device-events",
78795
+ label: "Motion without a subject",
78796
+ kinds: ["motion-noisy", "motion-calm"]
78797
+ },
78781
78798
  {
78782
78799
  id: "recording",
78783
78800
  family: "device-events",
@@ -78788,6 +78805,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
78788
78805
  "recording-degraded"
78789
78806
  ]
78790
78807
  },
78808
+ {
78809
+ id: "device-attention",
78810
+ family: "device-events",
78811
+ label: "Needs attention",
78812
+ kinds: ["device-attention-required", "device-attention-resolved"]
78813
+ },
78791
78814
  {
78792
78815
  id: "nodes",
78793
78816
  family: "system",
@@ -78927,6 +78950,7 @@ var ALERT_EPISODE = [
78927
78950
  "alert-error",
78928
78951
  "alert-critical-resolved"
78929
78952
  ];
78953
+ var DEVICE_ATTENTION = ["device-attention-required", "device-attention-resolved"];
78930
78954
  var UPDATES = [
78931
78955
  "addon-update-available",
78932
78956
  "server-update-available",
@@ -78956,9 +78980,12 @@ var DEVICE_EVENTS = [
78956
78980
  "stream-online",
78957
78981
  "stream-offline",
78958
78982
  "detection-blind",
78983
+ "motion-noisy",
78984
+ "motion-calm",
78959
78985
  "recording-stalled",
78960
78986
  "recording-healthy",
78961
78987
  "recording-degraded",
78988
+ ...DEVICE_ATTENTION,
78962
78989
  ...ALARM
78963
78990
  ];
78964
78991
  /** Every kind a summary may speak about — the two summarisable families. */
@@ -79126,7 +79153,9 @@ var STATE_SUMMARY_VARS = [
79126
79153
  [...RULE_TEXT],
79127
79154
  [...RULE_TEXT],
79128
79155
  [...RULE_TEXT],
79129
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79156
+ [...RULE_TEXT],
79157
+ [...RULE_TEXT],
79158
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79130
79159
  [...RULE_TEXT],
79131
79160
  [...RULE_TEXT],
79132
79161
  [...RULE_TEXT],
@@ -79148,11 +79177,11 @@ var STATE_SUMMARY_VARS = [
79148
79177
  [...RULE_TEXT],
79149
79178
  [...RULE_TEXT],
79150
79179
  [...RULE_TEXT],
79180
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79151
79181
  ([...RULE_TEXT], [...ALERT_EPISODE]),
79182
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79152
79183
  ([...RULE_TEXT], [...ALERT_EPISODE]),
79153
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79154
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79155
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79184
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79156
79185
  [...RULE_TEXT],
79157
79186
  [...RULE_TEXT],
79158
79187
  [...RULE_TEXT],
@@ -79228,6 +79257,15 @@ new Map([
79228
79257
  snapshotLabel: "Tanks low or full now",
79229
79258
  snapshotSection: "tanks"
79230
79259
  },
79260
+ {
79261
+ id: "motion-noise",
79262
+ family: "device-events",
79263
+ label: "Motion without a subject (constant / calm again)",
79264
+ bad: ["motion-noisy"],
79265
+ recovery: ["motion-calm"],
79266
+ snapshotLabel: "Cameras with constant motion now",
79267
+ snapshotSection: null
79268
+ },
79231
79269
  {
79232
79270
  id: "recording-health",
79233
79271
  family: "device-events",
@@ -79237,6 +79275,15 @@ new Map([
79237
79275
  snapshotLabel: "Recordings stalled now",
79238
79276
  snapshotSection: null
79239
79277
  },
79278
+ {
79279
+ id: "device-attention",
79280
+ family: "device-events",
79281
+ label: "Device needs attention (raised / resolved)",
79282
+ bad: ["device-attention-required"],
79283
+ recovery: ["device-attention-resolved"],
79284
+ snapshotLabel: "Devices needing attention now",
79285
+ snapshotSection: null
79286
+ },
79240
79287
  {
79241
79288
  id: "node-connectivity",
79242
79289
  family: "system",
@@ -1,5 +1,5 @@
1
1
  require("node:crypto");
2
- //#region ../types/dist/event-category-7pZEBmVg.mjs
2
+ //#region ../types/dist/event-category-BfJwUd2S.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -316,6 +316,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
316
316
  */
317
317
  EventCategory["PipelineDetectionBlind"] = "pipeline.detection-blind";
318
318
  /**
319
+ * A camera's motion source has fired constantly with no subject for an hour
320
+ * (motion duty or session count over the bar, zero detections) — it is not
321
+ * blind, its motion zones or sensitivity want tuning (D907). Paired with
322
+ * {@link PipelineMotionCalm}.
323
+ */
324
+ EventCategory["PipelineMotionNoisy"] = "pipeline.motion-noisy";
325
+ /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
326
+ EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
327
+ /**
319
328
  * Per-camera pipeline config was mutated by the orchestrator
320
329
  * (3-level settings change via `setAgentAddonDefaults` /
321
330
  * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
@@ -5422,7 +5431,7 @@ var ZodIssueCode = {
5422
5431
  var ZodFirstPartyTypeKind;
5423
5432
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5424
5433
  //#endregion
5425
- //#region ../types/dist/sleep-CueztlR1.mjs
5434
+ //#region ../types/dist/sleep-DJFUfKvA.mjs
5426
5435
  /**
5427
5436
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5428
5437
  * window to float samples (D455).
@@ -7955,7 +7964,7 @@ var EntryDescriptorSchema = object({
7955
7964
  /** JSON Schema of the entry body; `secret: true` marks a password field. */
7956
7965
  fields: record(string(), unknown())
7957
7966
  }).strict();
7958
- /** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
7967
+ /** What crosses the wire from an addon: JSON only, no Zod. An unknown key is refused. */
7959
7968
  var RecordKindDeclarationSchema = object({
7960
7969
  database: _enum(RECORD_DATABASES).default("settings"),
7961
7970
  class: _enum(RECORD_CLASSES),
@@ -7974,14 +7983,6 @@ var RecordKindDeclarationSchema = object({
7974
7983
  secretPaths: array(string().min(1)).default([]),
7975
7984
  /** The id key of each array that carries a secret (`{ headers: 'name' }`). */
7976
7985
  secretArrayKeys: record(string(), string()).default({}),
7977
- /**
7978
- * RETIRED (D900): the legacy table a kind adopted from during the records
7979
- * migration. Nothing declares it any more and nothing reads it; the schema
7980
- * still ACCEPTS it because the catalog rows the migration wrote carry it
7981
- * (an orphaned kind is never redeclared) and an addon built before the
7982
- * removal still sends it. Never set it.
7983
- */
7984
- adoptsFrom: string().min(1).optional(),
7985
7986
  /** Kinds that must agree through `transact` sit in one file under one family name. */
7986
7987
  transactFamily: string().min(1).optional(),
7987
7988
  entry: EntryDescriptorSchema.optional()
@@ -12681,17 +12682,6 @@ object({
12681
12682
  createdAt: number(),
12682
12683
  updatedAt: number()
12683
12684
  });
12684
- object({
12685
- id: string(),
12686
- label: string(),
12687
- isAdmin: boolean().default(false),
12688
- allowedProviders: AllowedProviderSchema,
12689
- allowedDevices: AllowedDevicesSchema,
12690
- tokenHash: string(),
12691
- tokenPrefix: string(),
12692
- createdAt: number(),
12693
- lastUsedAt: number().optional()
12694
- });
12695
12685
  object({
12696
12686
  id: string(),
12697
12687
  userId: string(),
@@ -16673,16 +16663,36 @@ var RecordKindStateSchema = _enum([
16673
16663
  var DeclareKindResultSchema = object({
16674
16664
  kindId: number().int(),
16675
16665
  state: RecordKindStateSchema,
16676
- createdIndexes: array(string()),
16677
- /**
16678
- * RETIRED (D900): always `null`. It carried the records-migration boot guard;
16679
- * it stays on the wire, null, because an addon built before the removal reads
16680
- * an ABSENT field as "adoption pending" and refuses to start.
16681
- */
16682
- adoptionPending: _null()
16666
+ createdIndexes: array(string())
16683
16667
  });
16684
16668
  /** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
16685
16669
  var SecretRefsList = array(string());
16670
+ /**
16671
+ * One op of a `transact`: the single-row writes, each on a kind of the caller.
16672
+ * All ops of one call share ONE database file and ONE `transactFamily`.
16673
+ */
16674
+ var RecordTransactOpSchema = discriminatedUnion("op", [
16675
+ object({
16676
+ op: literal("put"),
16677
+ kind: RecordKindNameSchema,
16678
+ record: RecordInputSchema,
16679
+ ifRev: number().int().min(0).optional()
16680
+ }),
16681
+ object({
16682
+ op: literal("patch"),
16683
+ kind: RecordKindNameSchema,
16684
+ id: RecordIdSchema,
16685
+ data: RecordBodySchema,
16686
+ at: number().int().nullable().optional(),
16687
+ expiresAt: number().int().nullable().optional(),
16688
+ ifRev: number().int().min(1).optional()
16689
+ }),
16690
+ object({
16691
+ op: literal("delete"),
16692
+ kind: RecordKindNameSchema,
16693
+ id: RecordIdSchema
16694
+ })
16695
+ ]);
16686
16696
  var RecordSecretRefsSchema = record(string(), record(string(), string()));
16687
16697
  method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
16688
16698
  kind: "mutation",
@@ -16789,6 +16799,16 @@ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema)
16789
16799
  }), {
16790
16800
  kind: "mutation",
16791
16801
  auth: "admin"
16802
+ }), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
16803
+ results: array(object({
16804
+ rev: number().int().nullable(),
16805
+ deleted: boolean()
16806
+ })),
16807
+ releasedRefs: SecretRefsList,
16808
+ secretRefs: SecretRefsList
16809
+ }), {
16810
+ kind: "mutation",
16811
+ auth: "admin"
16792
16812
  }), method(object({
16793
16813
  deviceId: number().int(),
16794
16814
  kinds: array(RecordKindNameSchema).optional()
@@ -42400,9 +42420,13 @@ var NcSystemEventKindSchema = _enum([
42400
42420
  "node-updating",
42401
42421
  "node-inference-unavailable",
42402
42422
  "detection-blind",
42423
+ "motion-noisy",
42424
+ "motion-calm",
42403
42425
  "recording-stalled",
42404
42426
  "recording-healthy",
42405
42427
  "recording-degraded",
42428
+ "device-attention-required",
42429
+ "device-attention-resolved",
42406
42430
  "storage-location-low",
42407
42431
  "storage-location-critical",
42408
42432
  "storage-location-ok",
@@ -47963,7 +47987,6 @@ var RecommendationReasonSchema = discriminatedUnion("kind", [
47963
47987
  modelId: string()
47964
47988
  }),
47965
47989
  object({ kind: literal("cpu-device") }),
47966
- object({ kind: literal("coral-untouched") }),
47967
47990
  object({ kind: literal("device-disabled") }),
47968
47991
  object({ kind: literal("device-unavailable") }),
47969
47992
  object({ kind: literal("node-offline") }),
@@ -48061,11 +48084,6 @@ var AutoTuneWarningSchema = discriminatedUnion("kind", [
48061
48084
  kind: literal("detector-store-unreadable"),
48062
48085
  error: string()
48063
48086
  }),
48064
- object({
48065
- kind: literal("coral-enabled"),
48066
- nodeId: string(),
48067
- deviceKey: string()
48068
- }),
48069
48087
  object({
48070
48088
  kind: literal("custom-pin-kept"),
48071
48089
  nodeId: string(),
@@ -55734,6 +55752,16 @@ var DishwasherStatusSchema = object({
55734
55752
  powerState: string().nullable(),
55735
55753
  saltLevel: DishwasherConsumableLevelSchema.nullable(),
55736
55754
  rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
55755
+ /**
55756
+ * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
55757
+ * combined care-and-filter reminder raised); false when every such reminder reads clear; null
55758
+ * when none is reported. A cleared reminder is not an unknown one.
55759
+ */
55760
+ filterCleaningDue: boolean().nullable(),
55761
+ /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
55762
+ machineCareDue: boolean().nullable(),
55763
+ /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
55764
+ machineCareRemainingRuns: number().int().nonnegative().nullable(),
55737
55765
  /** Option states: null when the appliance does not expose the option or has not said. */
55738
55766
  halfLoad: boolean().nullable(),
55739
55767
  hygienePlus: boolean().nullable(),
@@ -64384,16 +64412,6 @@ var UpdateUserInputSchema = object({
64384
64412
  allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
64385
64413
  scopes: array(TokenScopeSchema).optional()
64386
64414
  });
64387
- var ApiKeySummarySchema = object({
64388
- id: string(),
64389
- label: string(),
64390
- isAdmin: boolean().default(false),
64391
- allowedProviders: union([array(string()), literal("*")]).optional(),
64392
- allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
64393
- tokenPrefix: string(),
64394
- createdAt: number(),
64395
- lastUsedAt: number().optional()
64396
- });
64397
64415
  var OauthSessionSummarySchema = object({
64398
64416
  id: string(),
64399
64417
  userId: string(),
@@ -64415,8 +64433,7 @@ var TotpStatusSchema = object({
64415
64433
  confirmedAt: number().nullable()
64416
64434
  });
64417
64435
  /**
64418
- * user-management — singleton capability for user accounts and the
64419
- * legacy API-key rows.
64436
+ * user-management — singleton capability for user accounts.
64420
64437
  *
64421
64438
  * v2 model: every method gated by `auth: 'admin'`. Admin-only delegation
64422
64439
  * — the subset check on `setUserScopes` is moot
@@ -64472,10 +64489,6 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
64472
64489
  }), UserSummarySchema.extend({ passwordHash: string() }).nullable(), {
64473
64490
  kind: "mutation",
64474
64491
  access: "view"
64475
- }), method(_void(), array(ApiKeySummarySchema), { auth: "admin" }), method(object({ id: string() }), object({ success: literal(true) }), {
64476
- kind: "mutation",
64477
- auth: "admin",
64478
- access: "delete"
64479
64492
  }), method(object({ userId: string() }), TotpSetupResultSchema, {
64480
64493
  kind: "mutation",
64481
64494
  auth: "admin",
@@ -73931,6 +73944,12 @@ Object.freeze({
73931
73944
  addonId: null,
73932
73945
  access: "view"
73933
73946
  },
73947
+ "records.transact": {
73948
+ capName: "records",
73949
+ capScope: "system",
73950
+ addonId: null,
73951
+ access: "create"
73952
+ },
73934
73953
  "sceneMonitor.captureReference": {
73935
73954
  capName: "scene-monitor",
73936
73955
  capScope: "device",
@@ -75263,12 +75282,6 @@ Object.freeze({
75263
75282
  addonId: null,
75264
75283
  access: "view"
75265
75284
  },
75266
- "userManagement.listApiKeys": {
75267
- capName: "user-management",
75268
- capScope: "system",
75269
- addonId: null,
75270
- access: "view"
75271
- },
75272
75285
  "userManagement.listExternalIdentities": {
75273
75286
  capName: "user-management",
75274
75287
  capScope: "system",
@@ -75323,12 +75336,6 @@ Object.freeze({
75323
75336
  addonId: null,
75324
75337
  access: "create"
75325
75338
  },
75326
- "userManagement.revokeApiKey": {
75327
- capName: "user-management",
75328
- capScope: "system",
75329
- addonId: null,
75330
- access: "delete"
75331
- },
75332
75339
  "userManagement.revokeOauthSession": {
75333
75340
  capName: "user-management",
75334
75341
  capScope: "system",
@@ -78693,9 +78700,13 @@ new Map(Object.entries({
78693
78700
  "stream-online": "device-events",
78694
78701
  "stream-offline": "device-events",
78695
78702
  "detection-blind": "device-events",
78703
+ "motion-noisy": "device-events",
78704
+ "motion-calm": "device-events",
78696
78705
  "recording-stalled": "device-events",
78697
78706
  "recording-healthy": "device-events",
78698
78707
  "recording-degraded": "device-events",
78708
+ "device-attention-required": "device-events",
78709
+ "device-attention-resolved": "device-events",
78699
78710
  "node-online": "system",
78700
78711
  "node-offline": "system",
78701
78712
  "node-restarting": "system",
@@ -78778,6 +78789,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
78778
78789
  label: "Detecting nothing",
78779
78790
  kinds: ["detection-blind"]
78780
78791
  },
78792
+ {
78793
+ id: "motion-noise",
78794
+ family: "device-events",
78795
+ label: "Motion without a subject",
78796
+ kinds: ["motion-noisy", "motion-calm"]
78797
+ },
78781
78798
  {
78782
78799
  id: "recording",
78783
78800
  family: "device-events",
@@ -78788,6 +78805,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
78788
78805
  "recording-degraded"
78789
78806
  ]
78790
78807
  },
78808
+ {
78809
+ id: "device-attention",
78810
+ family: "device-events",
78811
+ label: "Needs attention",
78812
+ kinds: ["device-attention-required", "device-attention-resolved"]
78813
+ },
78791
78814
  {
78792
78815
  id: "nodes",
78793
78816
  family: "system",
@@ -78927,6 +78950,7 @@ var ALERT_EPISODE = [
78927
78950
  "alert-error",
78928
78951
  "alert-critical-resolved"
78929
78952
  ];
78953
+ var DEVICE_ATTENTION = ["device-attention-required", "device-attention-resolved"];
78930
78954
  var UPDATES = [
78931
78955
  "addon-update-available",
78932
78956
  "server-update-available",
@@ -78956,9 +78980,12 @@ var DEVICE_EVENTS = [
78956
78980
  "stream-online",
78957
78981
  "stream-offline",
78958
78982
  "detection-blind",
78983
+ "motion-noisy",
78984
+ "motion-calm",
78959
78985
  "recording-stalled",
78960
78986
  "recording-healthy",
78961
78987
  "recording-degraded",
78988
+ ...DEVICE_ATTENTION,
78962
78989
  ...ALARM
78963
78990
  ];
78964
78991
  /** Every kind a summary may speak about — the two summarisable families. */
@@ -79126,7 +79153,9 @@ var STATE_SUMMARY_VARS = [
79126
79153
  [...RULE_TEXT],
79127
79154
  [...RULE_TEXT],
79128
79155
  [...RULE_TEXT],
79129
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79156
+ [...RULE_TEXT],
79157
+ [...RULE_TEXT],
79158
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79130
79159
  [...RULE_TEXT],
79131
79160
  [...RULE_TEXT],
79132
79161
  [...RULE_TEXT],
@@ -79148,11 +79177,11 @@ var STATE_SUMMARY_VARS = [
79148
79177
  [...RULE_TEXT],
79149
79178
  [...RULE_TEXT],
79150
79179
  [...RULE_TEXT],
79180
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79151
79181
  ([...RULE_TEXT], [...ALERT_EPISODE]),
79182
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79152
79183
  ([...RULE_TEXT], [...ALERT_EPISODE]),
79153
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79154
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79155
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79184
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79156
79185
  [...RULE_TEXT],
79157
79186
  [...RULE_TEXT],
79158
79187
  [...RULE_TEXT],
@@ -79228,6 +79257,15 @@ new Map([
79228
79257
  snapshotLabel: "Tanks low or full now",
79229
79258
  snapshotSection: "tanks"
79230
79259
  },
79260
+ {
79261
+ id: "motion-noise",
79262
+ family: "device-events",
79263
+ label: "Motion without a subject (constant / calm again)",
79264
+ bad: ["motion-noisy"],
79265
+ recovery: ["motion-calm"],
79266
+ snapshotLabel: "Cameras with constant motion now",
79267
+ snapshotSection: null
79268
+ },
79231
79269
  {
79232
79270
  id: "recording-health",
79233
79271
  family: "device-events",
@@ -79237,6 +79275,15 @@ new Map([
79237
79275
  snapshotLabel: "Recordings stalled now",
79238
79276
  snapshotSection: null
79239
79277
  },
79278
+ {
79279
+ id: "device-attention",
79280
+ family: "device-events",
79281
+ label: "Device needs attention (raised / resolved)",
79282
+ bad: ["device-attention-required"],
79283
+ recovery: ["device-attention-resolved"],
79284
+ snapshotLabel: "Devices needing attention now",
79285
+ snapshotSection: null
79286
+ },
79240
79287
  {
79241
79288
  id: "node-connectivity",
79242
79289
  family: "system",
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-ChJRdNGo.js");
3
- const require_account_integration_offline = require("../account-integration-offline-Bf6PYmtk.js");
2
+ const require_dist = require("../dist-D6D2JO8x.js");
3
+ const require_account_integration_offline = require("../account-integration-offline-pY2Ojv8k.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_tls = require("node:tls");
6
6
  let node_zlib = require("node:zlib");
@@ -1400,7 +1400,7 @@ function buildHomeConnectFormSchema() {
1400
1400
  id: "gateway",
1401
1401
  title: "Home Connect",
1402
1402
  titleKey: "provider-bosch:home-connect.s_gateway.title",
1403
- description: "Create the login link and open it in a browser, then sign in. The browser cannot open the app and stops on an address starting with hcauth://auth/prod?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. The appliances are then downloaded and spoken to on the local network.",
1403
+ description: "Open the login link, sign in, then paste the address the browser stops on (hcauth://auth/prod?code=…). Chrome: DevTools > Network > last redirect > Location.",
1404
1404
  descriptionKey: "provider-bosch:home-connect.s_gateway.description",
1405
1405
  columns: 1,
1406
1406
  fields: [
@@ -3785,6 +3785,16 @@ function consumableFromEvents(view, nearlyEmpty, empty) {
3785
3785
  if (gone.includes(false) || nearly.includes(false)) return "ok";
3786
3786
  return null;
3787
3787
  }
3788
+ /** Any listed reminder raised → `true`; else any reading clear → `false`; none reported → `null`. */
3789
+ function reminderDue(view, shortNames) {
3790
+ const states = eventStates(view, shortNames);
3791
+ if (states.includes(true)) return true;
3792
+ return states.includes(false) ? false : null;
3793
+ }
3794
+ /** A non-negative whole count, or `null`. */
3795
+ function countOf(raw) {
3796
+ return typeof raw === "number" && Number.isInteger(raw) && raw >= 0 ? raw : null;
3797
+ }
3788
3798
  function programKey(raw) {
3789
3799
  return stringOf(raw);
3790
3800
  }
@@ -3818,6 +3828,9 @@ var COLD_DISHWASHER_STATUS = {
3818
3828
  powerState: null,
3819
3829
  saltLevel: null,
3820
3830
  rinseAidLevel: null,
3831
+ filterCleaningDue: null,
3832
+ machineCareDue: null,
3833
+ machineCareRemainingRuns: null,
3821
3834
  halfLoad: null,
3822
3835
  hygienePlus: null,
3823
3836
  extraDry: null,
@@ -3874,6 +3887,13 @@ function dishwasherStatusFromFeatures(input) {
3874
3887
  powerState: powerRaw,
3875
3888
  saltLevel: consumableFromEvents(input, ["SaltNearlyEmpty"], ["SaltEmpty", "SaltLack"]),
3876
3889
  rinseAidLevel: consumableFromEvents(input, ["RinseAidNearlyEmpty"], ["RinseAidEmpty", "RinseAidLack"]),
3890
+ filterCleaningDue: reminderDue(input, [
3891
+ "SmartFilterCleaningReminder",
3892
+ "CheckFilterSystem",
3893
+ "MachineCareAndFilterCleaningReminder"
3894
+ ]),
3895
+ machineCareDue: reminderDue(input, ["MachineCareReminder", "MachineCareAndFilterCleaningReminder"]),
3896
+ machineCareRemainingRuns: countOf(valueOf(input, findEntity(description, ["status"], ["RemainingProgramRuns"]))),
3877
3897
  halfLoad: optionBool(input, ["HalfLoad"]),
3878
3898
  hygienePlus: optionBool(input, ["HygienePlus"]),
3879
3899
  extraDry: optionBool(input, ["ExtraDry"]),
@@ -4219,7 +4239,7 @@ var LEGACY_CHILD_KEYS = {
4219
4239
  role: require_dist.DeviceRole.NumericSensor
4220
4240
  },
4221
4241
  "remote-control-level": {
4222
- key: "BSH.Common.Status.RemoteControlLevel",
4242
+ key: "BSH.Common.Setting.RemoteControlLevel",
4223
4243
  cap: "enum-sensor",
4224
4244
  role: require_dist.DeviceRole.EnumSensor
4225
4245
  },
@@ -4245,6 +4265,28 @@ var LEGACY_CHILD_KEYS = {
4245
4265
  }
4246
4266
  };
4247
4267
  /**
4268
+ * Keys the old fixed catalog guessed WRONG and persisted, mapped to the key the appliance really
4269
+ * lists. The remote-control level is a SETTING; the catalog filed it as a status, so a container
4270
+ * saved then carries `remote-control-level` on a key no description has, next to the derived
4271
+ * child the real key produced — one label, two devices.
4272
+ */
4273
+ var SUPERSEDED_LEGACY_KEYS = { "BSH.Common.Status.RemoteControlLevel": "BSH.Common.Setting.RemoteControlLevel" };
4274
+ /**
4275
+ * A persisted binding with a superseded legacy key re-pointed at the real one (same suffix, so
4276
+ * the same device); every other binding unchanged.
4277
+ */
4278
+ function withCurrentLegacyKey(binding) {
4279
+ const current = SUPERSEDED_LEGACY_KEYS[binding.key];
4280
+ return current === void 0 ? binding : {
4281
+ ...binding,
4282
+ key: current
4283
+ };
4284
+ }
4285
+ /** Whether a suffix is the one a key would derive — i.e. not an identity inherited from the old catalog. */
4286
+ function isDerivedSuffix(binding) {
4287
+ return binding.stableIdSuffix === slugFeatureKey(binding.key);
4288
+ }
4289
+ /**
4248
4290
  * The bindings that keep a container's EXISTING children alive until its description has been
4249
4291
  * read once: a container saved with the old fixed `children` list has no `entities` yet, and a
4250
4292
  * spec without those children would make the kernel delete them (removal by absence) before any
@@ -4288,6 +4330,20 @@ function keysMissingFromDescription(persisted, description) {
4288
4330
  return new Set(persisted.filter((b) => !isSyntheticKey(b.key) && description.entityByName(b.key) === void 0).map((b) => b.key));
4289
4331
  }
4290
4332
  /**
4333
+ * One row per Home Connect key. Two rows on one key are one entity filed twice (an identity from
4334
+ * the old catalog and the one derivation gave it): the older identity — the suffix that is NOT
4335
+ * the key's own slug — is the device the operator already has, so it stays and the twin leaves
4336
+ * (the kernel removes a child its spec no longer lists).
4337
+ */
4338
+ function oneRowPerKey(rows) {
4339
+ const byKey = /* @__PURE__ */ new Map();
4340
+ for (const row of rows) {
4341
+ const held = byKey.get(row.key);
4342
+ if (held === void 0 || isDerivedSuffix(held) && !isDerivedSuffix(row)) byKey.set(row.key, row);
4343
+ }
4344
+ return [...byKey.values()];
4345
+ }
4346
+ /**
4291
4347
  * The bindings a container should carry: what the description derives now, plus what it
4292
4348
  * already had. A child leaves only when its entity is missing from TWO agreeing reads (D49):
4293
4349
  * `missingBefore` is what the previous read lacked — one description that omits an entity is
@@ -4296,10 +4352,11 @@ function keysMissingFromDescription(persisted, description) {
4296
4352
  * derived model (legacy children) keeps its device ids instead of being rebuilt.
4297
4353
  */
4298
4354
  function mergeChildBindings(persisted, derived, description, missingBefore = /* @__PURE__ */ new Set()) {
4299
- const missingNow = keysMissingFromDescription(persisted, description);
4355
+ const rows = oneRowPerKey(persisted.map(withCurrentLegacyKey));
4356
+ const missingNow = keysMissingFromDescription(rows, description);
4300
4357
  const out = /* @__PURE__ */ new Map();
4301
4358
  const suffixByKey = /* @__PURE__ */ new Map();
4302
- for (const kept of persisted) {
4359
+ for (const kept of rows) {
4303
4360
  if (missingNow.has(kept.key) && missingBefore.has(kept.key)) continue;
4304
4361
  out.set(kept.stableIdSuffix, kept);
4305
4362
  suffixByKey.set(kept.key, kept);
@@ -1,5 +1,5 @@
1
- import { A as object, C as defineCustomActions, D as array, E as _enum, F as EventCategory, M as string, N as union, O as literal, P as unknown, S as customAction, T as isSecretMarkerLike, a as binaryCapability, b as errMsg, c as deviceAdoptionCapability, d as eventEmitterCapability, j as record, l as dishwasherCapability, m as powerMeterCapability, n as BaseDeviceProvider, p as numericSensorCapability, r as DishwasherOperationStateSchema, s as connectionTestCapability, t as BaseDevice, u as enumSensorCapability, v as DeviceRole, w as isSecretMarker, x as createEvent, y as DeviceType } from "../dist-hm2fhp2t.mjs";
2
- import { n as cascadeRemoveDevicesForIntegration, r as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-jlQzb2JO.mjs";
1
+ import { A as object, C as defineCustomActions, D as array, E as _enum, F as EventCategory, M as string, N as union, O as literal, P as unknown, S as customAction, T as isSecretMarkerLike, a as binaryCapability, b as errMsg, c as deviceAdoptionCapability, d as eventEmitterCapability, j as record, l as dishwasherCapability, m as powerMeterCapability, n as BaseDeviceProvider, p as numericSensorCapability, r as DishwasherOperationStateSchema, s as connectionTestCapability, t as BaseDevice, u as enumSensorCapability, v as DeviceRole, w as isSecretMarker, x as createEvent, y as DeviceType } from "../dist-C8QaBSuL.mjs";
2
+ import { n as cascadeRemoveDevicesForIntegration, r as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-W-dxoTT1.mjs";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  import { connect } from "node:tls";
5
5
  import { inflateRawSync } from "node:zlib";
@@ -1399,7 +1399,7 @@ function buildHomeConnectFormSchema() {
1399
1399
  id: "gateway",
1400
1400
  title: "Home Connect",
1401
1401
  titleKey: "provider-bosch:home-connect.s_gateway.title",
1402
- description: "Create the login link and open it in a browser, then sign in. The browser cannot open the app and stops on an address starting with hcauth://auth/prod?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. The appliances are then downloaded and spoken to on the local network.",
1402
+ description: "Open the login link, sign in, then paste the address the browser stops on (hcauth://auth/prod?code=…). Chrome: DevTools > Network > last redirect > Location.",
1403
1403
  descriptionKey: "provider-bosch:home-connect.s_gateway.description",
1404
1404
  columns: 1,
1405
1405
  fields: [
@@ -3784,6 +3784,16 @@ function consumableFromEvents(view, nearlyEmpty, empty) {
3784
3784
  if (gone.includes(false) || nearly.includes(false)) return "ok";
3785
3785
  return null;
3786
3786
  }
3787
+ /** Any listed reminder raised → `true`; else any reading clear → `false`; none reported → `null`. */
3788
+ function reminderDue(view, shortNames) {
3789
+ const states = eventStates(view, shortNames);
3790
+ if (states.includes(true)) return true;
3791
+ return states.includes(false) ? false : null;
3792
+ }
3793
+ /** A non-negative whole count, or `null`. */
3794
+ function countOf(raw) {
3795
+ return typeof raw === "number" && Number.isInteger(raw) && raw >= 0 ? raw : null;
3796
+ }
3787
3797
  function programKey(raw) {
3788
3798
  return stringOf(raw);
3789
3799
  }
@@ -3817,6 +3827,9 @@ var COLD_DISHWASHER_STATUS = {
3817
3827
  powerState: null,
3818
3828
  saltLevel: null,
3819
3829
  rinseAidLevel: null,
3830
+ filterCleaningDue: null,
3831
+ machineCareDue: null,
3832
+ machineCareRemainingRuns: null,
3820
3833
  halfLoad: null,
3821
3834
  hygienePlus: null,
3822
3835
  extraDry: null,
@@ -3873,6 +3886,13 @@ function dishwasherStatusFromFeatures(input) {
3873
3886
  powerState: powerRaw,
3874
3887
  saltLevel: consumableFromEvents(input, ["SaltNearlyEmpty"], ["SaltEmpty", "SaltLack"]),
3875
3888
  rinseAidLevel: consumableFromEvents(input, ["RinseAidNearlyEmpty"], ["RinseAidEmpty", "RinseAidLack"]),
3889
+ filterCleaningDue: reminderDue(input, [
3890
+ "SmartFilterCleaningReminder",
3891
+ "CheckFilterSystem",
3892
+ "MachineCareAndFilterCleaningReminder"
3893
+ ]),
3894
+ machineCareDue: reminderDue(input, ["MachineCareReminder", "MachineCareAndFilterCleaningReminder"]),
3895
+ machineCareRemainingRuns: countOf(valueOf(input, findEntity(description, ["status"], ["RemainingProgramRuns"]))),
3876
3896
  halfLoad: optionBool(input, ["HalfLoad"]),
3877
3897
  hygienePlus: optionBool(input, ["HygienePlus"]),
3878
3898
  extraDry: optionBool(input, ["ExtraDry"]),
@@ -4218,7 +4238,7 @@ var LEGACY_CHILD_KEYS = {
4218
4238
  role: DeviceRole.NumericSensor
4219
4239
  },
4220
4240
  "remote-control-level": {
4221
- key: "BSH.Common.Status.RemoteControlLevel",
4241
+ key: "BSH.Common.Setting.RemoteControlLevel",
4222
4242
  cap: "enum-sensor",
4223
4243
  role: DeviceRole.EnumSensor
4224
4244
  },
@@ -4244,6 +4264,28 @@ var LEGACY_CHILD_KEYS = {
4244
4264
  }
4245
4265
  };
4246
4266
  /**
4267
+ * Keys the old fixed catalog guessed WRONG and persisted, mapped to the key the appliance really
4268
+ * lists. The remote-control level is a SETTING; the catalog filed it as a status, so a container
4269
+ * saved then carries `remote-control-level` on a key no description has, next to the derived
4270
+ * child the real key produced — one label, two devices.
4271
+ */
4272
+ var SUPERSEDED_LEGACY_KEYS = { "BSH.Common.Status.RemoteControlLevel": "BSH.Common.Setting.RemoteControlLevel" };
4273
+ /**
4274
+ * A persisted binding with a superseded legacy key re-pointed at the real one (same suffix, so
4275
+ * the same device); every other binding unchanged.
4276
+ */
4277
+ function withCurrentLegacyKey(binding) {
4278
+ const current = SUPERSEDED_LEGACY_KEYS[binding.key];
4279
+ return current === void 0 ? binding : {
4280
+ ...binding,
4281
+ key: current
4282
+ };
4283
+ }
4284
+ /** Whether a suffix is the one a key would derive — i.e. not an identity inherited from the old catalog. */
4285
+ function isDerivedSuffix(binding) {
4286
+ return binding.stableIdSuffix === slugFeatureKey(binding.key);
4287
+ }
4288
+ /**
4247
4289
  * The bindings that keep a container's EXISTING children alive until its description has been
4248
4290
  * read once: a container saved with the old fixed `children` list has no `entities` yet, and a
4249
4291
  * spec without those children would make the kernel delete them (removal by absence) before any
@@ -4287,6 +4329,20 @@ function keysMissingFromDescription(persisted, description) {
4287
4329
  return new Set(persisted.filter((b) => !isSyntheticKey(b.key) && description.entityByName(b.key) === void 0).map((b) => b.key));
4288
4330
  }
4289
4331
  /**
4332
+ * One row per Home Connect key. Two rows on one key are one entity filed twice (an identity from
4333
+ * the old catalog and the one derivation gave it): the older identity — the suffix that is NOT
4334
+ * the key's own slug — is the device the operator already has, so it stays and the twin leaves
4335
+ * (the kernel removes a child its spec no longer lists).
4336
+ */
4337
+ function oneRowPerKey(rows) {
4338
+ const byKey = /* @__PURE__ */ new Map();
4339
+ for (const row of rows) {
4340
+ const held = byKey.get(row.key);
4341
+ if (held === void 0 || isDerivedSuffix(held) && !isDerivedSuffix(row)) byKey.set(row.key, row);
4342
+ }
4343
+ return [...byKey.values()];
4344
+ }
4345
+ /**
4290
4346
  * The bindings a container should carry: what the description derives now, plus what it
4291
4347
  * already had. A child leaves only when its entity is missing from TWO agreeing reads (D49):
4292
4348
  * `missingBefore` is what the previous read lacked — one description that omits an entity is
@@ -4295,10 +4351,11 @@ function keysMissingFromDescription(persisted, description) {
4295
4351
  * derived model (legacy children) keeps its device ids instead of being rebuilt.
4296
4352
  */
4297
4353
  function mergeChildBindings(persisted, derived, description, missingBefore = /* @__PURE__ */ new Set()) {
4298
- const missingNow = keysMissingFromDescription(persisted, description);
4354
+ const rows = oneRowPerKey(persisted.map(withCurrentLegacyKey));
4355
+ const missingNow = keysMissingFromDescription(rows, description);
4299
4356
  const out = /* @__PURE__ */ new Map();
4300
4357
  const suffixByKey = /* @__PURE__ */ new Map();
4301
- for (const kept of persisted) {
4358
+ for (const kept of rows) {
4302
4359
  if (missingNow.has(kept.key) && missingBefore.has(kept.key)) continue;
4303
4360
  out.set(kept.stableIdSuffix, kept);
4304
4361
  suffixByKey.set(kept.key, kept);
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-ChJRdNGo.js");
3
- const require_account_integration_offline = require("../account-integration-offline-Bf6PYmtk.js");
2
+ const require_dist = require("../dist-D6D2JO8x.js");
3
+ const require_account_integration_offline = require("../account-integration-offline-pY2Ojv8k.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  //#region src/homecom/custom-actions.ts
6
6
  var homeComActions = require_dist.defineCustomActions({
@@ -187,7 +187,7 @@ function buildHomeComFormSchema() {
187
187
  id: "account",
188
188
  title: "HomeCom account",
189
189
  titleKey: "provider-bosch:homecom.s_account.title",
190
- description: "Create the login link and open it in a browser, then sign in with your SingleKey ID. The browser cannot open the app and stops on an address starting with com.bosch.tt.dashtt.pointt://app/login?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. Tokens can also be pasted by hand in the fields below.",
190
+ description: "Sign in with your SingleKey ID, then paste the address the browser stops on (com.bosch.tt.dashtt.pointt://app/login?code=…). Find it in DevTools > Network.",
191
191
  descriptionKey: "provider-bosch:homecom.s_account.description",
192
192
  columns: 1,
193
193
  fields: [
@@ -1,5 +1,5 @@
1
- import { A as object, C as defineCustomActions, E as _enum, F as EventCategory, M as string, O as literal, S as customAction, T as isSecretMarkerLike, _ as waterHeaterCapability, b as errMsg, c as deviceAdoptionCapability, n as BaseDeviceProvider, o as climateControlCapability, s as connectionTestCapability, t as BaseDevice, w as isSecretMarker, y as DeviceType } from "../dist-hm2fhp2t.mjs";
2
- import { n as cascadeRemoveDevicesForIntegration, r as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-jlQzb2JO.mjs";
1
+ import { A as object, C as defineCustomActions, E as _enum, F as EventCategory, M as string, O as literal, S as customAction, T as isSecretMarkerLike, _ as waterHeaterCapability, b as errMsg, c as deviceAdoptionCapability, n as BaseDeviceProvider, o as climateControlCapability, s as connectionTestCapability, t as BaseDevice, w as isSecretMarker, y as DeviceType } from "../dist-C8QaBSuL.mjs";
2
+ import { n as cascadeRemoveDevicesForIntegration, r as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-W-dxoTT1.mjs";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  //#region src/homecom/custom-actions.ts
5
5
  var homeComActions = defineCustomActions({
@@ -186,7 +186,7 @@ function buildHomeComFormSchema() {
186
186
  id: "account",
187
187
  title: "HomeCom account",
188
188
  titleKey: "provider-bosch:homecom.s_account.title",
189
- description: "Create the login link and open it in a browser, then sign in with your SingleKey ID. The browser cannot open the app and stops on an address starting with com.bosch.tt.dashtt.pointt://app/login?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. Tokens can also be pasted by hand in the fields below.",
189
+ description: "Sign in with your SingleKey ID, then paste the address the browser stops on (com.bosch.tt.dashtt.pointt://app/login?code=…). Find it in DevTools > Network.",
190
190
  descriptionKey: "provider-bosch:homecom.s_account.description",
191
191
  columns: 1,
192
192
  fields: [
package/dist/shc/addon.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  //#region \0rolldown/runtime.js
3
3
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
4
4
  //#endregion
5
- const require_dist = require("../dist-ChJRdNGo.js");
5
+ const require_dist = require("../dist-D6D2JO8x.js");
6
6
  let node_crypto = require("node:crypto");
7
7
  let node_https = require("node:https");
8
8
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { A as object, C as defineCustomActions, D as array, E as _enum, F as EventCategory, M as string, O as literal, S as customAction, b as errMsg, c as deviceAdoptionCapability, f as heatingValveCapability, g as temperatureSensorCapability, h as switchCapability, i as batteryCapability, k as number, n as BaseDeviceProvider, o as climateControlCapability, s as connectionTestCapability, t as BaseDevice, y as DeviceType } from "../dist-hm2fhp2t.mjs";
1
+ import { A as object, C as defineCustomActions, D as array, E as _enum, F as EventCategory, M as string, O as literal, S as customAction, b as errMsg, c as deviceAdoptionCapability, f as heatingValveCapability, g as temperatureSensorCapability, h as switchCapability, i as batteryCapability, k as number, n as BaseDeviceProvider, o as climateControlCapability, s as connectionTestCapability, t as BaseDevice, y as DeviceType } from "../dist-C8QaBSuL.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { request } from "node:https";
package/i18n/en.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "bosch-home-connect.name": "Bosch Home Connect",
4
4
  "bosch-homecom.name": "Bosch HomeCom",
5
5
  "homecom.s_account.title": "HomeCom account",
6
- "homecom.s_account.description": "Create the login link and open it in a browser, then sign in with your SingleKey ID. The browser cannot open the app and stops on an address starting with com.bosch.tt.dashtt.pointt://app/login?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. Tokens can also be pasted by hand in the fields below.",
6
+ "homecom.s_account.description": "Sign in with your SingleKey ID, then paste the address the browser stops on (com.bosch.tt.dashtt.pointt://app/login?code=…). Find it in DevTools > Network.",
7
7
  "homecom.s_account.__homecom-begin.label": "HomeCom",
8
8
  "homecom.s_account.authorizeUrl.label": "Login link",
9
9
  "homecom.s_account.callback.label": "Redirect address or code",
@@ -18,7 +18,7 @@
18
18
  "homecom.s_account.brand.label": "Brand",
19
19
  "homecom.s_account.region.label": "Region",
20
20
  "home-connect.s_gateway.title": "Home Connect",
21
- "home-connect.s_gateway.description": "Create the login link and open it in a browser, then sign in. The browser cannot open the app and stops on an address starting with hcauth://auth/prod?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. The appliances are then downloaded and spoken to on the local network.",
21
+ "home-connect.s_gateway.description": "Open the login link, sign in, then paste the address the browser stops on (hcauth://auth/prod?code=…). Chrome: DevTools > Network > last redirect > Location.",
22
22
  "home-connect.s_gateway.__home-connect-begin.label": "Home Connect",
23
23
  "home-connect.s_gateway.authorizeUrl.label": "Login link",
24
24
  "home-connect.s_gateway.callback.label": "Redirect address or code",
package/i18n/it.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "bosch-home-connect.name": "Bosch Home Connect",
4
4
  "bosch-homecom.name": "Bosch HomeCom",
5
5
  "homecom.s_account.title": "Account HomeCom",
6
- "homecom.s_account.description": "Crea il link di accesso, aprilo in un browser e accedi con il tuo SingleKey ID. Il browser non riesce ad aprire l'app e si ferma su un indirizzo che inizia con com.bosch.tt.dashtt.pointt://app/login?code=. Incolla qui sotto l'intero indirizzo (o solo il codice) e premi Completa accesso. L'indirizzo si vede in Chrome ed Edge in DevTools (F12) > Network, con Preserve log attivo prima di accedere: seleziona l'ultimo reindirizzamento e copia l'intestazione Location. In Firefox usa Network > Headers > Location; in Safari usa Sviluppo > Mostra Web Inspector > Network. Un codice vale una sola volta e scade in pochi minuti. I token si possono anche incollare a mano nei campi qui sotto.",
6
+ "homecom.s_account.description": "Accedi con il tuo SingleKey ID, poi incolla l'indirizzo su cui si ferma il browser (com.bosch.tt.dashtt.pointt://app/login?code=…). Lo trovi in DevTools > Network.",
7
7
  "homecom.s_account.__homecom-begin.label": "HomeCom",
8
8
  "homecom.s_account.authorizeUrl.label": "Link di accesso",
9
9
  "homecom.s_account.callback.label": "Indirizzo di reindirizzamento o codice",
@@ -18,7 +18,7 @@
18
18
  "homecom.s_account.brand.label": "Marchio",
19
19
  "homecom.s_account.region.label": "Regione",
20
20
  "home-connect.s_gateway.title": "Home Connect",
21
- "home-connect.s_gateway.description": "Crea il link di accesso, aprilo in un browser e accedi. Il browser non riesce ad aprire l'app e si ferma su un indirizzo che inizia con hcauth://auth/prod?code=. Incolla qui sotto l'intero indirizzo (o solo il codice) e premi Completa accesso. L'indirizzo si vede in Chrome ed Edge in DevTools (F12) > Network, con Preserve log attivo prima di accedere: seleziona l'ultimo reindirizzamento e copia l'intestazione Location. In Firefox usa Network > Headers > Location; in Safari usa Sviluppo > Mostra Web Inspector > Network. Un codice vale una sola volta e scade in pochi minuti. Gli elettrodomestici vengono poi scaricati e usati sulla rete locale.",
21
+ "home-connect.s_gateway.description": "Apri il link di accesso, accedi, poi incolla l'indirizzo su cui si ferma il browser (hcauth://auth/prod?code=…). Chrome: DevTools > Network > ultimo redirect > Location.",
22
22
  "home-connect.s_gateway.__home-connect-begin.label": "Home Connect",
23
23
  "home-connect.s_gateway.authorizeUrl.label": "Link di accesso",
24
24
  "home-connect.s_gateway.callback.label": "Indirizzo di reindirizzamento o codice",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-bosch",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Bosch smart-home device-provider addons for CamStack — SHC, Home Connect, and HomeCom cloud integrations (scaffold)",
5
5
  "keywords": [
6
6
  "camstack",