matterbridge-example-dynamic-platform 3.0.3 → 3.0.4-dev-20260901-796d818

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
@@ -29,6 +29,14 @@ If you like this project and find it useful, please consider giving it a star on
29
29
 
30
30
  <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
31
31
 
32
+ ## [3.0.4] - Dev branch
33
+
34
+ ### Breaking changes
35
+
36
+ - [matterbridge]: Require matterbridge v.3.10.8 with matter v.1.6.0.
37
+
38
+ <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
39
+
32
40
  ## [3.0.3] - 2026-08-30
33
41
 
34
42
  ### Breaking changes
package/dist/module.js CHANGED
@@ -1,22 +1,9 @@
1
- import { aggregator, airPurifier, airQualitySensor, bridgedNode, closure, colorTemperatureLight, contactSensor, windowCovering, dimmableLight, mountedDimmableLoadControl, dimmablePlugInUnit, doorLock, electricalSensor, extendedColorLight, fan, flowSensor, genericSwitch, getSemtag, humiditySensor, lightSensor, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, modeSelect, occupancySensor, onOffLight, mountedOnOffControl, onOffPlugInUnit, onOffLightSwitch, powerSource, pressureSensor, pump, rainSensor, smokeCoAlarm, temperatureSensor, thermostat, waterFreezeDetector, waterLeakDetector, waterValve, } from 'matterbridge';
2
- import { AirConditioner, BasicVideoPlayer, BatteryStorage, Closure, Cooktop, Dishwasher, Evse, ExtractorHood, HeatPump, IrrigationSystem, LaundryDryer, LaundryWasher, MatterbridgeClosureControlServer, MicrowaveOven, Oven, Refrigerator, RoboticVacuumCleaner, SoilSensor, SolarPower, Speaker, WaterHeater, } from 'matterbridge/devices';
1
+ import { aggregator, airPurifier, airQualitySensor, bridgedNode, colorTemperatureLight, contactSensor, windowCovering, dimmableLight, mountedDimmableLoadControl, dimmablePlugInUnit, doorLock, electricalSensor, extendedColorLight, fan, flowSensor, genericSwitch, getSemtag, humiditySensor, lightSensor, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, modeSelect, occupancySensor, onOffLight, mountedOnOffControl, onOffPlugInUnit, onOffLightSwitch, powerSource, pressureSensor, pump, rainSensor, smokeCoAlarm, temperatureSensor, thermostat, waterFreezeDetector, waterLeakDetector, waterValve, } from 'matterbridge';
2
+ import { AirConditioner, BasicVideoPlayer, BatteryStorage, Closure, Cooktop, Dishwasher, Evse, ExtractorHood, HeatPump, IrrigationSystem, LaundryDryer, LaundryWasher, MicrowaveOven, Oven, Refrigerator, RoboticVacuumCleaner, SoilSensor, SolarPower, Speaker, WaterHeater, } from 'matterbridge/devices';
3
3
  import { debugStringify } from 'matterbridge/logger';
4
4
  import { ClosureCoveringTag, ClosurePanelTag, ClosureTag, ClosureWindowTag, CommonAreaNamespaceTag, CommonLocationTag, CommonNumberTag, CommonPositionTag, RefrigeratorTag, SwitchesTag, UINT16_MAX, UINT32_MAX, } from 'matterbridge/matter';
5
5
  import { AirQuality, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, ClosureControl, ClosureDimension, ColorControl, DeviceEnergyManagement, DoorLock, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, EnergyEvse, EnergyEvseMode, FanControl, FlowMeasurement, FormaldehydeConcentrationMeasurement, Identify, IlluminanceMeasurement, LevelControl, NitrogenDioxideConcentrationMeasurement, OccupancySensing, OnOff, OperationalState, OvenMode, OzoneConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerSource, PressureMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, RvcCleanMode, RvcOperationalState, RvcRunMode, SmokeCoAlarm, TemperatureMeasurement, Thermostat, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, } from 'matterbridge/matter/clusters';
6
- import { ThreeLevelAuto } from 'matterbridge/matter/types';
7
- import { fireAndForget, getEnumDescription, isValidBoolean, isValidNumber, isValidObject, isValidString, parseVersionString } from 'matterbridge/utils';
8
- function luxToMatter(lux) {
9
- if (!Number.isFinite(lux) || lux < 1)
10
- return 0;
11
- const encoded = Math.round(10000 * Math.log10(lux) + 1);
12
- return Math.min(encoded, 0xfffe);
13
- }
14
- function matterToLux(value) {
15
- if (!Number.isFinite(value) || value <= 0)
16
- return 0;
17
- const v = Math.min(value, 0xfffe);
18
- return Math.round(Math.pow(10, (v - 1) / 10000));
19
- }
6
+ import { fireAndForget, getEnumDescription, isValidBoolean, isValidNumber, isValidObject, isValidString, luxToMatter, matterToLux, parseVersionString } from 'matterbridge/utils';
20
7
  export default function initializePlugin(matterbridge, log, config) {
21
8
  return new ExampleMatterbridgeDynamicPlatform(matterbridge, log, config);
22
9
  }
@@ -110,8 +97,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
110
97
  constructor(matterbridge, log, config) {
111
98
  super(matterbridge, log, config);
112
99
  this.config = config;
113
- if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.7')) {
114
- throw new Error(`This plugin requires Matterbridge version >= "3.10.7". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
100
+ if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.8')) {
101
+ throw new Error(`This plugin requires Matterbridge version >= "3.10.8". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
115
102
  }
116
103
  this.log.info('Initializing platform:', this.config.name);
117
104
  }
@@ -180,41 +167,42 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
180
167
  this.irrigationSystem = await this.addDevice(this.irrigationSystem);
181
168
  this.closureGarageDoor = new Closure('Garage Door', 'GAR000071', {
182
169
  tagList: [getSemtag(ClosureTag.GarageDoor)],
170
+ movementDuration: 2000,
183
171
  });
184
172
  this.closureGarageDoor = await this.addDevice(this.closureGarageDoor);
185
- let closureGarageDoorMoveTimeout;
186
- function closureTargetToCurrentPosition(position) {
187
- if (position === ClosureControl.TargetPosition.MoveToFullyClosed)
188
- return ClosureControl.CurrentPosition.FullyClosed;
189
- if (position === ClosureControl.TargetPosition.MoveToFullyOpen)
190
- return ClosureControl.CurrentPosition.FullyOpened;
191
- if (position === ClosureControl.TargetPosition.MoveToPedestrianPosition)
192
- return ClosureControl.CurrentPosition.OpenedForPedestrian;
193
- if (position === ClosureControl.TargetPosition.MoveToVentilationPosition)
194
- return ClosureControl.CurrentPosition.OpenedForVentilation;
195
- return ClosureControl.CurrentPosition.OpenedAtSignature;
196
- }
197
- this.closureGarageDoor?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
173
+ this.closureGarageDoor?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed } }) => {
198
174
  this.closureGarageDoor?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
199
- attributes.countdownTime = 1;
200
- closureGarageDoorMoveTimeout = setTimeout(() => {
201
- const targetState = this.closureGarageDoor?.getAttribute(ClosureControl, 'overallTargetState');
202
- if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
203
- return;
204
- void this.closureGarageDoor?.setState({
205
- position: closureTargetToCurrentPosition(targetState.position),
206
- latch: targetState.latch,
207
- speed: targetState.speed,
208
- secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed && targetState.latch === true,
209
- }, targetState, ClosureControl.MainState.Stopped, 0, []);
210
- void this.closureGarageDoor?.triggerMovementCompleted();
211
- }, 1000).unref();
212
175
  });
213
- this.closureGarageDoor?.addCommandHandler('ClosureControl.stop', ({ attributes }) => {
176
+ this.closureGarageDoor?.addCommandHandler('ClosureControl.stop', () => {
214
177
  this.closureGarageDoor?.log.info('Command stop called');
215
- attributes.countdownTime = 0;
216
- clearTimeout(closureGarageDoorMoveTimeout);
217
- closureGarageDoorMoveTimeout = undefined;
178
+ });
179
+ this.closureSlidingGate = new Closure('Sliding Gate', 'GAT000073', {
180
+ tagList: [getSemtag(ClosureTag.Gate)],
181
+ movementDuration: 2000,
182
+ motionLatching: true,
183
+ speed: true,
184
+ pedestrian: true,
185
+ });
186
+ this.closureSlidingGate = await this.addDevice(this.closureSlidingGate);
187
+ this.closureSlidingGate?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed } }) => {
188
+ this.closureSlidingGate?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
189
+ });
190
+ this.closureSlidingGate?.addCommandHandler('ClosureControl.stop', () => {
191
+ this.closureSlidingGate?.log.info('Command stop called');
192
+ });
193
+ this.closureRoofWindow = new Closure('Roof Window', 'ROO000074', {
194
+ tagList: [getSemtag(ClosureTag.Window), getSemtag(ClosureWindowTag.Roof)],
195
+ movementDuration: 2000,
196
+ motionLatching: true,
197
+ speed: true,
198
+ ventilation: true,
199
+ });
200
+ this.closureRoofWindow = await this.addDevice(this.closureRoofWindow);
201
+ this.closureRoofWindow?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed } }) => {
202
+ this.closureRoofWindow?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
203
+ });
204
+ this.closureRoofWindow?.addCommandHandler('ClosureControl.stop', () => {
205
+ this.closureRoofWindow?.log.info('Command stop called');
218
206
  });
219
207
  this.closureVenetianBlind = new Closure('Venetian Blind', 'VEN000072', {
220
208
  tagList: [getSemtag(ClosureTag.Covering), getSemtag(ClosureCoveringTag.Venetian)],
@@ -222,13 +210,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
222
210
  const closureVenetianBlindLift = this.closureVenetianBlind.addPanel('Lift', [getSemtag(ClosurePanelTag.Lift)], 'lift');
223
211
  const closureVenetianBlindTilt = this.closureVenetianBlind.addPanel('Tilt', [getSemtag(ClosurePanelTag.Tilt)], 'tilt');
224
212
  this.closureVenetianBlind = await this.addDevice(this.closureVenetianBlind);
225
- const closurePanelTargetPercent = (position) => {
226
- if (position === ClosureControl.TargetPosition.MoveToFullyOpen)
227
- return 0;
228
- if (position === ClosureControl.TargetPosition.MoveToFullyClosed)
229
- return 10000;
230
- return 5000;
231
- };
232
213
  const closurePanelReachTimeouts = new Map();
233
214
  let closureVenetianBlindMoveTimeout;
234
215
  const clearClosureVenetianBlindTimeouts = () => {
@@ -253,7 +234,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
253
234
  ? ClosureControl.CurrentPosition.FullyClosed
254
235
  : ClosureControl.CurrentPosition.PartiallyOpened;
255
236
  void this.closureVenetianBlind?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureVenetianBlind.log);
256
- void this.closureVenetianBlind?.setAttribute(ClosureControl, 'overallCurrentState', { position: overallPosition, latch: liftCurrent.latch ?? true, speed: liftCurrent.speed, secureState: liftCurrent.position === 10000 && liftCurrent.latch === true }, this.closureVenetianBlind?.log);
237
+ void this.closureVenetianBlind?.setAttribute(ClosureControl, 'overallCurrentState', { position: overallPosition, secureState: liftCurrent.position === 10000 }, this.closureVenetianBlind?.log);
257
238
  };
258
239
  const simulateClosurePanelReachingTarget = (panel) => {
259
240
  clearTimeout(closurePanelReachTimeouts.get(panel));
@@ -268,8 +249,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
268
249
  }, 1000).unref();
269
250
  closurePanelReachTimeouts.set(panel, currentStateTimeout);
270
251
  };
271
- const moveClosurePanelTo = (panel, position, latch, speed) => {
272
- void panel.setAttribute(ClosureDimension, 'targetState', { position, latch: latch ?? true, speed }, panel.log);
252
+ const moveClosurePanelTo = (panel, position) => {
253
+ void panel.setAttribute(ClosureDimension, 'targetState', { position }, panel.log);
273
254
  simulateClosurePanelReachingTarget(panel);
274
255
  };
275
256
  this.closureVenetianBlind?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
@@ -282,9 +263,11 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
282
263
  const targetState = this.closureVenetianBlind?.getAttribute(ClosureControl, 'overallTargetState');
283
264
  if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
284
265
  return;
285
- const panelPercent = closurePanelTargetPercent(targetState.position);
286
- moveClosurePanelTo(closureVenetianBlindLift, panelPercent, targetState.latch, targetState.speed);
287
- moveClosurePanelTo(closureVenetianBlindTilt, panelPercent, targetState.latch, targetState.speed);
266
+ const panelPercent = this.closureVenetianBlind?.targetPositionToTargetPercent(targetState.position);
267
+ if (panelPercent === undefined)
268
+ return;
269
+ moveClosurePanelTo(closureVenetianBlindLift, panelPercent);
270
+ moveClosurePanelTo(closureVenetianBlindTilt, panelPercent);
288
271
  }, 1000).unref();
289
272
  });
290
273
  this.closureVenetianBlind?.addCommandHandler('ClosureControl.stop', () => {
@@ -307,96 +290,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
307
290
  };
308
291
  addClosurePanelStepSimulation(closureVenetianBlindLift);
309
292
  addClosurePanelStepSimulation(closureVenetianBlindTilt);
310
- this.closureSlidingGate = new MatterbridgeEndpoint([closure, powerSource], {
311
- id: 'SlidingGate-GAT000073',
312
- tagList: [getSemtag(ClosureTag.Gate)],
313
- }, this.config.debug)
314
- .createDefaultIdentifyClusterServer()
315
- .createDefaultBasicInformationClusterServer('Sliding Gate', 'GAT000073', 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Closure')
316
- .createDefaultPowerSourceWiredClusterServer();
317
- this.closureSlidingGate.behaviors.require(MatterbridgeClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed, ClosureControl.Feature.Pedestrian), {
318
- countdownTime: 0,
319
- mainState: ClosureControl.MainState.Stopped,
320
- currentErrorList: [],
321
- overallCurrentState: { position: ClosureControl.CurrentPosition.FullyClosed, latch: true, speed: ThreeLevelAuto.Auto, secureState: true },
322
- overallTargetState: { position: ClosureControl.TargetPosition.MoveToFullyClosed, latch: true, speed: ThreeLevelAuto.Auto },
323
- latchControlModes: { remoteLatching: true, remoteUnlatching: true },
324
- });
325
- this.closureSlidingGate = await this.addDevice(this.closureSlidingGate);
326
- let closureSlidingGateMoveTimeout;
327
- this.closureSlidingGate?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
328
- this.closureSlidingGate?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
329
- attributes.countdownTime = 1;
330
- closureSlidingGateMoveTimeout = setTimeout(() => {
331
- void (async () => {
332
- const targetState = this.closureSlidingGate?.getAttribute(ClosureControl, 'overallTargetState');
333
- if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
334
- return;
335
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'countdownTime', 0, this.closureSlidingGate.log);
336
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureSlidingGate.log);
337
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'currentErrorList', [], this.closureSlidingGate.log);
338
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'overallCurrentState', {
339
- position: closureTargetToCurrentPosition(targetState.position),
340
- latch: targetState.latch,
341
- speed: targetState.speed,
342
- secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed && targetState.latch === true,
343
- }, this.closureSlidingGate.log);
344
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'overallTargetState', targetState, this.closureSlidingGate.log);
345
- await this.closureSlidingGate?.triggerEvent(ClosureControl, 'movementCompleted', undefined, this.closureSlidingGate.log);
346
- })();
347
- }, 1000).unref();
348
- });
349
- this.closureSlidingGate?.addCommandHandler('ClosureControl.stop', ({ attributes }) => {
350
- this.closureSlidingGate?.log.info('Command stop called');
351
- attributes.countdownTime = 0;
352
- clearTimeout(closureSlidingGateMoveTimeout);
353
- closureSlidingGateMoveTimeout = undefined;
354
- });
355
- this.closureRoofWindow = new MatterbridgeEndpoint([closure, powerSource], {
356
- id: 'RoofWindow-ROO000074',
357
- tagList: [getSemtag(ClosureTag.Window), getSemtag(ClosureWindowTag.Roof)],
358
- }, this.config.debug)
359
- .createDefaultIdentifyClusterServer()
360
- .createDefaultBasicInformationClusterServer('Roof Window', 'ROO000074', 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Closure')
361
- .createDefaultPowerSourceWiredClusterServer();
362
- this.closureRoofWindow.behaviors.require(MatterbridgeClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed, ClosureControl.Feature.Ventilation), {
363
- countdownTime: 0,
364
- mainState: ClosureControl.MainState.Stopped,
365
- currentErrorList: [],
366
- overallCurrentState: { position: ClosureControl.CurrentPosition.FullyClosed, latch: true, speed: ThreeLevelAuto.Auto, secureState: true },
367
- overallTargetState: { position: ClosureControl.TargetPosition.MoveToFullyClosed, latch: true, speed: ThreeLevelAuto.Auto },
368
- latchControlModes: { remoteLatching: true, remoteUnlatching: true },
369
- });
370
- this.closureRoofWindow = await this.addDevice(this.closureRoofWindow);
371
- let closureRoofWindowMoveTimeout;
372
- this.closureRoofWindow?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
373
- this.closureRoofWindow?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
374
- attributes.countdownTime = 1;
375
- closureRoofWindowMoveTimeout = setTimeout(() => {
376
- void (async () => {
377
- const targetState = this.closureRoofWindow?.getAttribute(ClosureControl, 'overallTargetState');
378
- if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
379
- return;
380
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'countdownTime', 0, this.closureRoofWindow.log);
381
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureRoofWindow.log);
382
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'currentErrorList', [], this.closureRoofWindow.log);
383
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'overallCurrentState', {
384
- position: closureTargetToCurrentPosition(targetState.position),
385
- latch: targetState.latch,
386
- speed: targetState.speed,
387
- secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed && targetState.latch === true,
388
- }, this.closureRoofWindow.log);
389
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'overallTargetState', targetState, this.closureRoofWindow.log);
390
- await this.closureRoofWindow?.triggerEvent(ClosureControl, 'movementCompleted', undefined, this.closureRoofWindow.log);
391
- })();
392
- }, 1000).unref();
393
- });
394
- this.closureRoofWindow?.addCommandHandler('ClosureControl.stop', ({ attributes }) => {
395
- this.closureRoofWindow?.log.info('Command stop called');
396
- attributes.countdownTime = 0;
397
- clearTimeout(closureRoofWindowMoveTimeout);
398
- closureRoofWindowMoveTimeout = undefined;
399
- });
400
293
  this.climate = new MatterbridgeEndpoint([bridgedNode, powerSource], { id: 'Climate' }, this.config.debug)
401
294
  .createDefaultBridgedDeviceBasicInformationClusterServer('Climate', 'CLI00008', 0xfff1, 'Matterbridge', 'Matterbridge Climate')
402
295
  .createDefaultPowerSourceReplaceableBatteryClusterServer(90, PowerSource.BatChargeLevel.Ok, 2990, '2 x AA', 2, PowerSource.BatReplaceability.UserReplaceable)
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.3",
3
+ "version": "3.0.4-dev-20260901-796d818",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "3.0.3",
9
+ "version": "3.0.4-dev-20260901-796d818",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.3.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.3",
3
+ "version": "3.0.4-dev-20260901-796d818",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",