@camstack/addon-provider-homematic 1.2.161 → 1.2.162

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
@@ -5589,7 +5589,7 @@ var ZodIssueCode = {
5589
5589
  var ZodFirstPartyTypeKind;
5590
5590
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5591
5591
  //#endregion
5592
- //#region ../types/dist/sleep-Crlmu_tO.mjs
5592
+ //#region ../types/dist/sleep-C_u6cEAJ.mjs
5593
5593
  /**
5594
5594
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5595
5595
  * window to float samples (D455).
@@ -8982,6 +8982,7 @@ var CAP_NAMES = Object.freeze([
8982
8982
  "image-settings",
8983
8983
  "integrations",
8984
8984
  "intercom",
8985
+ "ir-transmitter",
8985
8986
  "keychain",
8986
8987
  "keychain-admin",
8987
8988
  "lawn-mower-control",
@@ -9010,6 +9011,7 @@ var CAP_NAMES = Object.freeze([
9010
9011
  "network-link",
9011
9012
  "network-quality",
9012
9013
  "node-certificates",
9014
+ "node-removal-hook",
9013
9015
  "nodes",
9014
9016
  "notification-output",
9015
9017
  "notification-rules",
@@ -9040,6 +9042,7 @@ var CAP_NAMES = Object.freeze([
9040
9042
  "recording-onboard",
9041
9043
  "recording-signal",
9042
9044
  "records",
9045
+ "remote",
9043
9046
  "running-state",
9044
9047
  "scene-monitor",
9045
9048
  "script-runner",
@@ -9063,6 +9066,7 @@ var CAP_NAMES = Object.freeze([
9063
9066
  "system",
9064
9067
  "tamper",
9065
9068
  "temperature-sensor",
9069
+ "template",
9066
9070
  "terminal-session",
9067
9071
  "toast",
9068
9072
  "training",
@@ -10003,6 +10007,11 @@ Object.freeze({
10003
10007
  "standby"
10004
10008
  ]
10005
10009
  },
10010
+ {
10011
+ "path": "stateSource",
10012
+ "valueType": "enum",
10013
+ "enumValues": ["reported", "assumed"]
10014
+ },
10006
10015
  {
10007
10016
  "path": "volumeLevel",
10008
10017
  "valueType": "number"
@@ -10229,6 +10238,11 @@ Object.freeze({
10229
10238
  "path": "batteryPower",
10230
10239
  "valueType": "number"
10231
10240
  },
10241
+ {
10242
+ "path": "powerSource",
10243
+ "valueType": "enum",
10244
+ "enumValues": ["mains", "battery"]
10245
+ },
10232
10246
  {
10233
10247
  "path": "desiccantLeftDays",
10234
10248
  "valueType": "number"
@@ -10273,6 +10287,18 @@ Object.freeze({
10273
10287
  {
10274
10288
  "path": "volume",
10275
10289
  "valueType": "number"
10290
+ },
10291
+ {
10292
+ "path": "lastFeedGrams",
10293
+ "valueType": "number"
10294
+ },
10295
+ {
10296
+ "path": "lastFeedHopper1",
10297
+ "valueType": "number"
10298
+ },
10299
+ {
10300
+ "path": "lastFeedHopper2",
10301
+ "valueType": "number"
10276
10302
  }
10277
10303
  ],
10278
10304
  "power-meter": [
@@ -10812,7 +10838,7 @@ object({
10812
10838
  params: record(string(), union([string(), number()])).optional()
10813
10839
  });
10814
10840
  //#endregion
10815
- //#region ../types/dist/err-msg-DIorGYeC.mjs
10841
+ //#region ../types/dist/err-msg-BCdlR2Li.mjs
10816
10842
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10817
10843
  DeviceType["Camera"] = "camera";
10818
10844
  DeviceType["Hub"] = "hub";
@@ -11158,6 +11184,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
11158
11184
  /** Fallback when the notifier service name doesn't match a known
11159
11185
  * pattern. */
11160
11186
  DeviceRole["GenericNotifier"] = "generic-notifier";
11187
+ /** Emits a pulse train (infrared or RF) on request — the `ir-transmitter`
11188
+ * cap. A `DeviceType.Generic` device. */
11189
+ DeviceRole["IrTransmitter"] = "ir-transmitter";
11161
11190
  return DeviceRole;
11162
11191
  }({});
11163
11192
  /**
@@ -11171,7 +11200,7 @@ function errMsg(err) {
11171
11200
  return String(err);
11172
11201
  }
11173
11202
  //#endregion
11174
- //#region ../types/dist/composition-CZImLpOI.mjs
11203
+ //#region ../types/dist/composition-bIBW5_13.mjs
11175
11204
  /**
11176
11205
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11177
11206
  *
@@ -16581,9 +16610,21 @@ var AlertSeveritySchema = _enum([
16581
16610
  "error",
16582
16611
  "critical"
16583
16612
  ]);
16613
+ /**
16614
+ * An action that RUNS something instead of going somewhere: the admin UI calls
16615
+ * the addon's custom action (`addons.custom`) with exactly these fields. The
16616
+ * producer owns the meaning; the UI knows nothing about the addon or the kind.
16617
+ */
16618
+ var AlertActionInvokeSchema = object({
16619
+ addonId: string().min(1),
16620
+ action: string().min(1),
16621
+ input: record(string(), unknown()).optional()
16622
+ });
16623
+ /** An action has `href` (a link) OR `invoke` (a button that runs a custom action). */
16584
16624
  var AlertActionSchema = object({
16585
16625
  label: string(),
16586
16626
  href: string().optional(),
16627
+ invoke: AlertActionInvokeSchema.optional(),
16587
16628
  /**
16588
16629
  * How `href` is followed. Absent / `app`: an admin-ui route (client-side).
16589
16630
  * `server`: a route the HUB serves (an addon HTTP route such as an OAuth
@@ -19238,12 +19279,13 @@ var CHANGED = {
19238
19279
  expr: "now"
19239
19280
  };
19240
19281
  function numeric(input) {
19282
+ const { alsoFrom, ...base } = input;
19241
19283
  return {
19242
- ...input,
19284
+ ...base,
19243
19285
  family: "numeric",
19244
19286
  deviceType: DeviceType.Sensor,
19245
19287
  memberDeviceType: DeviceType.Sensor,
19246
- memberDeviceTypes: [DeviceType.Sensor],
19288
+ memberDeviceTypes: [DeviceType.Sensor, ...alsoFrom?.deviceTypes ?? []],
19247
19289
  activeWhen: void 0,
19248
19290
  commandCaps: [],
19249
19291
  baseUnit: baseUnitOf(input.quantity),
@@ -19251,9 +19293,10 @@ function numeric(input) {
19251
19293
  accept: [{
19252
19294
  cap: input.outputCap,
19253
19295
  fieldPath: input.outputField
19254
- }]
19296
+ }, ...alsoFrom === void 0 ? [] : [alsoFrom.accept]]
19255
19297
  };
19256
19298
  }
19299
+ var CLIMATE_DEVICES = [DeviceType.Thermostat, DeviceType.Climate];
19257
19300
  function binary(input) {
19258
19301
  return {
19259
19302
  ...input,
@@ -19279,7 +19322,14 @@ var GROUP_TEMPLATES = [
19279
19322
  quantity: "temperature",
19280
19323
  defaultAggregate: "mean",
19281
19324
  empty: "null",
19282
- companions: [NOW$2]
19325
+ companions: [NOW$2],
19326
+ alsoFrom: {
19327
+ deviceTypes: CLIMATE_DEVICES,
19328
+ accept: {
19329
+ cap: "climate-control",
19330
+ fieldPath: "currentTemp"
19331
+ }
19332
+ }
19283
19333
  }),
19284
19334
  numeric({
19285
19335
  id: "humidity",
@@ -19290,7 +19340,14 @@ var GROUP_TEMPLATES = [
19290
19340
  quantity: "humidity",
19291
19341
  defaultAggregate: "mean",
19292
19342
  empty: "null",
19293
- companions: [NOW$2]
19343
+ companions: [NOW$2],
19344
+ alsoFrom: {
19345
+ deviceTypes: CLIMATE_DEVICES,
19346
+ accept: {
19347
+ cap: "climate-control",
19348
+ fieldPath: "currentHumidity"
19349
+ }
19350
+ }
19294
19351
  }),
19295
19352
  numeric({
19296
19353
  id: "pressure",
@@ -43257,6 +43314,14 @@ method(object({
43257
43314
  kind: "mutation",
43258
43315
  auth: "admin"
43259
43316
  });
43317
+ method(object({
43318
+ nodeId: string().min(1),
43319
+ /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
43320
+ removedAt: number()
43321
+ }), object({ counts: record(string(), number().int().nonnegative()) }), {
43322
+ kind: "mutation",
43323
+ auth: "admin"
43324
+ });
43260
43325
  /**
43261
43326
  * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
43262
43327
  * so it can delete what it keeps for that device (D865).
@@ -43904,6 +43969,17 @@ var LlmProfileSchema = object({
43904
43969
  * field in its editor only when its descriptor lists it in `profileFields`. */
43905
43970
  topK: number().int().positive().optional(),
43906
43971
  maxTokens: number().int().positive().optional(),
43972
+ /** How hard the model "thinks" before it answers. Omitted = the model's own
43973
+ * default. `off` matters for structured output: a thinking model can spend
43974
+ * the whole completion on reasoning and return an empty `content`. Carried
43975
+ * only by the wires that have it — a kind offers the field in its editor
43976
+ * only when its descriptor lists it in `profileFields`. */
43977
+ reasoningEffort: _enum([
43978
+ "off",
43979
+ "low",
43980
+ "medium",
43981
+ "high"
43982
+ ]).optional(),
43907
43983
  /** Prompt context window. Advisory for cloud kinds (they enforce their own). */
43908
43984
  contextLength: number().int().positive().optional(),
43909
43985
  /** Default system prompt. A caller's `system` REPLACES it (never appends —
@@ -43942,6 +44018,7 @@ var LlmProfileFieldSchema = _enum([
43942
44018
  "topP",
43943
44019
  "topK",
43944
44020
  "maxTokens",
44021
+ "reasoningEffort",
43945
44022
  "contextLength",
43946
44023
  "systemPrompt",
43947
44024
  "timeoutMs",
@@ -45192,6 +45269,43 @@ var NotificationActionSchema = object({
45192
45269
  */
45193
45270
  mode: _enum(["navigate", "background"]).optional()
45194
45271
  });
45272
+ /** A delivery parameter value: what a notifier declared it takes. */
45273
+ var DeliveryParamValueSchema = union([
45274
+ string(),
45275
+ number(),
45276
+ boolean()
45277
+ ]);
45278
+ /**
45279
+ * One extra delivery parameter a notifier KIND declares (D938). A rule that
45280
+ * addresses a target of that kind may set it; a kind that declares nothing is
45281
+ * never handed one. The admin UI derives its control from this descriptor —
45282
+ * no editor knows a service by name.
45283
+ *
45284
+ * Optional fields are `.optional()`, never `.default()` (a Zod default does not
45285
+ * run on the addon cap path).
45286
+ */
45287
+ var DeliveryParamSchema = object({
45288
+ key: string().min(1),
45289
+ label: string(),
45290
+ /** i18n key of the label, resolved by the declaring addon's namespace. */
45291
+ labelKey: string().optional(),
45292
+ type: _enum([
45293
+ "enum",
45294
+ "number",
45295
+ "boolean",
45296
+ "string"
45297
+ ]),
45298
+ /** `enum` only: the closed set of accepted values. */
45299
+ options: array(object({
45300
+ value: string(),
45301
+ label: string()
45302
+ })).optional(),
45303
+ /** `number` only. */
45304
+ min: number().optional(),
45305
+ max: number().optional(),
45306
+ /** What the notifier does when the rule sets nothing; shown as the placeholder. */
45307
+ default: DeliveryParamValueSchema.optional()
45308
+ });
45195
45309
  /**
45196
45310
  * The canonical notification. `body` is the only hard field (Apprise model).
45197
45311
  * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
@@ -45214,7 +45328,13 @@ var NotificationSchema = object({
45214
45328
  tag: string().optional(),
45215
45329
  deviceId: number().optional(),
45216
45330
  eventId: string().optional(),
45217
- metadata: record(string(), unknown()).optional()
45331
+ metadata: record(string(), unknown()).optional(),
45332
+ /**
45333
+ * Values for the parameters the target's kind DECLARES in
45334
+ * {@link TargetKindSchema} `deliveryParams`. Already validated against the
45335
+ * declaration by the sender; a notifier reads only the keys it declared.
45336
+ */
45337
+ delivery: record(string(), DeliveryParamValueSchema).optional()
45218
45338
  });
45219
45339
  /** One declared native severity/priority level for a kind. */
45220
45340
  var TargetKindLevelSchema = object({
@@ -45320,6 +45440,11 @@ var TargetKindSchema = object({
45320
45440
  */
45321
45441
  destinationKeys: array(string()).optional(),
45322
45442
  supportsDiscovery: boolean(),
45443
+ /**
45444
+ * Extra delivery parameters a rule may set for a target of this kind (D938).
45445
+ * Absent = none; the rule editor then offers nothing.
45446
+ */
45447
+ deliveryParams: array(DeliveryParamSchema).optional(),
45323
45448
  caps: TargetKindCapsSchema
45324
45449
  });
45325
45450
  /**
@@ -57611,7 +57736,9 @@ var ManifestShapeSchema = object({
57611
57736
  icon: string().optional()
57612
57737
  }).optional(),
57613
57738
  protected: boolean().optional(),
57614
- removable: boolean().optional()
57739
+ removable: boolean().optional(),
57740
+ /** Declared Integrations-page block (mirror of `AddonDeclaration.integrationSection`). */
57741
+ integrationSection: _enum(["system", "providers"]).optional()
57615
57742
  });
57616
57743
  var AddonProcessSchema = object({
57617
57744
  pid: number().optional(),
@@ -58740,11 +58867,373 @@ onBrightnessChanged: { data: object({
58740
58867
  minIntervalMs: 0
58741
58868
  } } }
58742
58869
  };
58870
+ /**
58871
+ * The role vocabulary of a remote (D963): what a button MEANS, independent of
58872
+ * how it is wired. A layout template places buttons by role; a device binds a
58873
+ * role to an action; an exporter asks for a role by name. Roles are shared so
58874
+ * one layout fits every appliance and one voice command fits every binding.
58875
+ *
58876
+ * A role outside this list is `custom:<key>` — the operator's own button (a
58877
+ * SmartIR source, a channel preset). The key is a lowercase slug.
58878
+ */
58879
+ var REMOTE_KNOWN_ROLES = [
58880
+ "power",
58881
+ "power_on",
58882
+ "power_off",
58883
+ "volume_up",
58884
+ "volume_down",
58885
+ "mute",
58886
+ "channel_up",
58887
+ "channel_down",
58888
+ "up",
58889
+ "down",
58890
+ "left",
58891
+ "right",
58892
+ "ok",
58893
+ "back",
58894
+ "home",
58895
+ "menu",
58896
+ "info",
58897
+ "guide",
58898
+ "exit",
58899
+ "input",
58900
+ "play",
58901
+ "pause",
58902
+ "play_pause",
58903
+ "stop",
58904
+ "next",
58905
+ "previous",
58906
+ "fast_forward",
58907
+ "rewind",
58908
+ "digit_0",
58909
+ "digit_1",
58910
+ "digit_2",
58911
+ "digit_3",
58912
+ "digit_4",
58913
+ "digit_5",
58914
+ "digit_6",
58915
+ "digit_7",
58916
+ "digit_8",
58917
+ "digit_9"
58918
+ ];
58919
+ var CUSTOM_KEY_RE = /^[a-z0-9][a-z0-9_]{0,47}$/;
58920
+ var KNOWN_ROLES = new Set(REMOTE_KNOWN_ROLES);
58921
+ function isRemoteKnownRole(value) {
58922
+ return KNOWN_ROLES.has(value);
58923
+ }
58924
+ function isRemoteCustomKey(key) {
58925
+ return CUSTOM_KEY_RE.test(key);
58926
+ }
58927
+ /** The key of a `custom:<key>` role, or null for any other string. */
58928
+ function customKeyOfRole(role) {
58929
+ if (!role.startsWith("custom:")) return null;
58930
+ const key = role.slice(7);
58931
+ return isRemoteCustomKey(key) ? key : null;
58932
+ }
58933
+ function isRemoteRole(value) {
58934
+ return isRemoteKnownRole(value) || customKeyOfRole(value) !== null;
58935
+ }
58936
+ var RemoteRoleSchema = string().refine(isRemoteRole, { message: "a role is one of the known roles or custom:<key> (lowercase letters, digits, _)" });
58937
+ var RemoteBindingSchema = object({ action: AutomationCapStepSchema.extend({ deviceId: number().int().nonnegative() }) });
58938
+ var RemoteCustomButtonSchema = object({
58939
+ /** The slug after `custom:` in the role. */
58940
+ key: string().refine(isRemoteCustomKey, { message: "a lowercase slug" }),
58941
+ /** What the operator calls it, and what a voice command may name. */
58942
+ name: string().min(1).max(60)
58943
+ });
58944
+ var RemotePowerModeSchema = _enum(["toggle", "discrete"]);
58945
+ var RemoteConfigSchema = object({
58946
+ layoutTemplateId: string().min(1).nullable(),
58947
+ /**
58948
+ * The transmitter device new bindings and an import send through. A binding
58949
+ * names its own device, so one command may override it.
58950
+ */
58951
+ defaultTransmitterId: number().int().nonnegative().nullable().default(null),
58952
+ /** Keyed by role. A role with no entry falls back to the native cap. */
58953
+ bindings: record(string(), RemoteBindingSchema),
58954
+ customButtons: array(RemoteCustomButtonSchema).max(100),
58955
+ power: object({ mode: RemotePowerModeSchema })
58956
+ }).superRefine((config, ctx) => {
58957
+ const roles = Object.keys(config.bindings);
58958
+ if (roles.length > 200) ctx.addIssue({
58959
+ code: "custom",
58960
+ message: "too many bindings",
58961
+ path: ["bindings"]
58962
+ });
58963
+ for (const role of roles) if (!isRemoteRole(role)) ctx.addIssue({
58964
+ code: "custom",
58965
+ message: `"${role}" is not a role`,
58966
+ path: ["bindings", role]
58967
+ });
58968
+ const keys = /* @__PURE__ */ new Set();
58969
+ config.customButtons.forEach((button, index) => {
58970
+ if (keys.has(button.key)) ctx.addIssue({
58971
+ code: "custom",
58972
+ message: `custom button "${button.key}" is listed twice`,
58973
+ path: [
58974
+ "customButtons",
58975
+ index,
58976
+ "key"
58977
+ ]
58978
+ });
58979
+ keys.add(button.key);
58980
+ });
58981
+ for (const role of roles) {
58982
+ const key = customKeyOfRole(role);
58983
+ if (key !== null && !keys.has(key)) ctx.addIssue({
58984
+ code: "custom",
58985
+ message: `${role} is bound but has no entry in customButtons`,
58986
+ path: ["bindings", role]
58987
+ });
58988
+ }
58989
+ });
58990
+ object({
58991
+ power: object({
58992
+ state: _enum(["on", "off"]),
58993
+ at: number()
58994
+ }).nullable(),
58995
+ muted: object({
58996
+ value: boolean(),
58997
+ at: number()
58998
+ }).nullable()
58999
+ });
59000
+ var RemoteLayoutButtonSchema = object({
59001
+ id: string().min(1).max(40),
59002
+ /** 0-based grid position. */
59003
+ row: number().int().min(0).max(24),
59004
+ col: number().int().min(0).max(24),
59005
+ rowSpan: number().int().min(1).max(24),
59006
+ colSpan: number().int().min(1).max(24),
59007
+ icon: string().min(1).max(60).optional(),
59008
+ label: string().min(1).max(40).optional(),
59009
+ role: RemoteRoleSchema
59010
+ });
59011
+ object({
59012
+ columns: number().int().min(1).max(24),
59013
+ rows: number().int().min(1).max(24),
59014
+ buttons: array(RemoteLayoutButtonSchema).max(200)
59015
+ }).superRefine((grid, ctx) => {
59016
+ const ids = /* @__PURE__ */ new Set();
59017
+ const taken = /* @__PURE__ */ new Map();
59018
+ grid.buttons.forEach((button, index) => {
59019
+ if (ids.has(button.id)) ctx.addIssue({
59020
+ code: "custom",
59021
+ message: `button id "${button.id}" is used twice`,
59022
+ path: [
59023
+ "buttons",
59024
+ index,
59025
+ "id"
59026
+ ]
59027
+ });
59028
+ ids.add(button.id);
59029
+ if (button.row + button.rowSpan > grid.rows || button.col + button.colSpan > grid.columns) {
59030
+ ctx.addIssue({
59031
+ code: "custom",
59032
+ message: `button "${button.id}" leaves the grid`,
59033
+ path: ["buttons", index]
59034
+ });
59035
+ return;
59036
+ }
59037
+ for (let r = button.row; r < button.row + button.rowSpan; r += 1) for (let c = button.col; c < button.col + button.colSpan; c += 1) {
59038
+ const cell = `${r}:${c}`;
59039
+ const other = taken.get(cell);
59040
+ if (other !== void 0) {
59041
+ ctx.addIssue({
59042
+ code: "custom",
59043
+ message: `buttons "${other}" and "${button.id}" overlap`,
59044
+ path: ["buttons", index]
59045
+ });
59046
+ return;
59047
+ }
59048
+ taken.set(cell, button.id);
59049
+ }
59050
+ });
59051
+ });
59052
+ var RemoteBoundCommandSchema = object({
59053
+ role: RemoteRoleSchema,
59054
+ /** The operator's name for a custom button; absent for a known role. */
59055
+ label: string().optional(),
59056
+ custom: boolean()
59057
+ });
59058
+ var RemoteCommandListSchema = object({
59059
+ layoutTemplateId: string().nullable(),
59060
+ power: object({ mode: RemotePowerModeSchema }),
59061
+ /** Only the roles that are BOUND — the ones a press can reach. */
59062
+ commands: array(RemoteBoundCommandSchema)
59063
+ });
59064
+ var RemoteRefusalReasonSchema = _enum([
59065
+ "no-remote",
59066
+ "unbound-role",
59067
+ "action-failed"
59068
+ ]);
59069
+ var RemoteSendResultSchema = discriminatedUnion("status", [
59070
+ object({
59071
+ status: literal("sent"),
59072
+ /** How many times the action ran. */
59073
+ sends: number().int().min(1)
59074
+ }),
59075
+ object({
59076
+ status: literal("skipped"),
59077
+ reason: literal("already-in-state")
59078
+ }),
59079
+ object({
59080
+ status: literal("refused"),
59081
+ reason: RemoteRefusalReasonSchema,
59082
+ detail: string().optional()
59083
+ })
59084
+ ]);
59085
+ var RemoteMediaCommandSchema = _enum([
59086
+ "play",
59087
+ "pause",
59088
+ "stop",
59089
+ "next",
59090
+ "previous",
59091
+ "setMute",
59092
+ "selectSource"
59093
+ ]);
59094
+ /** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
59095
+ var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
59096
+ var RemoteSetConfigResultSchema = discriminatedUnion("status", [object({ status: literal("saved") }), object({
59097
+ status: literal("refused"),
59098
+ reason: _enum(["unknown-template", "not-a-media-player"]),
59099
+ detail: string()
59100
+ })]);
59101
+ var deviceInput = object({ deviceId: number().int().nonnegative() });
59102
+ DeviceType.MediaPlayer;
59103
+ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
59104
+ kind: "mutation",
59105
+ auth: "admin"
59106
+ }), method(deviceInput, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput.extend({
59107
+ role: RemoteRoleSchema,
59108
+ /** Sends in a row (volume steps). */
59109
+ repeat: number().int().min(1).max(50).default(1)
59110
+ }), RemoteSendResultSchema, {
59111
+ kind: "mutation",
59112
+ auth: "protected"
59113
+ }), method(deviceInput.extend({
59114
+ command: RemoteMediaCommandSchema,
59115
+ muted: boolean().optional(),
59116
+ source: string().optional(),
59117
+ repeat: number().int().min(1).max(50).default(1)
59118
+ }), RemoteMediaCommandResultSchema, {
59119
+ kind: "mutation",
59120
+ auth: "protected"
59121
+ });
59122
+ var TemplateKindSchema = _enum(["remote"]);
59123
+ /**
59124
+ * template — the generic store of reusable, named operator data (D963). A
59125
+ * record is `{ id, kind, name, payload }`; the kinds (today only `remote`, the
59126
+ * button grid of a remote) and the schema each payload must satisfy are
59127
+ * declared by the system in `template-kinds.ts`. Nothing here is specific to a
59128
+ * kind: the engine validates a payload against its kind's schema and, on a
59129
+ * delete, asks the kind's owner whether anything still points at the record.
59130
+ *
59131
+ * Every refusal is a typed result — a UI says "unknown kind" or names the
59132
+ * devices that still use a template, without parsing text.
59133
+ */
59134
+ var TemplateRecordSchema = object({
59135
+ id: string().min(1),
59136
+ kind: TemplateKindSchema,
59137
+ name: string().min(1).max(120),
59138
+ /** Shaped by the kind; `checkTemplatePayload(kind, payload)` reads it. */
59139
+ payload: unknown(),
59140
+ createdAt: number(),
59141
+ updatedAt: number()
59142
+ });
59143
+ var TemplateWriteResultSchema = discriminatedUnion("status", [object({
59144
+ status: literal("saved"),
59145
+ template: TemplateRecordSchema
59146
+ }), object({
59147
+ status: literal("refused"),
59148
+ reason: _enum(["invalid-payload", "not-found"]),
59149
+ detail: string()
59150
+ })]);
59151
+ /** A device still pointing at a template. */
59152
+ var TemplateUserSchema = object({
59153
+ deviceId: number().int().nonnegative(),
59154
+ name: string()
59155
+ });
59156
+ var TemplateDeleteResultSchema = discriminatedUnion("status", [
59157
+ object({ status: literal("deleted") }),
59158
+ object({ status: literal("not-found") }),
59159
+ object({
59160
+ status: literal("in-use"),
59161
+ devices: array(TemplateUserSchema).min(1)
59162
+ })
59163
+ ]);
59164
+ method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSchema), { auth: "protected" }), method(object({ id: string().min(1) }), TemplateRecordSchema.nullable(), { auth: "protected" }), method(object({
59165
+ kind: TemplateKindSchema,
59166
+ name: string().min(1).max(120),
59167
+ payload: unknown()
59168
+ }), TemplateWriteResultSchema, {
59169
+ kind: "mutation",
59170
+ auth: "admin"
59171
+ }), method(object({
59172
+ id: string().min(1),
59173
+ name: string().min(1).max(120).optional(),
59174
+ payload: unknown().optional()
59175
+ }), TemplateWriteResultSchema, {
59176
+ kind: "mutation",
59177
+ auth: "admin"
59178
+ }), method(object({ id: string().min(1) }), TemplateDeleteResultSchema, {
59179
+ kind: "mutation",
59180
+ auth: "admin"
59181
+ });
58743
59182
  DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
58744
59183
  kind: "mutation",
58745
59184
  auth: "protected"
58746
59185
  });
58747
59186
  /**
59187
+ * A pulse train in signed microseconds: mark (+) first, then space (-), strictly
59188
+ * alternating, no zeros, 2..1024 entries.
59189
+ */
59190
+ var IrTimingsSchema = array(number().int()).min(2).max(1024).superRefine((timings, ctx) => {
59191
+ timings.forEach((value, index) => {
59192
+ const expectMark = index % 2 === 0;
59193
+ if (value === 0 || value > 0 !== expectMark) ctx.addIssue({
59194
+ code: "custom",
59195
+ message: `timing ${index} must be a non-zero ${expectMark ? "mark (positive)" : "space (negative)"}`,
59196
+ path: [index]
59197
+ });
59198
+ });
59199
+ });
59200
+ /** Per-command transmit options. Every key is optional; a transmitter applies what it can. */
59201
+ var TransmitSettingsSchema = object({
59202
+ carrierHz: number().int().positive().optional(),
59203
+ dutyCyclePercent: number().int().min(1).max(100).optional(),
59204
+ repeat: number().int().min(0).max(20).optional(),
59205
+ repeatDelayMs: number().int().min(0).optional()
59206
+ });
59207
+ /** The setting keys a transmitter may declare as settable (`getInfo.settable`). */
59208
+ var TransmitSettingKeySchema = _enum([
59209
+ "carrierHz",
59210
+ "dutyCyclePercent",
59211
+ "repeat",
59212
+ "repeatDelayMs"
59213
+ ]);
59214
+ var IrMediumSchema = _enum(["ir", "rf"]);
59215
+ var IrSendResultSchema = object({
59216
+ ok: literal(true),
59217
+ /** Settings the transmitter could not apply; the train was still sent. */
59218
+ ignoredSettings: array(string())
59219
+ });
59220
+ var IrTransmitterInfoSchema = object({
59221
+ media: array(IrMediumSchema),
59222
+ settable: array(TransmitSettingKeySchema),
59223
+ maxTimings: number().int().positive()
59224
+ });
59225
+ DeviceType.Generic, method(object({
59226
+ deviceId: number().int().nonnegative(),
59227
+ timings: IrTimingsSchema,
59228
+ settings: TransmitSettingsSchema.optional()
59229
+ }), IrSendResultSchema, {
59230
+ kind: "mutation",
59231
+ auth: "protected"
59232
+ }), method(object({ deviceId: number().int().nonnegative() }), IrTransmitterInfoSchema, {
59233
+ kind: "query",
59234
+ auth: "protected"
59235
+ });
59236
+ /**
58748
59237
  * `calendar-source` — collection cap exposing per-provider calendars and
58749
59238
  * their events. Every addon that contributes calendars (Home Assistant,
58750
59239
  * a future Google Calendar addon, …) registers a provider here. The hub
@@ -62841,9 +63330,35 @@ var MediaInfoSchema = object({
62841
63330
  /** Optional cover-art / thumbnail URL. */
62842
63331
  imageUrl: string().optional()
62843
63332
  });
63333
+ /**
63334
+ * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
63335
+ * the device said so. `assumed`: the driver has no feedback (infrared) and
63336
+ * tracks what it last SENT; it moves only after a send was accepted, and a
63337
+ * human at the real remote makes it wrong. A UI shows an assumed value as a
63338
+ * guess, never as a reading.
63339
+ */
63340
+ var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
63341
+ /** The fields of a media-player status a driver can only guess. */
63342
+ var MediaPlayerAssumedFieldSchema = _enum([
63343
+ "state",
63344
+ "volumeLevel",
63345
+ "isMuted",
63346
+ "source"
63347
+ ]);
62844
63348
  var MediaPlayerStatusSchema = object({
62845
63349
  /** Playback lifecycle state. */
62846
63350
  state: MediaPlayerStateSchema,
63351
+ /**
63352
+ * Whether the state fields are readings or the driver's own bookkeeping.
63353
+ * A slice written before this field existed parses as `reported`.
63354
+ */
63355
+ stateSource: MediaPlayerStateSourceSchema.default("reported"),
63356
+ /**
63357
+ * Which fields are the driver's own guess. `stateSource` is `assumed` when
63358
+ * this is not empty; a field not listed here is a reading even then (power
63359
+ * from a running-state source, mute guessed from the last send).
63360
+ */
63361
+ assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
62847
63362
  /** Volume as 0..100 inclusive. Null when the device has no volume
62848
63363
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
62849
63364
  volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -64771,9 +65286,14 @@ var PetFeederStatusSchema = object({
64771
65286
  * (`device_class: problem`, on = low). True when the tank reports empty,
64772
65287
  * or, on a dual-hopper model, when EITHER hopper is empty. */
64773
65288
  lowFood: boolean(),
64774
- /** Battery charge 0..100 (%). Null on mains-powered models or when the
64775
- * device has no battery reading. */
65289
+ /** Battery charge 0..100 (%). On a feeder running on `mains` this is the
65290
+ * BACKUP battery's charge, not the power source. Null when the device has
65291
+ * no battery reading. */
64776
65292
  batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
65293
+ /** What the feeder is powered from right now: `mains` (plugged in, the
65294
+ * battery is only a backup) or `battery`. Null when the device does not
65295
+ * report it. */
65296
+ powerSource: _enum(["mains", "battery"]).nullable(),
64777
65297
  /**
64778
65298
  * Desiccant life remaining, stored in seconds (duration base, D944).
64779
65299
  * Providers convert from the days the feeder reports. Null when the
@@ -64801,6 +65321,14 @@ var PetFeederStatusSchema = object({
64801
65321
  feedSound: boolean(),
64802
65322
  /** Speaker / prompt volume level (device-scaled integer). */
64803
65323
  volume: number().meta(readingMeta("none")),
65324
+ /** Portion (grams) of the last manual feed sent from a single-hopper
65325
+ * stepper — the default the next manual feed starts from. Persisted per
65326
+ * device by the provider; null until a manual feed has been sent. */
65327
+ lastFeedGrams: gramsPortion.nullable(),
65328
+ /** Hopper-1 portion of the last manual feed (dual-hopper); null until sent. */
65329
+ lastFeedHopper1: gramsPortion.nullable(),
65330
+ /** Hopper-2 portion of the last manual feed (dual-hopper); null until sent. */
65331
+ lastFeedHopper2: gramsPortion.nullable(),
64804
65332
  /** Ms epoch when the slice was last refreshed from the cloud. */
64805
65333
  lastFetchedAt: number()
64806
65334
  });
@@ -70838,6 +71366,14 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
70838
71366
  kind: "mutation",
70839
71367
  auth: "admin"
70840
71368
  });
71369
+ discriminatedUnion("kind", [object({
71370
+ kind: literal("raw"),
71371
+ timings: array(number().int()),
71372
+ settings: TransmitSettingsSchema.optional()
71373
+ }), object({
71374
+ kind: literal("pronto"),
71375
+ code: string()
71376
+ })]);
70841
71377
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
70842
71378
  /**
70843
71379
  * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
@@ -71223,7 +71759,7 @@ function tryConvertUnit(value, from, to) {
71223
71759
  return fromCanonical(toCanonical(value, fromSpec), toSpec);
71224
71760
  }
71225
71761
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
71226
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
71762
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
71227
71763
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
71228
71764
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
71229
71765
  new Set(CAP_NAMES);
@@ -76376,6 +76912,20 @@ Object.freeze({
76376
76912
  access: "create",
76377
76913
  role: "viewer"
76378
76914
  },
76915
+ "irTransmitter.getInfo": {
76916
+ capName: "ir-transmitter",
76917
+ capScope: "device",
76918
+ addonId: null,
76919
+ access: "view",
76920
+ role: "viewer"
76921
+ },
76922
+ "irTransmitter.send": {
76923
+ capName: "ir-transmitter",
76924
+ capScope: "device",
76925
+ addonId: null,
76926
+ access: "create",
76927
+ role: "viewer"
76928
+ },
76379
76929
  "keychain.delete": {
76380
76930
  capName: "keychain",
76381
76931
  capScope: "system",
@@ -77608,6 +78158,13 @@ Object.freeze({
77608
78158
  access: "create",
77609
78159
  role: "admin"
77610
78160
  },
78161
+ "nodeRemovalHook.onNodeRemoved": {
78162
+ capName: "node-removal-hook",
78163
+ capScope: "system",
78164
+ addonId: null,
78165
+ access: "create",
78166
+ role: "admin"
78167
+ },
77611
78168
  "nodes.clusterAddonStatus": {
77612
78169
  capName: "nodes",
77613
78170
  capScope: "system",
@@ -80499,6 +81056,41 @@ Object.freeze({
80499
81056
  access: "create",
80500
81057
  role: "admin"
80501
81058
  },
81059
+ "remote.getConfig": {
81060
+ capName: "remote",
81061
+ capScope: "system",
81062
+ addonId: null,
81063
+ access: "view",
81064
+ role: "admin"
81065
+ },
81066
+ "remote.listCommands": {
81067
+ capName: "remote",
81068
+ capScope: "system",
81069
+ addonId: null,
81070
+ access: "view",
81071
+ role: "admin"
81072
+ },
81073
+ "remote.sendCommand": {
81074
+ capName: "remote",
81075
+ capScope: "system",
81076
+ addonId: null,
81077
+ access: "create",
81078
+ role: "admin"
81079
+ },
81080
+ "remote.sendMediaCommand": {
81081
+ capName: "remote",
81082
+ capScope: "system",
81083
+ addonId: null,
81084
+ access: "create",
81085
+ role: "admin"
81086
+ },
81087
+ "remote.setConfig": {
81088
+ capName: "remote",
81089
+ capScope: "system",
81090
+ addonId: null,
81091
+ access: "create",
81092
+ role: "admin"
81093
+ },
80502
81094
  "runningState.getStatus": {
80503
81095
  capName: "running-state",
80504
81096
  capScope: "device",
@@ -81689,6 +82281,41 @@ Object.freeze({
81689
82281
  access: "view",
81690
82282
  role: "viewer"
81691
82283
  },
82284
+ "template.create": {
82285
+ capName: "template",
82286
+ capScope: "system",
82287
+ addonId: null,
82288
+ access: "create",
82289
+ role: "admin"
82290
+ },
82291
+ "template.delete": {
82292
+ capName: "template",
82293
+ capScope: "system",
82294
+ addonId: null,
82295
+ access: "delete",
82296
+ role: "admin"
82297
+ },
82298
+ "template.get": {
82299
+ capName: "template",
82300
+ capScope: "system",
82301
+ addonId: null,
82302
+ access: "view",
82303
+ role: "admin"
82304
+ },
82305
+ "template.list": {
82306
+ capName: "template",
82307
+ capScope: "system",
82308
+ addonId: null,
82309
+ access: "view",
82310
+ role: "admin"
82311
+ },
82312
+ "template.update": {
82313
+ capName: "template",
82314
+ capScope: "system",
82315
+ addonId: null,
82316
+ access: "create",
82317
+ role: "admin"
82318
+ },
81692
82319
  "terminalSession.adoptLegacyMonitor": {
81693
82320
  capName: "terminal-session",
81694
82321
  capScope: "system",
@@ -83999,6 +84626,11 @@ var ACTUATOR_CAP_VOCABULARY = {
83999
84626
  icon: "circle-dot"
84000
84627
  }]
84001
84628
  },
84629
+ "ir-transmitter": {
84630
+ scope: "device",
84631
+ noun: t("IR / RF transmitter", "Trasmettitore IR / RF"),
84632
+ notOperatorFacing: { send: "a raw pulse train is not something an operator composes in an automation; the remote that owns the command table sends it" }
84633
+ },
84002
84634
  control: {
84003
84635
  scope: "device",
84004
84636
  noun: t("Control", "Controllo"),
@@ -86476,6 +87108,7 @@ var SYSTEM_CAP_VOCABULARY = {
86476
87108
  "device-manager": system("the device registry itself — rename, move and remove are administration"),
86477
87109
  "device-provider": system(INTERNAL),
86478
87110
  "device-removal-hook": system(INTERNAL),
87111
+ "node-removal-hook": system(INTERNAL),
86479
87112
  "device-state": system("the runtime-state mirror every trigger already reads through"),
86480
87113
  "display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
86481
87114
  "embedding-encoder": system(ML),
@@ -86515,6 +87148,7 @@ var SYSTEM_CAP_VOCABULARY = {
86515
87148
  "recording-archive": system(STORAGE),
86516
87149
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
86517
87150
  records: system(INTERNAL),
87151
+ remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
86518
87152
  "server-management": system(CLUSTER),
86519
87153
  "settings-store": system(INTERNAL),
86520
87154
  "smtp-provider": system(INTERNAL),
@@ -86527,6 +87161,7 @@ var SYSTEM_CAP_VOCABULARY = {
86527
87161
  "storage-provider": system(STORAGE),
86528
87162
  "stream-broker": system(PIPELINE),
86529
87163
  system: system(CLUSTER),
87164
+ template: system("operator-authored templates — edited in their own editor, never actuated"),
86530
87165
  "terminal-session": system(CLUSTER),
86531
87166
  toast: system(UI_PLUMBING),
86532
87167
  training: system(ML),
@@ -87718,6 +88353,16 @@ Object.freeze({
87718
88353
  form: "single",
87719
88354
  optional: false
87720
88355
  }],
88356
+ "irTransmitter.getInfo": [{
88357
+ name: "deviceId",
88358
+ form: "single",
88359
+ optional: false
88360
+ }],
88361
+ "irTransmitter.send": [{
88362
+ name: "deviceId",
88363
+ form: "single",
88364
+ optional: false
88365
+ }],
87721
88366
  "keychain.deleteDeviceScope": [{
87722
88367
  name: "deviceId",
87723
88368
  form: "single",
@@ -88943,6 +89588,31 @@ Object.freeze({
88943
89588
  form: "single",
88944
89589
  optional: true
88945
89590
  }],
89591
+ "remote.getConfig": [{
89592
+ name: "deviceId",
89593
+ form: "single",
89594
+ optional: false
89595
+ }],
89596
+ "remote.listCommands": [{
89597
+ name: "deviceId",
89598
+ form: "single",
89599
+ optional: false
89600
+ }],
89601
+ "remote.sendCommand": [{
89602
+ name: "deviceId",
89603
+ form: "single",
89604
+ optional: false
89605
+ }],
89606
+ "remote.sendMediaCommand": [{
89607
+ name: "deviceId",
89608
+ form: "single",
89609
+ optional: false
89610
+ }],
89611
+ "remote.setConfig": [{
89612
+ name: "deviceId",
89613
+ form: "single",
89614
+ optional: false
89615
+ }],
88946
89616
  "runningState.getStatus": [{
88947
89617
  name: "deviceId",
88948
89618
  form: "single",
@@ -90893,6 +91563,14 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
90893
91563
  String.raw`(?<=^|["'(\s>,\[{])`;
90894
91564
  var MB = 1024 * 1024;
90895
91565
  1024 * MB, 3072 * MB;
91566
+ object({
91567
+ manufacturer: string().optional(),
91568
+ supportedModels: array(string()).optional(),
91569
+ supportedController: string().optional(),
91570
+ commandsEncoding: string(),
91571
+ delay: number().nonnegative().optional(),
91572
+ commands: record(string(), unknown())
91573
+ });
90896
91574
  //#endregion
90897
91575
  //#region src/callback-host.ts
90898
91576
  /**
@@ -115656,6 +116334,14 @@ var HmChildDevice = class extends BaseDevice {
115656
116334
  this.valueChangedUnsub = facade.on("valueChanged", (event) => {
115657
116335
  if (event.device !== this.address) return;
115658
116336
  if (!this.ownsEventChannel(event)) return;
116337
+ this.ctx.logger.debug("Homematic value change accepted", {
116338
+ tags: { deviceId: this.ctx.id },
116339
+ meta: {
116340
+ address: this.address,
116341
+ channel: event.channel,
116342
+ parameter: event.parameter
116343
+ }
116344
+ });
115659
116345
  handle(event, "live");
115660
116346
  });
115661
116347
  this.seedTriggerUnsubs = [facade.on("ready", () => this.seedCurrentValues()), facade.on("connection", () => this.seedCurrentValues())];
@@ -115730,13 +116416,33 @@ var HmChildDevice = class extends BaseDevice {
115730
116416
  if (status.includes(event.channel)) return true;
115731
116417
  return this.resolveHmDevice()?.channels.some((c) => status.includes(c.address) && (c.dataPoints ?? []).some((d) => d.parameter === event.parameter)) !== true;
115732
116418
  }
116419
+ /** A value change the filter refused — named, per reason, on the record. */
116420
+ logValueDropped(event, reason) {
116421
+ this.ctx.logger.debug("Homematic value change dropped", {
116422
+ tags: { deviceId: this.ctx.id },
116423
+ meta: {
116424
+ address: this.address,
116425
+ ownedChannel: this.channel,
116426
+ channel: event.channel,
116427
+ parameter: event.parameter,
116428
+ reason
116429
+ }
116430
+ });
116431
+ }
115733
116432
  ownsEventChannel(event) {
115734
116433
  const owned = this.channel;
115735
116434
  if (owned === void 0) return true;
115736
116435
  const channel = event.channel;
115737
116436
  if (typeof channel === "string" && channel.length > 0) {
115738
- if (!this.ownedChannels().includes(channel)) return false;
115739
- return this.isPreferredSource(event);
116437
+ if (!this.ownedChannels().includes(channel)) {
116438
+ this.logValueDropped(event, "foreign-channel");
116439
+ return false;
116440
+ }
116441
+ if (!this.isPreferredSource(event)) {
116442
+ this.logValueDropped(event, "not-status-source");
116443
+ return false;
116444
+ }
116445
+ return true;
115740
116446
  }
115741
116447
  this.ctx.logger.warn("Homematic value event without a channel — dropped", {
115742
116448
  tags: { deviceId: this.ctx.id },