@camstack/addon-provider-homeassistant 1.2.143 → 1.2.144

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-BQE3XuD4.js");
2
+ const require_dist = require("./dist-BwQbmKRC.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_zlib = require("node:zlib");
5
5
  //#region src/ha-admin-link.ts
package/dist/addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as parseAdminBase, A as contactCapability, At as union, B as gasCapability, C as buildAddonRouteProvider, Ct as array, D as climateControlCapability, Dt as object, E as carbonMonoxideCapability, Et as number, G as lockControlCapability, H as humiditySensorCapability, I as enumSensorCapability, J as normalizeUnit, K as mediaPlayerCapability, L as eventEmitterCapability, M as coverCapability, Mt as url, N as deviceAdminLinkCapability, Nt as EventCategory, O as colorCapability, Ot as record, P as deviceAdoptionCapability, R as fanControlCapability, S as brokerCapability, St as _enum, T as buttonCapability, Tt as literal, U as imageCapability, V as humidifierCapability, W as lawnMowerControlCapability, X as notifierCapability, Y as notificationOutputCapability, Z as numericSensorCapability, _ as automationControlCapability, _t as DeviceFeature, at as smokeCapability, b as binaryCapability, bt as createEvent, ct as temperatureSensorCapability, d as TargetSchema, dt as valveCapability, et as powerMeterCapability, f as accessoriesCapability, ft as vibrationCapability, g as ambientLightSensorCapability, h as alarmPanelCapability, ht as errMsg, it as scriptRunnerCapability, j as controlCapability, jt as unknown, k as connectivityCapability, kt as string, lt as updateCapability, m as airQualitySensorCapability, mt as weatherCapability, n as BaseDevice, nt as presenceCapability, ot as switchCapability, p as addonRoutesCapability, pt as waterHeaterCapability, q as motionCapability, r as BaseDeviceProvider, rt as pressureSensorCapability, s as EnumSensorDateTimeFormatSchema, st as tamperCapability, tt as prepareNotification, ut as vacuumControlCapability, v as batteryCapability, vt as DeviceRole, w as buildDeviceAdminUrl, wt as discriminatedUnion, x as brightnessCapability, y as bestLocationMatch, yt as DeviceType, z as floodCapability } from "./dist-CcnfBSQ5.mjs";
1
+ import { $ as parseAdminBase, A as contactCapability, At as union, B as gasCapability, C as buildAddonRouteProvider, Ct as array, D as climateControlCapability, Dt as object, E as carbonMonoxideCapability, Et as number, G as lockControlCapability, H as humiditySensorCapability, I as enumSensorCapability, J as normalizeUnit, K as mediaPlayerCapability, L as eventEmitterCapability, M as coverCapability, Mt as url, N as deviceAdminLinkCapability, Nt as EventCategory, O as colorCapability, Ot as record, P as deviceAdoptionCapability, R as fanControlCapability, S as brokerCapability, St as _enum, T as buttonCapability, Tt as literal, U as imageCapability, V as humidifierCapability, W as lawnMowerControlCapability, X as notifierCapability, Y as notificationOutputCapability, Z as numericSensorCapability, _ as automationControlCapability, _t as DeviceFeature, at as smokeCapability, b as binaryCapability, bt as createEvent, ct as temperatureSensorCapability, d as TargetSchema, dt as valveCapability, et as powerMeterCapability, f as accessoriesCapability, ft as vibrationCapability, g as ambientLightSensorCapability, h as alarmPanelCapability, ht as errMsg, it as scriptRunnerCapability, j as controlCapability, jt as unknown, k as connectivityCapability, kt as string, lt as updateCapability, m as airQualitySensorCapability, mt as weatherCapability, n as BaseDevice, nt as presenceCapability, ot as switchCapability, p as addonRoutesCapability, pt as waterHeaterCapability, q as motionCapability, r as BaseDeviceProvider, rt as pressureSensorCapability, s as EnumSensorDateTimeFormatSchema, st as tamperCapability, tt as prepareNotification, ut as vacuumControlCapability, v as batteryCapability, vt as DeviceRole, w as buildDeviceAdminUrl, wt as discriminatedUnion, x as brightnessCapability, y as bestLocationMatch, yt as DeviceType, z as floodCapability } from "./dist-CzKKAmb9.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
4
4
  //#region src/ha-admin-link.ts
@@ -5359,7 +5359,7 @@ var ZodIssueCode = {
5359
5359
  var ZodFirstPartyTypeKind;
5360
5360
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5361
5361
  //#endregion
5362
- //#region ../types/dist/sleep-PEo0-Fz9.mjs
5362
+ //#region ../types/dist/sleep-pM_J7YnY.mjs
5363
5363
  /**
5364
5364
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5365
5365
  * window to float samples (D455).
@@ -17358,6 +17358,7 @@ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
17358
17358
  sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
17359
17359
  sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
17360
17360
  sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
17361
+ sub("cover", "control", "control", "#0ea5e9", "door", "Cover");
17361
17362
  sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
17362
17363
  sub("siren", "control", "control", "#dc2626", "siren", "Siren");
17363
17364
  sub("button", "control", "control", "#10b981", "button", "Button");
@@ -17676,6 +17677,10 @@ var NcSystemEventKindSchema = _enum([
17676
17677
  "alarm-disarmed",
17677
17678
  "alarm-arming",
17678
17679
  "alarm-arm-refused",
17680
+ "alarm-pending",
17681
+ "alarm-rearmed",
17682
+ "alarm-sensor-bypass",
17683
+ "alarm-check-failed",
17679
17684
  "addon-updated",
17680
17685
  "server-updated",
17681
17686
  "export-completed",
@@ -18271,6 +18276,19 @@ var NcMediaFrameSchema = _enum([
18271
18276
  "full",
18272
18277
  "boxed"
18273
18278
  ]);
18279
+ /**
18280
+ * How a SENSOR notification shows the cameras that link the sensor.
18281
+ *
18282
+ * `mosaic` — one image composed of every linked camera's photograph, taken at
18283
+ * the trigger. `each` — one image per camera where the target takes several
18284
+ * attachments; a target that takes one keeps the first camera's (the degrade
18285
+ * engine drops the rest by order). Either way ONE notification per event.
18286
+ * Absent = `mosaic`, applied by the Notification Center and deliberately NOT
18287
+ * a Zod default (see NcRulePatchSchema: a default materialises on every
18288
+ * partial patch). Meaningless for a rule whose subject is not a sensor, and a
18289
+ * sensor linked to one camera ships that camera's plain snapshot either way.
18290
+ */
18291
+ var NcLinkedCameraModeSchema = _enum(["mosaic", "each"]);
18274
18292
  var NcMediaPolicySchema = object({
18275
18293
  attach: _enum([
18276
18294
  "best",
@@ -18316,7 +18334,9 @@ var NcMediaPolicySchema = object({
18316
18334
  * A profile that is not assigned falls back to the cheapest, and the render
18317
18335
  * reports which one actually ran.
18318
18336
  */
18319
- profile: CamProfileSchema.optional()
18337
+ profile: CamProfileSchema.optional(),
18338
+ /** See {@link NcLinkedCameraModeSchema}. */
18339
+ linkedCameras: NcLinkedCameraModeSchema.optional()
18320
18340
  });
18321
18341
  /**
18322
18342
  * Cooldown GRANULARITY over the subject's class — how much a fired
@@ -18436,7 +18456,7 @@ var NcRuleInputSchema = object({
18436
18456
  cooldownSec: 60,
18437
18457
  scope: "rule-device"
18438
18458
  }),
18439
- /** `{{var}}` templating over camera/class/label/zones/confidence/time. */
18459
+ /** `{{var}}` title/body. The variables are declared in `NC_TEMPLATE_VARS` (@camstack/types) and depend on the rule kind — see `templateVarsFor`. */
18440
18460
  template: object({
18441
18461
  title: string().max(500).optional(),
18442
18462
  body: string().max(2e3).optional()
@@ -18616,6 +18636,22 @@ var NcRulePatchSchema = NcRuleInputSchema.partial().extend({
18616
18636
  throttle: NcThrottleSchema.optional(),
18617
18637
  priority: number().int().min(1).max(5).optional()
18618
18638
  });
18639
+ var NcRuleClearableKeySchema = _enum([
18640
+ "template",
18641
+ "schedule",
18642
+ "targetUsers",
18643
+ "snoozeOptions",
18644
+ "snoozeAllowGlobal",
18645
+ "waitForEnhancement",
18646
+ "actions",
18647
+ "confirm",
18648
+ "groupIdleSec"
18649
+ ]);
18650
+ var NcRuleUpdateInputSchema = object({
18651
+ ruleId: string(),
18652
+ patch: NcRulePatchSchema,
18653
+ clear: array(NcRuleClearableKeySchema).optional()
18654
+ });
18619
18655
  /** A persisted rule. */
18620
18656
  var NcRuleSchema = NcRuleInputSchema.extend({
18621
18657
  id: string(),
@@ -18647,6 +18683,85 @@ var NcTestResultSchema = object({
18647
18683
  className: string().optional(),
18648
18684
  label: string().optional()
18649
18685
  });
18686
+ /** The five rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts`
18687
+ * is the same union; the template-vars spec pins the two together. */
18688
+ var NcRuleKindSchema = _enum([
18689
+ "detection",
18690
+ "sensor",
18691
+ "occupancy",
18692
+ "sound",
18693
+ "system"
18694
+ ]);
18695
+ /** Which producer renders a template: an ordinary rule, a timelapse, a digest. */
18696
+ var NcTemplateFamilySchema = _enum([
18697
+ "rule",
18698
+ "timelapse",
18699
+ "summary"
18700
+ ]);
18701
+ /** Which text field of that producer. `previewText` is the timelapse frame
18702
+ * caption, `captionText` the digest mosaic caption — both see fewer vars. */
18703
+ var NcTemplateFieldSchema = _enum([
18704
+ "title",
18705
+ "body",
18706
+ "previewText",
18707
+ "captionText"
18708
+ ]);
18709
+ var NcTemplateVarGroupSchema = _enum([
18710
+ "subject",
18711
+ "place",
18712
+ "time",
18713
+ "rule",
18714
+ "occupancy",
18715
+ "sound",
18716
+ "sensor",
18717
+ "system",
18718
+ "digest",
18719
+ "ai"
18720
+ ]);
18721
+ /**
18722
+ * ONE `{{var}}` a notification template may name.
18723
+ *
18724
+ * `families` / `fields` / `kinds` / `deliveries` / `systemEventKinds` say WHERE
18725
+ * it has a value; absent = no restriction on that axis. `systemEventKinds` is
18726
+ * read only for a `system` rule. `dynamic` declares a FAMILY of names
18727
+ * (`count_<class>`): the descriptor's `name` is the example member.
18728
+ */
18729
+ var NcTemplateVarDescriptorSchema = object({
18730
+ name: string().regex(/^\w+$/),
18731
+ label: string(),
18732
+ description: string().optional(),
18733
+ example: string(),
18734
+ group: NcTemplateVarGroupSchema,
18735
+ families: array(NcTemplateFamilySchema).min(1),
18736
+ fields: array(NcTemplateFieldSchema).optional(),
18737
+ kinds: array(NcRuleKindSchema).optional(),
18738
+ deliveries: array(NcDeliverySchema).optional(),
18739
+ systemEventKinds: array(NcSystemEventKindSchema).optional(),
18740
+ dynamic: object({
18741
+ prefix: literal("count_"),
18742
+ from: literal("classes")
18743
+ }).optional()
18744
+ });
18745
+ /** Input to `previewTemplate` — the editor's own context, plus the draft text. */
18746
+ var NcTemplatePreviewInputSchema = object({
18747
+ context: object({
18748
+ family: NcTemplateFamilySchema,
18749
+ field: NcTemplateFieldSchema,
18750
+ kind: NcRuleKindSchema.optional(),
18751
+ delivery: NcDeliverySchema.optional(),
18752
+ systemEventKinds: array(NcSystemEventKindSchema).optional()
18753
+ }),
18754
+ template: object({
18755
+ title: string().max(500).optional(),
18756
+ body: string().max(2e3).optional()
18757
+ })
18758
+ });
18759
+ var NcTemplatePreviewSchema = object({
18760
+ title: string().nullable(),
18761
+ body: string().nullable(),
18762
+ /** Names the template uses that this context never fills — they render empty. */
18763
+ unknown: array(string())
18764
+ });
18650
18765
  var NcConditionDescriptorSchema = object({
18651
18766
  /** Field id inside `NcConditions` (or `'schedule'` for the rule-level group). */
18652
18767
  id: string(),
@@ -18912,8 +19027,17 @@ object({
18912
19027
  lastAt: number()
18913
19028
  });
18914
19029
  /**
18915
- * The three durations the panel's state machine runs on, plus who hears about
18916
- * an arm.
19030
+ * The per-mode lists, at most one per mode. Two lists for one mode have no
19031
+ * meaning a reader could agree on (union? the last one?), so they are refused
19032
+ * at the boundary rather than interpreted.
19033
+ */
19034
+ var NcAlarmNonBlockingListSchema = array(object({
19035
+ mode: AlarmArmModeSchema,
19036
+ deviceIds: array(number().int()).max(200)
19037
+ })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
19038
+ /**
19039
+ * The three durations the panel's state machine runs on, who hears about its
19040
+ * transitions, and which openings each mode tolerates.
18917
19041
  *
18918
19042
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
18919
19043
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -18932,21 +19056,39 @@ var NcAlarmSettingsSchema = object({
18932
19056
  * existed, and therefore what an untouched install keeps doing.
18933
19057
  */
18934
19058
  triggeredDurationSec: number().int().min(0).max(3600),
18935
- /** Send a notification when a mode takes effect. */
18936
- announceArm: boolean(),
18937
19059
  /**
18938
- * Where that notification goes. Target ids from `notification-output`.
19060
+ * Deprecated by D630 — every transition is announced. Parsed, never read;
19061
+ * remove after one release. Optional so an older admin's patch (which still
19062
+ * sends it) parses rather than failing the whole save.
19063
+ */
19064
+ announceArm: boolean().optional(),
19065
+ /**
19066
+ * Target ids from `notification-output`: every alarm transition goes here.
18939
19067
  *
18940
- * Explicit rather than "everyone": an arm announcement is a household
19068
+ * Explicit rather than "everyone": an alarm announcement is a household
18941
19069
  * message, and broadcasting it to every configured endpoint (including a
18942
19070
  * webhook wired to something else) is not a default anybody would choose.
18943
- * Empty with `announceArm: true` sends nothing, and the server logs that —
18944
- * silence must be attributable.
19071
+ * Empty sends nothing, and the server logs that — silence must be
19072
+ * attributable.
18945
19073
  */
18946
- announceTargets: array(string().min(1)).max(16)
19074
+ announceTargets: array(string().min(1)).max(16),
19075
+ /**
19076
+ * Per arm mode, the covered devices whose being OPEN does not refuse the
19077
+ * arm (a window left ajar for the cat under `home`, say). At most one entry
19078
+ * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
19079
+ * parses as "every opening blocks" — the behaviour it was written under.
19080
+ */
19081
+ nonBlocking: NcAlarmNonBlockingListSchema.default([])
18947
19082
  });
18948
- /** Every field optional — a tab edits one control at a time. */
18949
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.partial();
19083
+ /**
19084
+ * Every field optional — a tab edits one control at a time.
19085
+ *
19086
+ * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
19087
+ * remove an inner `.default()`, so a patch that never named the field would
19088
+ * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
19089
+ * (the same trap {@link NcRulePatchSchema} documents).
19090
+ */
19091
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
18950
19092
  /**
18951
19093
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
18952
19094
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -18991,6 +19133,36 @@ var NcAlarmModeCoverageSchema = object({
18991
19133
  */
18992
19134
  skippedDevices: array(NcAlarmSkippedDeviceSchema).default([])
18993
19135
  });
19136
+ /** One covered device that reads open, and whether that refuses the arm. */
19137
+ var NcAlarmOpeningSchema = object({
19138
+ deviceId: number().int(),
19139
+ name: string(),
19140
+ /** The device's state word as read (`open`, `unlocked`, …). */
19141
+ state: string(),
19142
+ /** False when the mode's non-blocking list names this device. */
19143
+ blocking: boolean()
19144
+ });
19145
+ /** A device the panel is currently ignoring, and which side of it it is on. */
19146
+ var NcAlarmExclusionViewSchema = object({
19147
+ deviceId: number().int(),
19148
+ name: string(),
19149
+ phase: _enum(["open", "closed"])
19150
+ });
19151
+ /** One mode's openings: devices read open, plus those whose state is unknown. */
19152
+ var NcAlarmModeOpeningsSchema = object({
19153
+ mode: AlarmArmModeSchema,
19154
+ devices: array(NcAlarmOpeningSchema),
19155
+ /** Covered devices whose state could not be read (D49): neither open nor closed. */
19156
+ unknown: array(number().int())
19157
+ });
19158
+ var NcAlarmLiveSchema = object({
19159
+ state: AlarmStateSchema,
19160
+ /** The mode being armed into (exit delay) or held; null when disarmed. */
19161
+ targetMode: AlarmArmModeSchema.nullable(),
19162
+ availableModes: array(AlarmArmModeSchema),
19163
+ openings: array(NcAlarmModeOpeningsSchema),
19164
+ exclusions: array(NcAlarmExclusionViewSchema)
19165
+ });
18994
19166
  var NcAlarmConfigSchema = object({
18995
19167
  /**
18996
19168
  * The panel's device id, or null when this install has no panel (the ensure
@@ -19000,7 +19172,13 @@ var NcAlarmConfigSchema = object({
19000
19172
  */
19001
19173
  deviceId: number().int().nullable(),
19002
19174
  settings: NcAlarmSettingsSchema,
19003
- coverage: array(NcAlarmModeCoverageSchema)
19175
+ coverage: array(NcAlarmModeCoverageSchema),
19176
+ /**
19177
+ * The panel as it is NOW — state, what each mode would find open, what is
19178
+ * excluded. Optional: a panel-less hub answers without it, and a client
19179
+ * must not read its absence as "nothing is open".
19180
+ */
19181
+ live: NcAlarmLiveSchema.optional()
19004
19182
  });
19005
19183
  /**
19006
19184
  * ONE rule's demand on ONE camera's clip ring.
@@ -19048,10 +19226,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
19048
19226
  kind: "mutation",
19049
19227
  auth: "admin",
19050
19228
  caller: "required"
19051
- }), method(object({
19052
- ruleId: string(),
19053
- patch: NcRulePatchSchema
19054
- }), object({ rule: NcRuleSchema }), {
19229
+ }), method(NcRuleUpdateInputSchema, object({ rule: NcRuleSchema }), {
19055
19230
  kind: "mutation",
19056
19231
  auth: "admin",
19057
19232
  caller: "required"
@@ -19079,7 +19254,10 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
19079
19254
  }), method(object({}), object({
19080
19255
  catalog: array(NcConditionDescriptorSchema),
19081
19256
  taxonomy: NcTaxonomySchema.optional()
19082
- })), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
19257
+ })), method(object({}), object({ vars: array(NcTemplateVarDescriptorSchema) })), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
19258
+ kind: "mutation",
19259
+ access: "view"
19260
+ }), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
19083
19261
  kind: "mutation",
19084
19262
  caller: "required"
19085
19263
  }), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
@@ -23568,6 +23746,7 @@ var CAP_TO_KIND = {
23568
23746
  "enum-sensor": "enum-sensor",
23569
23747
  "event-emitter": "device-event",
23570
23748
  "lock-control": "lock",
23749
+ cover: "cover",
23571
23750
  switch: "switch",
23572
23751
  button: "button",
23573
23752
  doorbell: "doorbell"
@@ -43080,6 +43259,12 @@ Object.freeze({
43080
43259
  addonId: null,
43081
43260
  access: "view"
43082
43261
  },
43262
+ "notificationRules.getTemplateCatalog": {
43263
+ capName: "notification-rules",
43264
+ capScope: "system",
43265
+ addonId: null,
43266
+ access: "view"
43267
+ },
43083
43268
  "notificationRules.listDeviceMutes": {
43084
43269
  capName: "notification-rules",
43085
43270
  capScope: "system",
@@ -43098,6 +43283,12 @@ Object.freeze({
43098
43283
  addonId: null,
43099
43284
  access: "view"
43100
43285
  },
43286
+ "notificationRules.previewTemplate": {
43287
+ capName: "notification-rules",
43288
+ capScope: "system",
43289
+ addonId: null,
43290
+ access: "view"
43291
+ },
43101
43292
  "notificationRules.resolveArtifactUrl": {
43102
43293
  capName: "notification-rules",
43103
43294
  capScope: "system",
@@ -48872,6 +49063,41 @@ function prepareNotification(caps, n) {
48872
49063
  };
48873
49064
  }
48874
49065
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
49066
+ var RULE_BODY_ONLY = ["body"];
49067
+ var RULE_TEXT = ["title", "body"];
49068
+ var BATTERY = ["device-battery-low", "device-battery-normal"];
49069
+ var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
49070
+ var UPDATES = [
49071
+ "addon-update-available",
49072
+ "server-update-available",
49073
+ "wrapper-update-available"
49074
+ ];
49075
+ var PACKAGE_UPDATES = ["addon-update-available", "server-update-available"];
49076
+ var ALARM = [
49077
+ "alarm-triggered",
49078
+ "alarm-armed",
49079
+ "alarm-disarmed",
49080
+ "alarm-arming",
49081
+ "alarm-arm-refused",
49082
+ "alarm-pending",
49083
+ "alarm-rearmed",
49084
+ "alarm-sensor-bypass",
49085
+ "alarm-check-failed"
49086
+ ];
49087
+ var COMBINED_ALARM = ["alarm-triggered"];
49088
+ var DEVICE_EVENTS = [
49089
+ "device-online",
49090
+ "device-offline",
49091
+ "device-disabled",
49092
+ "device-enabled",
49093
+ ...BATTERY,
49094
+ ...CONSUMABLE,
49095
+ "stream-online",
49096
+ "stream-offline",
49097
+ "detection-blind",
49098
+ ...ALARM
49099
+ ];
49100
+ [...DEVICE_EVENTS], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_BODY_ONLY], [...RULE_TEXT], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...BATTERY, ...CONSUMABLE], [...RULE_TEXT], [...CONSUMABLE], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT];
48875
49101
  /**
48876
49102
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
48877
49103
  *
@@ -5359,7 +5359,7 @@ var ZodIssueCode = {
5359
5359
  var ZodFirstPartyTypeKind;
5360
5360
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5361
5361
  //#endregion
5362
- //#region ../types/dist/sleep-PEo0-Fz9.mjs
5362
+ //#region ../types/dist/sleep-pM_J7YnY.mjs
5363
5363
  /**
5364
5364
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5365
5365
  * window to float samples (D455).
@@ -17358,6 +17358,7 @@ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
17358
17358
  sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
17359
17359
  sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
17360
17360
  sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
17361
+ sub("cover", "control", "control", "#0ea5e9", "door", "Cover");
17361
17362
  sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
17362
17363
  sub("siren", "control", "control", "#dc2626", "siren", "Siren");
17363
17364
  sub("button", "control", "control", "#10b981", "button", "Button");
@@ -17676,6 +17677,10 @@ var NcSystemEventKindSchema = _enum([
17676
17677
  "alarm-disarmed",
17677
17678
  "alarm-arming",
17678
17679
  "alarm-arm-refused",
17680
+ "alarm-pending",
17681
+ "alarm-rearmed",
17682
+ "alarm-sensor-bypass",
17683
+ "alarm-check-failed",
17679
17684
  "addon-updated",
17680
17685
  "server-updated",
17681
17686
  "export-completed",
@@ -18271,6 +18276,19 @@ var NcMediaFrameSchema = _enum([
18271
18276
  "full",
18272
18277
  "boxed"
18273
18278
  ]);
18279
+ /**
18280
+ * How a SENSOR notification shows the cameras that link the sensor.
18281
+ *
18282
+ * `mosaic` — one image composed of every linked camera's photograph, taken at
18283
+ * the trigger. `each` — one image per camera where the target takes several
18284
+ * attachments; a target that takes one keeps the first camera's (the degrade
18285
+ * engine drops the rest by order). Either way ONE notification per event.
18286
+ * Absent = `mosaic`, applied by the Notification Center and deliberately NOT
18287
+ * a Zod default (see NcRulePatchSchema: a default materialises on every
18288
+ * partial patch). Meaningless for a rule whose subject is not a sensor, and a
18289
+ * sensor linked to one camera ships that camera's plain snapshot either way.
18290
+ */
18291
+ var NcLinkedCameraModeSchema = _enum(["mosaic", "each"]);
18274
18292
  var NcMediaPolicySchema = object({
18275
18293
  attach: _enum([
18276
18294
  "best",
@@ -18316,7 +18334,9 @@ var NcMediaPolicySchema = object({
18316
18334
  * A profile that is not assigned falls back to the cheapest, and the render
18317
18335
  * reports which one actually ran.
18318
18336
  */
18319
- profile: CamProfileSchema.optional()
18337
+ profile: CamProfileSchema.optional(),
18338
+ /** See {@link NcLinkedCameraModeSchema}. */
18339
+ linkedCameras: NcLinkedCameraModeSchema.optional()
18320
18340
  });
18321
18341
  /**
18322
18342
  * Cooldown GRANULARITY over the subject's class — how much a fired
@@ -18436,7 +18456,7 @@ var NcRuleInputSchema = object({
18436
18456
  cooldownSec: 60,
18437
18457
  scope: "rule-device"
18438
18458
  }),
18439
- /** `{{var}}` templating over camera/class/label/zones/confidence/time. */
18459
+ /** `{{var}}` title/body. The variables are declared in `NC_TEMPLATE_VARS` (@camstack/types) and depend on the rule kind — see `templateVarsFor`. */
18440
18460
  template: object({
18441
18461
  title: string().max(500).optional(),
18442
18462
  body: string().max(2e3).optional()
@@ -18616,6 +18636,22 @@ var NcRulePatchSchema = NcRuleInputSchema.partial().extend({
18616
18636
  throttle: NcThrottleSchema.optional(),
18617
18637
  priority: number().int().min(1).max(5).optional()
18618
18638
  });
18639
+ var NcRuleClearableKeySchema = _enum([
18640
+ "template",
18641
+ "schedule",
18642
+ "targetUsers",
18643
+ "snoozeOptions",
18644
+ "snoozeAllowGlobal",
18645
+ "waitForEnhancement",
18646
+ "actions",
18647
+ "confirm",
18648
+ "groupIdleSec"
18649
+ ]);
18650
+ var NcRuleUpdateInputSchema = object({
18651
+ ruleId: string(),
18652
+ patch: NcRulePatchSchema,
18653
+ clear: array(NcRuleClearableKeySchema).optional()
18654
+ });
18619
18655
  /** A persisted rule. */
18620
18656
  var NcRuleSchema = NcRuleInputSchema.extend({
18621
18657
  id: string(),
@@ -18647,6 +18683,85 @@ var NcTestResultSchema = object({
18647
18683
  className: string().optional(),
18648
18684
  label: string().optional()
18649
18685
  });
18686
+ /** The five rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts`
18687
+ * is the same union; the template-vars spec pins the two together. */
18688
+ var NcRuleKindSchema = _enum([
18689
+ "detection",
18690
+ "sensor",
18691
+ "occupancy",
18692
+ "sound",
18693
+ "system"
18694
+ ]);
18695
+ /** Which producer renders a template: an ordinary rule, a timelapse, a digest. */
18696
+ var NcTemplateFamilySchema = _enum([
18697
+ "rule",
18698
+ "timelapse",
18699
+ "summary"
18700
+ ]);
18701
+ /** Which text field of that producer. `previewText` is the timelapse frame
18702
+ * caption, `captionText` the digest mosaic caption — both see fewer vars. */
18703
+ var NcTemplateFieldSchema = _enum([
18704
+ "title",
18705
+ "body",
18706
+ "previewText",
18707
+ "captionText"
18708
+ ]);
18709
+ var NcTemplateVarGroupSchema = _enum([
18710
+ "subject",
18711
+ "place",
18712
+ "time",
18713
+ "rule",
18714
+ "occupancy",
18715
+ "sound",
18716
+ "sensor",
18717
+ "system",
18718
+ "digest",
18719
+ "ai"
18720
+ ]);
18721
+ /**
18722
+ * ONE `{{var}}` a notification template may name.
18723
+ *
18724
+ * `families` / `fields` / `kinds` / `deliveries` / `systemEventKinds` say WHERE
18725
+ * it has a value; absent = no restriction on that axis. `systemEventKinds` is
18726
+ * read only for a `system` rule. `dynamic` declares a FAMILY of names
18727
+ * (`count_<class>`): the descriptor's `name` is the example member.
18728
+ */
18729
+ var NcTemplateVarDescriptorSchema = object({
18730
+ name: string().regex(/^\w+$/),
18731
+ label: string(),
18732
+ description: string().optional(),
18733
+ example: string(),
18734
+ group: NcTemplateVarGroupSchema,
18735
+ families: array(NcTemplateFamilySchema).min(1),
18736
+ fields: array(NcTemplateFieldSchema).optional(),
18737
+ kinds: array(NcRuleKindSchema).optional(),
18738
+ deliveries: array(NcDeliverySchema).optional(),
18739
+ systemEventKinds: array(NcSystemEventKindSchema).optional(),
18740
+ dynamic: object({
18741
+ prefix: literal("count_"),
18742
+ from: literal("classes")
18743
+ }).optional()
18744
+ });
18745
+ /** Input to `previewTemplate` — the editor's own context, plus the draft text. */
18746
+ var NcTemplatePreviewInputSchema = object({
18747
+ context: object({
18748
+ family: NcTemplateFamilySchema,
18749
+ field: NcTemplateFieldSchema,
18750
+ kind: NcRuleKindSchema.optional(),
18751
+ delivery: NcDeliverySchema.optional(),
18752
+ systemEventKinds: array(NcSystemEventKindSchema).optional()
18753
+ }),
18754
+ template: object({
18755
+ title: string().max(500).optional(),
18756
+ body: string().max(2e3).optional()
18757
+ })
18758
+ });
18759
+ var NcTemplatePreviewSchema = object({
18760
+ title: string().nullable(),
18761
+ body: string().nullable(),
18762
+ /** Names the template uses that this context never fills — they render empty. */
18763
+ unknown: array(string())
18764
+ });
18650
18765
  var NcConditionDescriptorSchema = object({
18651
18766
  /** Field id inside `NcConditions` (or `'schedule'` for the rule-level group). */
18652
18767
  id: string(),
@@ -18912,8 +19027,17 @@ object({
18912
19027
  lastAt: number()
18913
19028
  });
18914
19029
  /**
18915
- * The three durations the panel's state machine runs on, plus who hears about
18916
- * an arm.
19030
+ * The per-mode lists, at most one per mode. Two lists for one mode have no
19031
+ * meaning a reader could agree on (union? the last one?), so they are refused
19032
+ * at the boundary rather than interpreted.
19033
+ */
19034
+ var NcAlarmNonBlockingListSchema = array(object({
19035
+ mode: AlarmArmModeSchema,
19036
+ deviceIds: array(number().int()).max(200)
19037
+ })).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
19038
+ /**
19039
+ * The three durations the panel's state machine runs on, who hears about its
19040
+ * transitions, and which openings each mode tolerates.
18917
19041
  *
18918
19042
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
18919
19043
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -18932,21 +19056,39 @@ var NcAlarmSettingsSchema = object({
18932
19056
  * existed, and therefore what an untouched install keeps doing.
18933
19057
  */
18934
19058
  triggeredDurationSec: number().int().min(0).max(3600),
18935
- /** Send a notification when a mode takes effect. */
18936
- announceArm: boolean(),
18937
19059
  /**
18938
- * Where that notification goes. Target ids from `notification-output`.
19060
+ * Deprecated by D630 — every transition is announced. Parsed, never read;
19061
+ * remove after one release. Optional so an older admin's patch (which still
19062
+ * sends it) parses rather than failing the whole save.
19063
+ */
19064
+ announceArm: boolean().optional(),
19065
+ /**
19066
+ * Target ids from `notification-output`: every alarm transition goes here.
18939
19067
  *
18940
- * Explicit rather than "everyone": an arm announcement is a household
19068
+ * Explicit rather than "everyone": an alarm announcement is a household
18941
19069
  * message, and broadcasting it to every configured endpoint (including a
18942
19070
  * webhook wired to something else) is not a default anybody would choose.
18943
- * Empty with `announceArm: true` sends nothing, and the server logs that —
18944
- * silence must be attributable.
19071
+ * Empty sends nothing, and the server logs that — silence must be
19072
+ * attributable.
18945
19073
  */
18946
- announceTargets: array(string().min(1)).max(16)
19074
+ announceTargets: array(string().min(1)).max(16),
19075
+ /**
19076
+ * Per arm mode, the covered devices whose being OPEN does not refuse the
19077
+ * arm (a window left ajar for the cat under `home`, say). At most one entry
19078
+ * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
19079
+ * parses as "every opening blocks" — the behaviour it was written under.
19080
+ */
19081
+ nonBlocking: NcAlarmNonBlockingListSchema.default([])
18947
19082
  });
18948
- /** Every field optional — a tab edits one control at a time. */
18949
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.partial();
19083
+ /**
19084
+ * Every field optional — a tab edits one control at a time.
19085
+ *
19086
+ * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
19087
+ * remove an inner `.default()`, so a patch that never named the field would
19088
+ * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
19089
+ * (the same trap {@link NcRulePatchSchema} documents).
19090
+ */
19091
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
18950
19092
  /**
18951
19093
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
18952
19094
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -18991,6 +19133,36 @@ var NcAlarmModeCoverageSchema = object({
18991
19133
  */
18992
19134
  skippedDevices: array(NcAlarmSkippedDeviceSchema).default([])
18993
19135
  });
19136
+ /** One covered device that reads open, and whether that refuses the arm. */
19137
+ var NcAlarmOpeningSchema = object({
19138
+ deviceId: number().int(),
19139
+ name: string(),
19140
+ /** The device's state word as read (`open`, `unlocked`, …). */
19141
+ state: string(),
19142
+ /** False when the mode's non-blocking list names this device. */
19143
+ blocking: boolean()
19144
+ });
19145
+ /** A device the panel is currently ignoring, and which side of it it is on. */
19146
+ var NcAlarmExclusionViewSchema = object({
19147
+ deviceId: number().int(),
19148
+ name: string(),
19149
+ phase: _enum(["open", "closed"])
19150
+ });
19151
+ /** One mode's openings: devices read open, plus those whose state is unknown. */
19152
+ var NcAlarmModeOpeningsSchema = object({
19153
+ mode: AlarmArmModeSchema,
19154
+ devices: array(NcAlarmOpeningSchema),
19155
+ /** Covered devices whose state could not be read (D49): neither open nor closed. */
19156
+ unknown: array(number().int())
19157
+ });
19158
+ var NcAlarmLiveSchema = object({
19159
+ state: AlarmStateSchema,
19160
+ /** The mode being armed into (exit delay) or held; null when disarmed. */
19161
+ targetMode: AlarmArmModeSchema.nullable(),
19162
+ availableModes: array(AlarmArmModeSchema),
19163
+ openings: array(NcAlarmModeOpeningsSchema),
19164
+ exclusions: array(NcAlarmExclusionViewSchema)
19165
+ });
18994
19166
  var NcAlarmConfigSchema = object({
18995
19167
  /**
18996
19168
  * The panel's device id, or null when this install has no panel (the ensure
@@ -19000,7 +19172,13 @@ var NcAlarmConfigSchema = object({
19000
19172
  */
19001
19173
  deviceId: number().int().nullable(),
19002
19174
  settings: NcAlarmSettingsSchema,
19003
- coverage: array(NcAlarmModeCoverageSchema)
19175
+ coverage: array(NcAlarmModeCoverageSchema),
19176
+ /**
19177
+ * The panel as it is NOW — state, what each mode would find open, what is
19178
+ * excluded. Optional: a panel-less hub answers without it, and a client
19179
+ * must not read its absence as "nothing is open".
19180
+ */
19181
+ live: NcAlarmLiveSchema.optional()
19004
19182
  });
19005
19183
  /**
19006
19184
  * ONE rule's demand on ONE camera's clip ring.
@@ -19048,10 +19226,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
19048
19226
  kind: "mutation",
19049
19227
  auth: "admin",
19050
19228
  caller: "required"
19051
- }), method(object({
19052
- ruleId: string(),
19053
- patch: NcRulePatchSchema
19054
- }), object({ rule: NcRuleSchema }), {
19229
+ }), method(NcRuleUpdateInputSchema, object({ rule: NcRuleSchema }), {
19055
19230
  kind: "mutation",
19056
19231
  auth: "admin",
19057
19232
  caller: "required"
@@ -19079,7 +19254,10 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
19079
19254
  }), method(object({}), object({
19080
19255
  catalog: array(NcConditionDescriptorSchema),
19081
19256
  taxonomy: NcTaxonomySchema.optional()
19082
- })), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
19257
+ })), method(object({}), object({ vars: array(NcTemplateVarDescriptorSchema) })), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
19258
+ kind: "mutation",
19259
+ access: "view"
19260
+ }), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
19083
19261
  kind: "mutation",
19084
19262
  caller: "required"
19085
19263
  }), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
@@ -23568,6 +23746,7 @@ var CAP_TO_KIND = {
23568
23746
  "enum-sensor": "enum-sensor",
23569
23747
  "event-emitter": "device-event",
23570
23748
  "lock-control": "lock",
23749
+ cover: "cover",
23571
23750
  switch: "switch",
23572
23751
  button: "button",
23573
23752
  doorbell: "doorbell"
@@ -43080,6 +43259,12 @@ Object.freeze({
43080
43259
  addonId: null,
43081
43260
  access: "view"
43082
43261
  },
43262
+ "notificationRules.getTemplateCatalog": {
43263
+ capName: "notification-rules",
43264
+ capScope: "system",
43265
+ addonId: null,
43266
+ access: "view"
43267
+ },
43083
43268
  "notificationRules.listDeviceMutes": {
43084
43269
  capName: "notification-rules",
43085
43270
  capScope: "system",
@@ -43098,6 +43283,12 @@ Object.freeze({
43098
43283
  addonId: null,
43099
43284
  access: "view"
43100
43285
  },
43286
+ "notificationRules.previewTemplate": {
43287
+ capName: "notification-rules",
43288
+ capScope: "system",
43289
+ addonId: null,
43290
+ access: "view"
43291
+ },
43101
43292
  "notificationRules.resolveArtifactUrl": {
43102
43293
  capName: "notification-rules",
43103
43294
  capScope: "system",
@@ -48872,6 +49063,41 @@ function prepareNotification(caps, n) {
48872
49063
  };
48873
49064
  }
48874
49065
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
49066
+ var RULE_BODY_ONLY = ["body"];
49067
+ var RULE_TEXT = ["title", "body"];
49068
+ var BATTERY = ["device-battery-low", "device-battery-normal"];
49069
+ var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
49070
+ var UPDATES = [
49071
+ "addon-update-available",
49072
+ "server-update-available",
49073
+ "wrapper-update-available"
49074
+ ];
49075
+ var PACKAGE_UPDATES = ["addon-update-available", "server-update-available"];
49076
+ var ALARM = [
49077
+ "alarm-triggered",
49078
+ "alarm-armed",
49079
+ "alarm-disarmed",
49080
+ "alarm-arming",
49081
+ "alarm-arm-refused",
49082
+ "alarm-pending",
49083
+ "alarm-rearmed",
49084
+ "alarm-sensor-bypass",
49085
+ "alarm-check-failed"
49086
+ ];
49087
+ var COMBINED_ALARM = ["alarm-triggered"];
49088
+ var DEVICE_EVENTS = [
49089
+ "device-online",
49090
+ "device-offline",
49091
+ "device-disabled",
49092
+ "device-enabled",
49093
+ ...BATTERY,
49094
+ ...CONSUMABLE,
49095
+ "stream-online",
49096
+ "stream-offline",
49097
+ "detection-blind",
49098
+ ...ALARM
49099
+ ];
49100
+ [...DEVICE_EVENTS], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_BODY_ONLY], [...RULE_TEXT], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...BATTERY, ...CONSUMABLE], [...RULE_TEXT], [...CONSUMABLE], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT];
48875
49101
  /**
48876
49102
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
48877
49103
  *
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  //#endregion
6
- const require_dist = require("../dist-BQE3XuD4.js");
6
+ const require_dist = require("../dist-BwQbmKRC.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  //#region src/ha-export/topics.ts
9
9
  /**
@@ -4938,9 +4938,11 @@ function projectMotion(deviceKey, input) {
4938
4938
  /**
4939
4939
  * A clearing edge carries no timestamp. `last_detection` answers "when
4940
4940
  * did something last happen", and restamping it when nothing happened
4941
- * makes an idle camera look permanently busy.
4941
+ * makes an idle camera look permanently busy. Same for a detection whose
4942
+ * timestamp is unknown or implausible — omitted rather than published as
4943
+ * a lie the automation author would trust.
4942
4944
  */
4943
- if (input.detected) values.push({
4945
+ if (input.detected && input.timestamp !== null) values.push({
4944
4946
  topic: stateTopic(deviceKey, "motion_last_detection"),
4945
4947
  value: iso(input.timestamp)
4946
4948
  });
@@ -5930,6 +5932,13 @@ var PUSH_PATH = "/api/camstack/push";
5930
5932
  var MEDIA_ROUTE_PREFIX = `/addon/${ADDON_ID}/ha-media`;
5931
5933
  /** How long `doorbell_pressed` stays on before its OFF edge is published. */
5932
5934
  var DOORBELL_PULSE_MS = 5e3;
5935
+ /**
5936
+ * The earliest timestamp a real change could plausibly carry. Below this,
5937
+ * a slice field is a placeholder or a seeded "known value, unknown change
5938
+ * time" stamp (e.g. Homematic's `HM_SEEDED_CHANGED_AT = 1`) — never a real
5939
+ * instant. Year 2000 predates every device this addon exports.
5940
+ */
5941
+ var MIN_PLAUSIBLE_TIMESTAMP_MS = Date.UTC(2e3, 0, 1);
5933
5942
  /** The hub's API port — where the data plane is reverse-proxied. */
5934
5943
  var HUB_API_PORT = 4443;
5935
5944
  /** One PTZ button press, as a relative move. */
@@ -6232,9 +6241,10 @@ var HaExportAddon = class extends require_dist.BaseAddon {
6232
6241
  const detected = slice["detected"];
6233
6242
  const lastDetectedAt = readNumber(slice, "lastDetectedAt");
6234
6243
  if (typeof detected !== "boolean") return;
6244
+ const timestamp = lastDetectedAt === null ? Date.now() : lastDetectedAt >= MIN_PLAUSIBLE_TIMESTAMP_MS ? lastDetectedAt : null;
6235
6245
  this.push(deviceId, projectMotion(deviceKey, {
6236
6246
  detected,
6237
- timestamp: lastDetectedAt ?? Date.now()
6247
+ timestamp
6238
6248
  }));
6239
6249
  }
6240
6250
  }
@@ -1,4 +1,4 @@
1
- import { C as buildAddonRouteProvider, F as deviceExportCapability, Nt as EventCategory, Q as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, gt as BaseAddon, i as CAMERA_SWITCH_ORDER, kt as string, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, xt as nodePin } from "../dist-CcnfBSQ5.mjs";
1
+ import { C as buildAddonRouteProvider, F as deviceExportCapability, Nt as EventCategory, Q as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, gt as BaseAddon, i as CAMERA_SWITCH_ORDER, kt as string, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, xt as nodePin } from "../dist-CzKKAmb9.mjs";
2
2
  import { createHmac, timingSafeEqual } from "node:crypto";
3
3
  //#region src/ha-export/topics.ts
4
4
  /**
@@ -4933,9 +4933,11 @@ function projectMotion(deviceKey, input) {
4933
4933
  /**
4934
4934
  * A clearing edge carries no timestamp. `last_detection` answers "when
4935
4935
  * did something last happen", and restamping it when nothing happened
4936
- * makes an idle camera look permanently busy.
4936
+ * makes an idle camera look permanently busy. Same for a detection whose
4937
+ * timestamp is unknown or implausible — omitted rather than published as
4938
+ * a lie the automation author would trust.
4937
4939
  */
4938
- if (input.detected) values.push({
4940
+ if (input.detected && input.timestamp !== null) values.push({
4939
4941
  topic: stateTopic(deviceKey, "motion_last_detection"),
4940
4942
  value: iso(input.timestamp)
4941
4943
  });
@@ -5925,6 +5927,13 @@ var PUSH_PATH = "/api/camstack/push";
5925
5927
  var MEDIA_ROUTE_PREFIX = `/addon/${ADDON_ID}/ha-media`;
5926
5928
  /** How long `doorbell_pressed` stays on before its OFF edge is published. */
5927
5929
  var DOORBELL_PULSE_MS = 5e3;
5930
+ /**
5931
+ * The earliest timestamp a real change could plausibly carry. Below this,
5932
+ * a slice field is a placeholder or a seeded "known value, unknown change
5933
+ * time" stamp (e.g. Homematic's `HM_SEEDED_CHANGED_AT = 1`) — never a real
5934
+ * instant. Year 2000 predates every device this addon exports.
5935
+ */
5936
+ var MIN_PLAUSIBLE_TIMESTAMP_MS = Date.UTC(2e3, 0, 1);
5928
5937
  /** The hub's API port — where the data plane is reverse-proxied. */
5929
5938
  var HUB_API_PORT = 4443;
5930
5939
  /** One PTZ button press, as a relative move. */
@@ -6227,9 +6236,10 @@ var HaExportAddon = class extends BaseAddon {
6227
6236
  const detected = slice["detected"];
6228
6237
  const lastDetectedAt = readNumber(slice, "lastDetectedAt");
6229
6238
  if (typeof detected !== "boolean") return;
6239
+ const timestamp = lastDetectedAt === null ? Date.now() : lastDetectedAt >= MIN_PLAUSIBLE_TIMESTAMP_MS ? lastDetectedAt : null;
6230
6240
  this.push(deviceId, projectMotion(deviceKey, {
6231
6241
  detected,
6232
- timestamp: lastDetectedAt ?? Date.now()
6242
+ timestamp
6233
6243
  }));
6234
6244
  }
6235
6245
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homeassistant",
3
- "version": "1.2.143",
3
+ "version": "1.2.144",
4
4
  "description": "Home Assistant device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",