hm-simulator 1.3.1 → 1.3.2

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/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes of hm-simulator. The release workflow takes each version's section from here for the
4
4
  GitHub release notes.
5
5
 
6
+ ## 1.3.2
7
+
8
+ - `getParamsetDescription` of an address the interface does not know answers the `unknownInstance`
9
+ fault - `-2 Invalid device` under hmipserver's default table, `-2 Unknown instance` under rfd's -
10
+ as both interface processes do on 3.89.11; it answered `''`. A paramset the channel does not list
11
+ answers the new table entry `unknownParamsetDescription` (`-3 Unknown Paramset` / `-3 Unknown
12
+ paramset`, measured); a paramset the channel has but the description set lacks still answers `''`
13
+ (B-5, found through node-red-contrib-ccu's handling of the fault on a real CCU).
14
+ - `getLinkPeers` of a device address faults on `rfd` with the `notSupported` entry (`-1 Failure`
15
+ under rfd's table, as rfd 3.89.11 answers; `-1 Generic error` under the default one), where it
16
+ answered the peers of all channels; on hmipserver and the other interfaces it answers the channels'
17
+ lists one after the other, a peer linked with two of the channels twice, as hmipserver 3.89.11 does
18
+ (B-4).
19
+
6
20
  ## 1.3.1
7
21
 
8
22
  - `getLinks` without an address reports `FLAGS: 1` (`SENDER_BROKEN`) on every device-internal link of
package/README.md CHANGED
@@ -496,8 +496,13 @@ information there. Asked for one address, `getLinks("ABC0000001:1", 0)` and `get
496
496
  0)` answer the same link with `FLAGS: 0`, and `getLinkPeers` names the own channel once. The simulator
497
497
  does the same on `rfd`: `addLink` stores `FLAGS: 0`, the unfiltered list adds bit 1 to internal links
498
498
  (seeded flags are kept and or-ed), the filtered one reports what is stored. hmipserver is not
499
- measured and reports the stored flags. rfd also refuses `getLinkPeers` of a device address (`-1
500
- Failure`); the simulator answers the peers of all its channels.
499
+ measured and reports the stored flags.
500
+
501
+ `getLinkPeers` of a **device** address (measured 2026-10-02, 3.89.11): rfd refuses it with `-1
502
+ Failure`; hmipserver answers the channels' peer lists one after the other, so a peer linked with two
503
+ of the channels is named twice. The simulator does the same: on `rfd` the `notSupported` entry of the
504
+ fault table in force (`-1 Generic error` under hmipserver's default table, `-1 Failure` under
505
+ `FAULT_TABLES.bidcos`), on every other interface the channels' lists in channel order.
501
506
 
502
507
  ### CONFIG_PENDING
503
508
 
@@ -535,20 +540,21 @@ The two models are the explanations that competed before the measurement (Homema
535
540
  The default table is **hmipserver's**, because that is the one an application has to survive
536
541
  (measured):
537
542
 
538
- | key | code | string | when |
539
- | ------------------ | ---- | ------------------------------- | ---------------------------------------------------------------------------------- |
540
- | `unknownMethod` | -1 | Invalid XML-RPC message | no handler for the method name (hmipserver answers this without a faultCode) |
541
- | `unknownInstance` | -2 | Invalid device | address not known to this interface |
542
- | `unknownParamset` | -2 | Invalid device | the device has no such paramset |
543
- | `unknownLink` | -2 | Invalid device | the two channels are not linked |
544
- | `unknownParameter` | -5 | Unknown Parameter for value key | the paramset has no such parameter |
545
- | `readOnly` | -5 | Invalid parameter or value | `OPERATIONS & 2` is not set |
546
- | `typeError` | -5 | Invalid parameter or value | value does not fit the parameter's `TYPE` |
547
- | `outOfRange` | -5 | Invalid parameter or value | ENUM value not in `VALUE_LIST` |
548
- | `invalidValue` | -5 | Invalid parameter or value | the stored channel configuration cannot be transferred |
549
- | `notSupported` | -1 | Generic error | a method this interface does not implement |
550
- | `notReachable` | -1 | Generic error (UNREACH) | a sleeping battery device |
551
- | `invalidArguments` | -321 | Invalid arguments | wrong number of arguments; hmipserver really answers a Java exception message here |
543
+ | key | code | string | when |
544
+ | ---------------------------- | ---- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
545
+ | `unknownMethod` | -1 | Invalid XML-RPC message | no handler for the method name (hmipserver answers this without a faultCode) |
546
+ | `unknownInstance` | -2 | Invalid device | address not known to this interface |
547
+ | `unknownParamset` | -2 | Invalid device | `getParamset`/`putParamset` of a paramset the device does not have |
548
+ | `unknownParamsetDescription` | -3 | Unknown Paramset | `getParamsetDescription` of a paramset the channel does not list (3.89.11 appends the name: `Unknown Paramset: NOSUCH`) |
549
+ | `unknownLink` | -2 | Invalid device | the two channels are not linked |
550
+ | `unknownParameter` | -5 | Unknown Parameter for value key | the paramset has no such parameter |
551
+ | `readOnly` | -5 | Invalid parameter or value | `OPERATIONS & 2` is not set |
552
+ | `typeError` | -5 | Invalid parameter or value | value does not fit the parameter's `TYPE` |
553
+ | `outOfRange` | -5 | Invalid parameter or value | ENUM value not in `VALUE_LIST` |
554
+ | `invalidValue` | -5 | Invalid parameter or value | the stored channel configuration cannot be transferred |
555
+ | `notSupported` | -1 | Generic error | a method this interface does not implement |
556
+ | `notReachable` | -1 | Generic error (UNREACH) | a sleeping battery device |
557
+ | `invalidArguments` | -321 | Invalid arguments | wrong number of arguments; hmipserver really answers a Java exception message here |
552
558
 
553
559
  Over XML-RPC a fault is an XML-RPC fault, over BIN-RPC a message of type `0xff` with a
554
560
  `faultCode`/`faultString` struct. rfd's table is exported as `BIDCOS_FAULTS` and differs in more
@@ -561,6 +567,22 @@ new HmSim({faults: FAULT_TABLES.bidcos});
561
567
  new HmSim({faults: {unknownParameter: {faultCode: -4, faultString: 'Unknown parameter'}}}); // one entry
562
568
  ```
563
569
 
570
+ ### Unknown addresses and paramsets
571
+
572
+ Measured 2026-10-02 on rfd and hmipserver of firmware 3.89.11, with a device address and a channel
573
+ address alike: an address the interface does not know is a fault from every method that takes one -
574
+ `getDeviceDescription`, `getParamsetDescription`, `getParamset`, `getLinks`, `getLinkPeers` - rfd's
575
+ `-2 Unknown instance`, hmipserver's `-2 Invalid device`. The simulator answers `unknownInstance` of
576
+ the table in force everywhere (until 1.3.1 `getParamsetDescription` answered `''` there).
577
+
578
+ `getParamsetDescription` of a paramset the channel does not have: hmipserver answers `-3 Unknown
579
+ Paramset: <name>`; rfd answers `-3 Unknown paramset` on a channel without a `LINK` paramset and the
580
+ `LINK` description on a channel that has one (the name is taken as a peer address, as `getParamset`
581
+ does). The simulator answers `unknownParamsetDescription` whenever the channel's `PARAMSETS` lacks
582
+ the name. A paramset the channel lists but the description set has no entry for still answers `''`:
583
+ that is a gap in the fixture (logged once, see
584
+ [Device data and fixtures](#device-data-and-fixtures)), not something an interface process does.
585
+
564
586
  ### Service messages
565
587
 
566
588
  `getServiceMessages` answers `[[address, datapoint, value]]`: every datapoint whose description
package/lib/faults.d.ts CHANGED
@@ -11,6 +11,7 @@ export type FaultName =
11
11
  | 'unknownMethod'
12
12
  | 'unknownInstance'
13
13
  | 'unknownParamset'
14
+ | 'unknownParamsetDescription'
14
15
  | 'unknownParameter'
15
16
  | 'unknownLink'
16
17
  | 'readOnly'
package/lib/faults.js CHANGED
@@ -35,8 +35,11 @@ const DEFAULT_FAULTS = {
35
35
  unknownMethod: {faultCode: -1, faultString: 'Invalid XML-RPC message'},
36
36
  // device or channel address not known to this interface process
37
37
  unknownInstance: {faultCode: -2, faultString: 'Invalid device'},
38
- // the device/channel exists but does not have the requested paramset
38
+ // the device/channel exists but does not have the requested paramset (getParamset, putParamset)
39
39
  unknownParamset: {faultCode: -2, faultString: 'Invalid device'},
40
+ // getParamsetDescription of a paramset the channel does not have. hmipserver 3.89.11 appends the
41
+ // name it was asked for (`Unknown Paramset: NOSUCH`); measured 2026-10-02
42
+ unknownParamsetDescription: {faultCode: -3, faultString: 'Unknown Paramset'},
40
43
  // the paramset exists but does not contain the requested parameter (getValue/setValue)
41
44
  unknownParameter: {faultCode: -5, faultString: 'Unknown Parameter for value key'},
42
45
  // the two channels are not linked with each other
@@ -68,6 +71,10 @@ const BIDCOS_FAULTS = {
68
71
  // rfd does not fault here at all: an unknown paramset name is taken as a peer channel address
69
72
  // and the link defaults come back. The entry covers what the simulator cannot model.
70
73
  unknownParamset: {faultCode: -2, faultString: 'Unknown instance'},
74
+ // getParamsetDescription of a paramset the channel does not have, on a channel without a LINK
75
+ // paramset; a channel that has one answers the LINK description for any unknown name (rfd
76
+ // 3.89.11, measured 2026-10-02)
77
+ unknownParamsetDescription: {faultCode: -3, faultString: 'Unknown paramset'},
71
78
  unknownParameter: {faultCode: -5, faultString: 'Unknown parameter'},
72
79
  unknownLink: {faultCode: -1, faultString: 'Failure'},
73
80
  // rfd accepts a setValue on a read-only datapoint, so this is never raised against it
package/lib/sim.js CHANGED
@@ -571,6 +571,28 @@ class HmSim {
571
571
  return this.requireDevice(iface, address);
572
572
  }
573
573
 
574
+ /**
575
+ * The RPC `getParamsetDescription`: unlike {@link getParamsetDescription}, which the simulator
576
+ * calls for itself, an address the interface does not know is a fault (rfd `-2 Unknown instance`,
577
+ * hmipserver `-2 Invalid device`, measured on 3.89.11), and so is a paramset the channel does not
578
+ * list in `PARAMSETS` (`-3` on both). A paramset the channel has but the description set lacks
579
+ * still answers `''`, logged once by {@link resolveParamsetKey} - that is a gap in the fixture,
580
+ * not something an interface process does.
581
+ * @param {string} iface
582
+ * @param {string} address device or channel address
583
+ * @param {string} paramset MASTER, VALUES, LINK or SERVICE
584
+ */
585
+ describeParamset(iface, address, paramset) {
586
+ const device = this.requireDevice(iface, address);
587
+ if (typeof paramset !== 'string' || paramset === '') {
588
+ throw this.fault('invalidArguments', 'paramset type');
589
+ }
590
+ if (Array.isArray(device.PARAMSETS) && !device.PARAMSETS.includes(paramset)) {
591
+ throw this.fault('unknownParamsetDescription', `${address}/${paramset}`);
592
+ }
593
+ return this.getParamsetDescription(iface, device, paramset);
594
+ }
595
+
574
596
  getValue(iface, address, datapoint) {
575
597
  const {description, values} = this.storedParamset(iface, address, 'VALUES');
576
598
  if (!description[datapoint]) {
@@ -949,14 +971,31 @@ class HmSim {
949
971
  }
950
972
  }
951
973
 
974
+ /**
975
+ * The peers of a channel: the other end of every link it is on, the own channel once for a link
976
+ * with itself. For a device address rfd answers `-1 Failure` (measured, 3.89.11); hmipserver
977
+ * answers the channels' lists one after the other, a peer linked with two of the channels twice
978
+ * (measured, 3.89.11) - the simulator does the same on every interface but `rfd`.
979
+ * @param {string} iface
980
+ * @param {Array} params [address]
981
+ * @returns {string[]} channel addresses
982
+ */
952
983
  getLinkPeers(iface, params) {
953
- const addresses = new Set(this.channelsOf(iface, params[0]));
984
+ const [address] = params || [];
985
+ const device = this.requireDevice(iface, address);
986
+ if (iface === 'rfd' && !device.PARENT) {
987
+ // rfd's -1 Failure; -1 Generic error under hmipserver's default table
988
+ throw this.fault('notSupported', `getLinkPeers of a device address: ${address}`);
989
+ }
990
+
954
991
  const peers = [];
955
- for (const link of this.links[iface] || []) {
956
- if (addresses.has(link.SENDER)) {
957
- peers.push(link.RECEIVER);
958
- } else if (addresses.has(link.RECEIVER)) {
959
- peers.push(link.SENDER);
992
+ for (const channel of this.channelsOf(iface, address)) {
993
+ for (const link of this.links[iface] || []) {
994
+ if (link.SENDER === channel) {
995
+ peers.push(link.RECEIVER);
996
+ } else if (link.RECEIVER === channel) {
997
+ peers.push(link.SENDER);
998
+ }
960
999
  }
961
1000
  }
962
1001
  return peers;
@@ -2416,7 +2455,7 @@ class HmSim {
2416
2455
  'system.multicall': (iface, params) => this.multicall(iface, params),
2417
2456
  init: (iface, params) => this.init(iface, params),
2418
2457
  listDevices: (iface) => this.devices[iface].devices,
2419
- getParamsetDescription: (iface, params) => this.getParamsetDescription(iface, params[0], params[1]),
2458
+ getParamsetDescription: (iface, params) => this.describeParamset(iface, params[0], params[1]),
2420
2459
  getDeviceDescription: (iface, params) => this.getDeviceDescription(iface, params[0]),
2421
2460
  ping: (iface, params) => this.ping(iface, params),
2422
2461
  setValue: (iface, params) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hm-simulator",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "Simulates a Homematic CCU (rfd, hmipserver, VirtualDevices, CUxD, ReGa) for automated tests",
5
5
  "main": "sim.js",
6
6
  "types": "./sim.d.ts",
package/sim.d.ts CHANGED
@@ -405,6 +405,8 @@ declare class HmSim {
405
405
  paramset: string,
406
406
  ): HmSim.ParamsetDescription | undefined;
407
407
  getDeviceDescription(iface: string, address: string): HmSim.DeviceDescription;
408
+ /** the RPC `getParamsetDescription`: faults for an unknown address or a paramset the channel lacks */
409
+ describeParamset(iface: string, address: string, paramset: string): HmSim.ParamsetDescription | undefined;
408
410
 
409
411
  /* ---------------------------------------------------------------- scenario api: service messages and health */
410
412