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