matterbridge 3.2.3-dev-20250813-34745fd → 3.2.3-dev-20250816-3639b16

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
@@ -12,9 +12,12 @@ If you like this project and find it useful, please consider giving it a star on
12
12
 
13
13
  ### Added
14
14
 
15
- - [Oven]: Added Oven class and Jest test. It is not supported by the Home app.
16
- - [MicrowaveOven]: Added MicrowaveOven class and Jest test. It is not supported by the Home app.
17
- - [Matter]: Added Matter Specification Version 1.4 pdf files.
15
+ - [Oven]: Added Oven() class and Jest test. It is not supported by the Home app.
16
+ - [MicrowaveOven]: Added MicrowaveOven() class and Jest test. It is not supported by the Home app.
17
+ - [Cooktop]: Added Cooktop() class and Jest test. It is not supported by the Home app.
18
+ - [Refrigerator]: Added Refrigerator() class and Jest test. It is not supported by the Home app.
19
+ - [Pages]: Added first draft of https://luligu.github.io/matterbridge.
20
+ - [Matter]: Added Matter Specification Version 1.0, 1.1, 1.2, 1.3, 1.4 and 1.4.1 pdf files.
18
21
 
19
22
  ### Changed
20
23
 
package/README-DEV.md CHANGED
@@ -357,6 +357,57 @@ The schema file is loaded from the root of the plugin package. The file must be
357
357
 
358
358
  The properties of the schema file shall correspond to the properties of the config file.
359
359
 
360
+ # Frequently asked questions
361
+
362
+ ## Why plugins cannot install matterbridge as a dependency, devDependency or peerDependency
363
+
364
+ There must be one and only one instance of Matterbridge and matter.js in the node_modules directory.
365
+
366
+ ### What happens when matterbridge or matter.js are present like a devDependencies
367
+
368
+ The plugins can be globally installed in different ways:
369
+
370
+ - from npm (all devDependencies are installed in node_modules if the plugin is not correctly published)
371
+ - from a tarball (all devDependencies are installed in node_modules if the tarball is not correctly built)
372
+ - from a local path (devDependencies are always installed in node_modules!)
373
+
374
+ In all these cases the devDependencies are always installed by npm and show up in the plugins `node_modules`:
375
+
376
+ - npm install -g ./yourplugin
377
+ - npm install -g git+https://github.com/you/yourplugin.git
378
+ - npm install -g yourplugin
379
+
380
+ In the first 2 cases the devDependeincies are always installed in node_modules!
381
+
382
+ In the last (most dangerous case) they are installed when the user forgets to add --omit=dev or doesn't have NODE_ENV=production.
383
+
384
+ This is also the reason why to be safe 100% all official plugins are published for production removing also devDependencies from package.json.
385
+
386
+ I also lock the dependencies with npm shrinkwrap cause npm installs always the latest versions that mach your range in package.json but sometimes this just breaks the plugin. This permits to be sure that the user host machine has exactly the same dependencies you coded your plugin with.
387
+
388
+ ### The technical reason we cannot have matterbridge or @matter in the plugin node_modules.
389
+
390
+ Module Resolution in Matterbridge Plugin System.
391
+ When Matterbridge loads plugins on demand as ESM modules, the module resolution follows Node.js's standard module resolution algorithm. Here's how it works:
392
+
393
+ **1. Plugin Loading Process**
394
+ From the code in pluginManager.ts (lines 628-632), Matterbridge:
395
+
396
+ Resolves the plugin's main entry point from its package.json
397
+ Converts the file path to a URL using pathToFileURL()
398
+ Uses dynamic import: await import(pluginUrl.href)
399
+
400
+ **2. Module Resolution Priority**
401
+ When the plugin code runs import statements, Node.js follows this resolution order:
402
+
403
+ Plugin's local node_modules - Checked first
404
+ Parent directories - Walks up the directory tree looking for node_modules
405
+ Matterbridge's node_modules - Only reached if not found in plugin's dependencies
406
+
407
+ **3. Key Behavior**
408
+ Plugin's node_modules takes precedence - If a package exists in the plugin's own node_modules, that version will be used.
409
+ Matterbridge's node_modules is used as fallback.
410
+
360
411
  # Contribution Guidelines
361
412
 
362
413
  Thank you for your interest in contributing to my project!
@@ -0,0 +1,21 @@
1
+ import { cookSurface, cooktop, powerSource } from '../matterbridgeDeviceTypes.js';
2
+ import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
3
+ import { createLevelTemperatureControlClusterServer } from './temperatureControl.js';
4
+ export class Cooktop extends MatterbridgeEndpoint {
5
+ constructor(name, serial) {
6
+ super([cooktop, powerSource], { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
7
+ this.createDefaultIdentifyClusterServer();
8
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Cooktop');
9
+ this.createDefaultPowerSourceWiredClusterServer();
10
+ this.createOffOnlyOnOffClusterServer(true);
11
+ this.addFixedLabel('composed', 'Cooktop');
12
+ }
13
+ addSurface(name, tagList, selectedTemperatureLevel = 2, supportedTemperatureLevels = ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5']) {
14
+ const surface = this.addChildDeviceType(name, cookSurface, { tagList }, true);
15
+ surface.log.logName = name;
16
+ createLevelTemperatureControlClusterServer(surface, selectedTemperatureLevel, supportedTemperatureLevels);
17
+ surface.createDefaultTemperatureMeasurementClusterServer(2000);
18
+ surface.createOffOnlyOnOffClusterServer(true);
19
+ return surface;
20
+ }
21
+ }
@@ -1,4 +1,3 @@
1
- import { TemperatureControl } from '@matter/main/clusters/temperature-control';
2
1
  import { ModeBase } from '@matter/main/clusters/mode-base';
3
2
  import { DishwasherModeServer } from '@matter/main/behaviors/dishwasher-mode';
4
3
  import { DishwasherAlarmServer } from '@matter/main/behaviors/dishwasher-alarm';
@@ -6,7 +5,7 @@ import { DishwasherMode } from '@matter/main/clusters/dishwasher-mode';
6
5
  import { dishwasher, powerSource } from '../matterbridgeDeviceTypes.js';
7
6
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
8
7
  import { MatterbridgeOnOffServer, MatterbridgeServer } from '../matterbridgeBehaviors.js';
9
- import { MatterbridgeLevelTemperatureControlServer, MatterbridgeNumberTemperatureControlServer } from './temperatureControl.js';
8
+ import { createLevelTemperatureControlClusterServer, createNumberTemperatureControlClusterServer } from './temperatureControl.js';
10
9
  export class Dishwasher extends MatterbridgeEndpoint {
11
10
  constructor(name, serial, currentMode, supportedModes, selectedTemperatureLevel, supportedTemperatureLevels, temperatureSetpoint, minTemperature, maxTemperature, step, operationalState) {
12
11
  super([dishwasher, powerSource], { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
@@ -17,27 +16,11 @@ export class Dishwasher extends MatterbridgeEndpoint {
17
16
  this.createDefaultDishwasherModeClusterServer(currentMode, supportedModes);
18
17
  this.createDefaultDishwasherAlarmClusterServer();
19
18
  if (temperatureSetpoint)
20
- this.createNumberTemperatureControlClusterServer(temperatureSetpoint, minTemperature, maxTemperature, step);
19
+ createNumberTemperatureControlClusterServer(this, temperatureSetpoint, minTemperature, maxTemperature, step);
21
20
  else
22
- this.createLevelTemperatureControlClusterServer(selectedTemperatureLevel, supportedTemperatureLevels);
21
+ createLevelTemperatureControlClusterServer(this, selectedTemperatureLevel, supportedTemperatureLevels);
23
22
  this.createDefaultOperationalStateClusterServer(operationalState);
24
23
  }
25
- createLevelTemperatureControlClusterServer(selectedTemperatureLevel = 1, supportedTemperatureLevels = ['Cold', 'Warm', 'Hot', '30°', '40°', '60°', '80°']) {
26
- this.behaviors.require(MatterbridgeLevelTemperatureControlServer.with(TemperatureControl.Feature.TemperatureLevel), {
27
- selectedTemperatureLevel,
28
- supportedTemperatureLevels,
29
- });
30
- return this;
31
- }
32
- createNumberTemperatureControlClusterServer(temperatureSetpoint = 40 * 100, minTemperature = 30 * 100, maxTemperature = 60 * 100, step = 10 * 100) {
33
- this.behaviors.require(MatterbridgeNumberTemperatureControlServer.with(TemperatureControl.Feature.TemperatureNumber, TemperatureControl.Feature.TemperatureStep), {
34
- temperatureSetpoint,
35
- minTemperature,
36
- maxTemperature,
37
- step,
38
- });
39
- return this;
40
- }
41
24
  createDefaultDishwasherModeClusterServer(currentMode = 2, supportedModes = [
42
25
  { label: 'Light', mode: 1, modeTags: [{ value: DishwasherMode.ModeTag.Light }] },
43
26
  { label: 'Normal', mode: 2, modeTags: [{ value: DishwasherMode.ModeTag.Normal }] },
@@ -4,6 +4,8 @@ export * from './extractorHood.js';
4
4
  export * from './dishwasher.js';
5
5
  export * from './microwaveOven.js';
6
6
  export * from './oven.js';
7
+ export * from './cooktop.js';
8
+ export * from './refrigerator.js';
7
9
  export * from './waterHeater.js';
8
10
  export * from './evse.js';
9
11
  export * from './solarPower.js';
@@ -1,11 +1,10 @@
1
1
  import { LaundryWasherMode } from '@matter/main/clusters/laundry-washer-mode';
2
- import { TemperatureControl } from '@matter/main/clusters/temperature-control';
3
2
  import { LaundryDryerControls } from '@matter/main/clusters/laundry-dryer-controls';
4
3
  import { LaundryDryerControlsServer } from '@matter/main/behaviors/laundry-dryer-controls';
5
4
  import { laundryDryer, powerSource } from '../matterbridgeDeviceTypes.js';
6
5
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
7
6
  import { MatterbridgeLaundryWasherModeServer } from './laundryWasher.js';
8
- import { MatterbridgeLevelTemperatureControlServer, MatterbridgeNumberTemperatureControlServer } from './temperatureControl.js';
7
+ import { createLevelTemperatureControlClusterServer, createNumberTemperatureControlClusterServer } from './temperatureControl.js';
9
8
  export class LaundryDryer extends MatterbridgeEndpoint {
10
9
  constructor(name, serial, currentMode, supportedModes, selectedTemperatureLevel, supportedTemperatureLevels, temperatureSetpoint, minTemperature, maxTemperature, step, operationalState) {
11
10
  super([laundryDryer, powerSource], { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
@@ -16,9 +15,9 @@ export class LaundryDryer extends MatterbridgeEndpoint {
16
15
  this.createDefaultLaundryWasherModeClusterServer(currentMode, supportedModes);
17
16
  this.createDefaultLaundryDryerControlsClusterServer(1);
18
17
  if (temperatureSetpoint)
19
- this.createNumberTemperatureControlClusterServer(temperatureSetpoint, minTemperature, maxTemperature, step);
18
+ createNumberTemperatureControlClusterServer(this, temperatureSetpoint, minTemperature, maxTemperature, step);
20
19
  else
21
- this.createLevelTemperatureControlClusterServer(selectedTemperatureLevel, supportedTemperatureLevels);
20
+ createLevelTemperatureControlClusterServer(this, selectedTemperatureLevel, supportedTemperatureLevels);
22
21
  this.createDefaultOperationalStateClusterServer(operationalState);
23
22
  }
24
23
  createDefaultLaundryWasherModeClusterServer(currentMode = 2, supportedModes = [
@@ -40,20 +39,4 @@ export class LaundryDryer extends MatterbridgeEndpoint {
40
39
  });
41
40
  return this;
42
41
  }
43
- createLevelTemperatureControlClusterServer(selectedTemperatureLevel = 1, supportedTemperatureLevels = ['Cold', 'Warm', 'Hot', '30°', '40°', '60°', '80°']) {
44
- this.behaviors.require(MatterbridgeLevelTemperatureControlServer.with(TemperatureControl.Feature.TemperatureLevel), {
45
- selectedTemperatureLevel,
46
- supportedTemperatureLevels,
47
- });
48
- return this;
49
- }
50
- createNumberTemperatureControlClusterServer(temperatureSetpoint = 40 * 100, minTemperature = 30 * 100, maxTemperature = 60 * 100, step = 10 * 100) {
51
- this.behaviors.require(MatterbridgeNumberTemperatureControlServer.with(TemperatureControl.Feature.TemperatureNumber, TemperatureControl.Feature.TemperatureStep), {
52
- temperatureSetpoint,
53
- minTemperature,
54
- maxTemperature,
55
- step,
56
- });
57
- return this;
58
- }
59
42
  }
@@ -13,6 +13,7 @@ export class Oven extends MatterbridgeEndpoint {
13
13
  this.createDefaultIdentifyClusterServer();
14
14
  this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Oven');
15
15
  this.createDefaultPowerSourceWiredClusterServer();
16
+ this.addFixedLabel('composed', 'Oven');
16
17
  }
17
18
  addCabinet(name, tagList, currentMode = 2, supportedModes = [
18
19
  { label: 'Bake', mode: 1, modeTags: [{ value: OvenMode.ModeTag.Bake }] },
@@ -0,0 +1,55 @@
1
+ import { ModeBase } from '@matter/main/clusters/mode-base';
2
+ import { RefrigeratorAndTemperatureControlledCabinetModeServer } from '@matter/main/behaviors/refrigerator-and-temperature-controlled-cabinet-mode';
3
+ import { RefrigeratorAndTemperatureControlledCabinetMode } from '@matter/main/clusters/refrigerator-and-temperature-controlled-cabinet-mode';
4
+ import { oven, powerSource, temperatureControlledCabinetCooler } from '../matterbridgeDeviceTypes.js';
5
+ import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
6
+ import { MatterbridgeServer } from '../matterbridgeBehaviors.js';
7
+ import { createLevelTemperatureControlClusterServer } from './temperatureControl.js';
8
+ export class Refrigerator extends MatterbridgeEndpoint {
9
+ constructor(name, serial) {
10
+ super([oven, powerSource], { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
11
+ this.createDefaultIdentifyClusterServer();
12
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Refrigerator');
13
+ this.createDefaultPowerSourceWiredClusterServer();
14
+ this.addFixedLabel('composed', 'Refrigerator');
15
+ }
16
+ addCabinet(name, tagList, currentMode = 1, supportedModes = [
17
+ { label: 'Auto', mode: 1, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.Auto }] },
18
+ { label: 'RapidCool', mode: 2, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.RapidCool }] },
19
+ { label: 'RapidFreeze', mode: 3, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.RapidFreeze }] },
20
+ ], selectedTemperatureLevel = 2, supportedTemperatureLevels = ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5'], currentTemperature = 1000) {
21
+ const cabinet = this.addChildDeviceType(name, temperatureControlledCabinetCooler, { tagList }, true);
22
+ cabinet.log.logName = name;
23
+ cabinet.createDefaultIdentifyClusterServer();
24
+ createLevelTemperatureControlClusterServer(cabinet, selectedTemperatureLevel, supportedTemperatureLevels);
25
+ this.createDefaultRefrigeratorAndTemperatureControlledCabinetModeClusterServer(cabinet, currentMode, supportedModes);
26
+ cabinet.createDefaultTemperatureMeasurementClusterServer(currentTemperature);
27
+ return cabinet;
28
+ }
29
+ createDefaultRefrigeratorAndTemperatureControlledCabinetModeClusterServer(endpoint, currentMode, supportedModes) {
30
+ endpoint.behaviors.require(MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer, {
31
+ supportedModes,
32
+ currentMode,
33
+ });
34
+ return endpoint;
35
+ }
36
+ }
37
+ export class MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer extends RefrigeratorAndTemperatureControlledCabinetModeServer {
38
+ initialize() {
39
+ const device = this.endpoint.stateOf(MatterbridgeServer);
40
+ device.log.info('MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer initialized');
41
+ }
42
+ changeToMode(request) {
43
+ const device = this.endpoint.stateOf(MatterbridgeServer);
44
+ const supportedMode = this.state.supportedModes.find((supportedMode) => supportedMode.mode === request.newMode);
45
+ if (supportedMode) {
46
+ device.log.info(`MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer: changeToMode (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}) called with mode ${supportedMode.mode} = ${supportedMode.label}`);
47
+ this.state.currentMode = request.newMode;
48
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
49
+ }
50
+ else {
51
+ device.log.error(`MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer: changeToMode (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber}) called with invalid mode ${request.newMode}`);
52
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
53
+ }
54
+ }
55
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.2.3-dev-20250813-34745fd",
3
+ "version": "3.2.3-dev-20250816-3639b16",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.2.3-dev-20250813-34745fd",
9
+ "version": "3.2.3-dev-20250816-3639b16",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.15.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.2.3-dev-20250813-34745fd",
3
+ "version": "3.2.3-dev-20250816-3639b16",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",