Skip to content

Commit

Permalink
Dev 2.1.6-dev.3
Browse files Browse the repository at this point in the history
  • Loading branch information
Luligu committed Feb 13, 2025
1 parent 3abfa24 commit 8c3f719
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 64 deletions.
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "matterbridge",
"version": "2.1.6-dev.2",
"version": "2.1.6-dev.3",
"description": "Matterbridge plugin manager for Matter",
"author": "https://github.com/Luligu",
"license": "Apache-2.0",
Expand Down Expand Up @@ -130,7 +130,7 @@
"cleanBuildProduction": "npm run clean && npm run buildProduction",
"deepClean": "npx rimraf tsconfig.tsbuildinfo package-lock.json npm-shrinkwrap.json ./dist ./node_modules",
"deepCleanBuild": "npm run deepClean && npm install && npm run build && npm link",
"runMeBeforePublish": "npm run deepCleanBuild && npm run lint && npm run format && npm run test:coverage",
"runMeBeforePublish": "npm run lint && npm run format && npm run build && npm run test",
"prepublishOnly": "npm pkg delete devDependencies scripts && npm install --omit=dev && npm shrinkwrap",
"prepublishOnlyProduction": "npm run cleanBuildProduction && npm pkg delete devDependencies scripts types && npx rimraf ./node_modules && npm install --omit=dev && npm shrinkwrap",
"npmPack": "copy package.json package.log && npm run prepublishOnlyProduction && npm pack && copy package.log package.json && npm run deepCleanBuild",
Expand Down
6 changes: 2 additions & 4 deletions src/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1094,15 +1094,13 @@ export class Frontend {
);
};
interval();
this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000); // 1 second
this.memoryInterval.unref();
this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000).unref(); // 1 second
this.memoryTimeout = setTimeout(
() => {
this.stopCpuMemoryDump();
},
getIntParameter('memorytimeout') ?? 600000, // 10 minutes
);
this.memoryTimeout.unref();
).unref();
}

private stopCpuMemoryDump() {
Expand Down
1 change: 1 addition & 0 deletions src/matterbridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2084,6 +2084,7 @@ export class Matterbridge extends EventEmitter {
sanitizeFabrics(serverNode.state.commissioning.fabrics, true);
}
this.frontend.wssSendRefreshRequired();
this.frontend.wssSendSnackbarMessage(`${storeId} is online`);
});

/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
Expand Down
47 changes: 31 additions & 16 deletions src/matterbridgePlatform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,18 +321,25 @@ describe('Matterbridge platform', () => {

it('should save the select', async () => {
let platform = new MatterbridgePlatform(matterbridge, new AnsiLogger({ logName: 'Matterbridge platform' }), { name: 'matterbridge-jest', type: 'type', debug: false, unregisterOnShutdown: false });
expect(platform.storage).toBeDefined();
expect(platform.context).toBeUndefined();
await waiter('storage and context', () => platform.context !== undefined);
await platform.ready;
expect(platform.storage).toBeDefined();
expect(platform.context).toBeDefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `MatterbridgePlatform for plugin matterbridge-jest is fully initialized`);
platform.selectDevice.clear();
platform.selectEntity.clear();
platform.selectDevice.set('serial', { serial: 'serial', name: 'name' });
platform.selectEntity.set('name', { name: 'name', description: 'description' });
for (let i = 1; i <= 100; i++) {
platform.selectDevice.set('serial' + i, { serial: 'serial' + i, name: 'name' + i });
platform.selectEntity.set('name' + i, { name: 'name' + i, description: 'description' + i });
}
expect(platform.selectDevice.size).toBe(100);
expect(platform.selectDevice.has('serial1')).toBeTruthy();
expect(platform.selectDevice.has('serial100')).toBeTruthy();
expect(platform.selectEntity.size).toBe(100);
expect(platform.selectEntity.has('name1')).toBeTruthy();
expect(platform.selectEntity.has('name100')).toBeTruthy();
await platform.onShutdown();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Saving 1 selectDevice...`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Saving 1 selectEntity...`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Saving 100 selectDevice...`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Saving 100 selectEntity...`);
loggerLogSpy.mockClear();

platform = new MatterbridgePlatform(matterbridge, new AnsiLogger({ logName: 'Matterbridge platform' }), { name: 'matterbridge-jest', type: 'type', debug: false, unregisterOnShutdown: false });
Expand All @@ -343,11 +350,15 @@ describe('Matterbridge platform', () => {

expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Loading selectDevice for plugin matterbridge-jest`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Loading selectEntity for plugin matterbridge-jest`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Loaded 1 selectDevice for plugin matterbridge-jest`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Loaded 1 selectEntity for plugin matterbridge-jest`);

expect(platform.selectDevice.has('serial')).toBeTruthy();
expect(platform.selectEntity.has('name')).toBeTruthy();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Loaded 100 selectDevice for plugin matterbridge-jest`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Loaded 100 selectEntity for plugin matterbridge-jest`);

expect(platform.selectDevice.size).toBe(100);
expect(platform.selectDevice.has('serial1')).toBeTruthy();
expect(platform.selectDevice.has('serial100')).toBeTruthy();
expect(platform.selectEntity.size).toBe(100);
expect(platform.selectEntity.has('name1')).toBeTruthy();
expect(platform.selectEntity.has('name100')).toBeTruthy();
platform.selectDevice.clear();
platform.selectEntity.clear();
await platform.onShutdown();
Expand Down Expand Up @@ -441,22 +452,26 @@ describe('Matterbridge platform', () => {
const testDevice = new MatterbridgeEndpoint(contactSensor, { uniqueStorageKey: 'test' }, true);
testDevice.createDefaultBasicInformationClusterServer('test', 'serial01234', 0xfff1, 'Matterbridge', 0x8001, 'Test device');
testDevice.addRequiredClusterServers();
const child1 = testDevice.addChildDeviceType('child1', [temperatureSensor], undefined, true);
const child1 = testDevice.addChildDeviceType('child1', temperatureSensor, undefined, true);
child1.addRequiredClusterServers();
child1.number = 201;
const child2 = testDevice.addChildDeviceType('child2', [humiditySensor], undefined, true);
const child2 = testDevice.addChildDeviceType('child2', humiditySensor, undefined, true);
child2.addRequiredClusterServers();
child2.number = 202;
const child3 = testDevice.addChildDeviceType('child3', humiditySensor, undefined, true);
child3.addRequiredClusterServers();
// child3.number = undefined;
await platform.registerDevice(testDevice);
testDevice.number = 101;
(matterbridge as any).devices.set(testDevice);
expect(await testDevice.getChildEndpoints()).toHaveLength(2);
expect(testDevice.getChildEndpoints()).toHaveLength(3);
jest.clearAllMocks();
expect(await platform.checkEndpointNumbers()).toBe(3);
expect(loggerLogSpy).not.toHaveBeenCalledWith(LogLevel.WARN, `Endpoint number for device ${CYAN}${testDevice.uniqueId}${wr} changed from ${CYAN}100${wr} to ${CYAN}101${wr}`);
expect(loggerLogSpy).not.toHaveBeenCalledWith(LogLevel.DEBUG, `Setting endpoint number for device ${CYAN}${testDevice.uniqueId}${db} to ${CYAN}${testDevice.maybeNumber}${db}`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Setting child endpoint number for device ${CYAN}${testDevice.uniqueId}${db}.${CYAN}child1${db} to ${CYAN}201${db}`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Setting child endpoint number for device ${CYAN}${testDevice.uniqueId}${db}.${CYAN}child2${db} to ${CYAN}202${db}`);
expect(loggerLogSpy).not.toHaveBeenCalledWith(LogLevel.DEBUG, `Setting child endpoint number for device ${CYAN}${testDevice.uniqueId}${db}.${CYAN}child3${db} to ${CYAN}202${db}`);
});

test('checkEndpointNumbers should validate the testDevice with child endpoints', async () => {
Expand All @@ -472,7 +487,7 @@ describe('Matterbridge platform', () => {
await platform.registerDevice(testDevice);
testDevice.number = 101;
(matterbridge as any).devices.set(testDevice);
expect(await testDevice.getChildEndpoints()).toHaveLength(2);
expect(testDevice.getChildEndpoints()).toHaveLength(2);
jest.clearAllMocks();
expect(await platform.checkEndpointNumbers()).toBe(3);
expect(loggerLogSpy).not.toHaveBeenCalledWith(LogLevel.WARN, expect.anything());
Expand Down
175 changes: 165 additions & 10 deletions src/pluginManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,6 @@ describe('PluginManager', () => {

const version = await plugins.install('matterbridge-example-accessory-platform');
expect(version).not.toBeUndefined();
// expect(version).not.toBeUndefined();
// console.error(`Plugin installed: ${version}`);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.INFO, `Installing plugin ${plg}matterbridge-example-accessory-platform${nf}`);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.INFO, `Installed plugin ${plg}matterbridge-example-accessory-platform${nf}`);
}, 300000);
Expand Down Expand Up @@ -381,21 +379,43 @@ describe('PluginManager', () => {
test('load default config plugin matterbridge-example-accessory-platform', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

const configFileName = path.join(matterbridge.matterbridgeDirectory, `matterbridge-example-accessory-platform.config.json`);
const deleteConfig = async () => {
try {
await fs.unlink(configFileName);
} catch (error) {
// Ignore error
}
};

expect(plugins.length).toBe(2);
const plugin = plugins.get('matterbridge-example-accessory-platform');
expect(plugin).not.toBeUndefined();
if (!plugin) return;
const configFile = path.join(matterbridge.matterbridgeDirectory, `${plugin.name}.config.json`);
try {
await fs.unlink(configFile);
} catch (error) {
// Ignore error
}

await deleteConfig();
jest.spyOn(fs, 'writeFile').mockImplementationOnce(async () => {
throw new Error('Test error');
});
let config = await plugins.loadConfig(plugin);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.DEBUG, `Created config file ${configFile} for plugin ${plg}${plugin.name}${db}.`);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.ERROR, `Error creating config file ${configFileName} for plugin ${plg}${plugin.name}${er}: Test error`);
loggerLogSpy.mockClear();

await deleteConfig();
jest.spyOn(fs, 'access').mockImplementationOnce(async () => {
throw new Error('Test error');
});
config = await plugins.loadConfig(plugin);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.ERROR, `Error accessing config file ${configFileName} for plugin ${plg}${plugin.name}${er}: Test error`);
loggerLogSpy.mockClear();

await deleteConfig();
config = await plugins.loadConfig(plugin);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.DEBUG, `Loaded config file ${configFile} for plugin ${plg}${plugin.name}${db}.`);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.DEBUG, `Created config file ${configFileName} for plugin ${plg}${plugin.name}${db}.`);
loggerLogSpy.mockClear();

config = await plugins.loadConfig(plugin);
expect((plugins as any).log.log).toHaveBeenCalledWith(LogLevel.DEBUG, `Loaded config file ${configFileName} for plugin ${plg}${plugin.name}${db}.`);
expect(config).not.toBeUndefined();
expect(config).not.toBeNull();
expect(config.name).toBe(plugin.name);
Expand Down Expand Up @@ -500,6 +520,22 @@ describe('PluginManager', () => {
expect((schema.properties as any).unregisterOnShutdown).toBeDefined();
}, 60000);

test('should not load plugin matterbridge-example-accessory-platform if parse error', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

const plugin = plugins.get('matterbridge-example-accessory-platform');
expect(plugin).not.toBeUndefined();
if (!plugin) return;

jest.spyOn(JSON, 'parse').mockImplementationOnce(() => {
throw new Error('Test error');
});
const platform = await plugins.load(plugin, false, 'Test with Jest');
expect(platform).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.ERROR, `Failed to load plugin ${plg}${plugin?.name}${er}: Test error`);
plugin.error = false;
});

test('load plugin matterbridge-example-accessory-platform', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

Expand Down Expand Up @@ -562,6 +598,80 @@ describe('PluginManager', () => {
expect(plugin.configured).toBe(true);
}, 60000);

test('should not start plugin matterbridge-example-accessory-platform if not loaded', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

const plugin = plugins.get('matterbridge-example-accessory-platform');
expect(plugin).not.toBeUndefined();
if (!plugin) return;
expect(plugin.loaded).toBeTruthy();
expect(plugin.started).toBeFalsy();
expect(plugin.configured).toBeFalsy();

plugin.loaded = false;
const result = await plugins.start(plugin, 'Test with Jest', false);
expect(result).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.ERROR, `Plugin ${plg}${plugin?.name}${er} not loaded`);
plugin.loaded = true;
});

test('should not start plugin matterbridge-example-accessory-platform if no platform', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

const plugin = plugins.get('matterbridge-example-accessory-platform');
expect(plugin).not.toBeUndefined();
if (!plugin) return;
expect(plugin.loaded).toBeTruthy();
expect(plugin.started).toBeFalsy();
expect(plugin.configured).toBeFalsy();

const platform = plugin.platform;
plugin.platform = undefined;
const result = await plugins.start(plugin, 'Test with Jest', false);
expect(result).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.ERROR, `Plugin ${plg}${plugin?.name}${er} no platform found`);
plugin.platform = platform;
});

test('should not start plugin matterbridge-example-accessory-platform if already started', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

const plugin = plugins.get('matterbridge-example-accessory-platform');
expect(plugin).not.toBeUndefined();
if (!plugin) return;
expect(plugin.loaded).toBeTruthy();
expect(plugin.started).toBeFalsy();
expect(plugin.configured).toBeFalsy();

plugin.started = true;
const result = await plugins.start(plugin, 'Test with Jest', false);
expect(result).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.ERROR, `Plugin ${plg}${plugin?.name}${er} already started`);
plugin.started = false;
});

test('should log start plugin matterbridge-example-accessory-platform if it throws', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

const plugin = plugins.get('matterbridge-example-accessory-platform');
expect(plugin).not.toBeUndefined();
if (!plugin) return;
expect(plugin.loaded).toBeTruthy();
expect(plugin.started).toBeFalsy();
expect(plugin.configured).toBeFalsy();
expect(plugin.platform).toBeDefined();
if (!plugin.platform) return;
jest.spyOn(plugin.platform, 'onStart').mockImplementationOnce(async () => {
throw new Error('Test error');
});
const result = await plugins.start(plugin, 'Test with Jest', false);
expect(result).toBeUndefined();
expect(plugin.error).toBeTruthy();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.INFO, `Starting plugin ${plg}${plugin.name}${nf} type ${typ}${plugin?.type}${nf}`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.ERROR, `Failed to start plugin ${plg}${plugin.name}${er}: Test error`);
plugin.error = false;
});

test('start plugin matterbridge-example-accessory-platform', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

Expand Down Expand Up @@ -662,6 +772,51 @@ describe('PluginManager', () => {
expect(plugin.configured).toBe(true);
}, 60000);

test('should not shutdown plugin matterbridge-example-accessory-platform', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

const plugin = plugins.get('matterbridge-example-accessory-platform');
expect(plugin).not.toBeUndefined();
if (!plugin) return;
expect(plugin.loaded).toBeTruthy();
expect(plugin.started).toBeTruthy();
expect(plugin.configured).toBeTruthy();

plugin.loaded = false;
let result = await plugins.shutdown(plugin, 'Test with Jest', true, false);
expect(result).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Plugin ${plg}${plugin.name}${db} not loaded`);
plugin.loaded = true;

loggerLogSpy.mockClear();
plugin.started = false;
result = await plugins.shutdown(plugin, 'Test with Jest', true, false);
expect(result).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Plugin ${plg}${plugin.name}${db} not started`);
plugin.started = true;

loggerLogSpy.mockClear();
const platform = plugin.platform;
plugin.platform = undefined;
plugin.configured = false;
result = await plugins.shutdown(plugin, 'Test with Jest', true, false);
expect(result).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Plugin ${plg}${plugin.name}${db} not configured`);
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.DEBUG, `Plugin ${plg}${plugin.name}${db} no platform found`);
plugin.configured = true;
plugin.platform = platform;

loggerLogSpy.mockClear();
expect(plugin.platform).toBeDefined();
if (!plugin.platform) return;
jest.spyOn(plugin.platform, 'onShutdown').mockImplementationOnce(async () => {
throw new Error('Test error');
});
result = await plugins.shutdown(plugin, 'Test with Jest', true, false);
expect(result).toBeUndefined();
expect(loggerLogSpy).toHaveBeenCalledWith(LogLevel.ERROR, `Failed to shut down plugin ${plg}${plugin.name}${er}: Test error`);
});

test('shutdown plugin matterbridge-example-accessory-platform', async () => {
if (matterbridge.systemInformation.osPlatform === 'darwin') return; // MacOS fails

Expand Down
Loading

0 comments on commit 8c3f719

Please sign in to comment.