diff --git a/.github/workflows/build-matterbridge-plugin.yml b/.github/workflows/build-matterbridge-plugin.yml
index 92ad2df..7080f77 100644
--- a/.github/workflows/build-matterbridge-plugin.yml
+++ b/.github/workflows/build-matterbridge-plugin.yml
@@ -21,6 +21,12 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
+ - name: Clean cache
+ run: npm cache clean --force
+
+ - name: Install latest npm
+ run: npm install -g npm@latest
+
- name: Verify Node.js version
run: node -v
@@ -42,14 +48,3 @@ jobs:
- name: Build the project
run: npm run build
- # - name: List, audit, fix outdated dependencies and build again
- # run: |
- # npm uninstall matterbridge
- # npm list --outdated
- # npm audit || true # ignore failures
- # npm audit fix || true
- # npm list --outdated
- # npm install -g matterbridge
- # npm install
- # npm run build
-
diff --git a/.github/workflows/publish-matterbridge-plugin.yml b/.github/workflows/publish-matterbridge-plugin.yml
index 6b6ab2b..5236b8e 100644
--- a/.github/workflows/publish-matterbridge-plugin.yml
+++ b/.github/workflows/publish-matterbridge-plugin.yml
@@ -18,6 +18,12 @@ jobs:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
+ - name: Clean cache
+ run: npm cache clean --force
+
+ - name: Install latest npm
+ run: npm install -g npm@latest
+
- name: Verify Node.js version
run: node -v
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1cb666b..96843d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,21 @@ If you like this project and find it useful, please consider giving it a star on
- Unless you are using docker (in that case all is already updated when you pull the image), please update Matterbridge to 1.5.4 to work with matterbridge-shelly >= 0.9.5. This is a one time issue due to the update to matter.js 0.10.0.
+## [0.9.9] - 2024-09-17
+
+### Changed
+
+- [matterbridge]: Removed Matterbridge deprecated method to get the child endpoints.
+- [package]: Updated dependencies.
+
+### Fixed
+
+- [shelly]: Fixed the bug in configure when postfix is used.
+
+
+
+
+
## [0.9.8] - 2024-09-13
### Added
diff --git a/package-lock.json b/package-lock.json
index eb237bb..765c862 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "matterbridge-shelly",
- "version": "0.9.8",
+ "version": "0.9.9-dev.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "matterbridge-shelly",
- "version": "0.9.8",
+ "version": "0.9.9-dev.1",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -21,9 +21,9 @@
"devDependencies": {
"@eslint/js": "9.10.0",
"@types/eslint__js": "8.42.3",
- "@types/jest": "29.5.12",
+ "@types/jest": "29.5.13",
"@types/multicast-dns": "7.2.4",
- "@types/node": "22.5.4",
+ "@types/node": "22.5.5",
"@types/ws": "8.5.12",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-jest": "28.8.3",
@@ -1617,9 +1617,9 @@
}
},
"node_modules/@types/jest": {
- "version": "29.5.12",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
- "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
+ "version": "29.5.13",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz",
+ "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1646,9 +1646,9 @@
}
},
"node_modules/@types/node": {
- "version": "22.5.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
- "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
+ "version": "22.5.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
+ "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
diff --git a/package.json b/package.json
index ef46299..2fc90d9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "matterbridge-shelly",
- "version": "0.9.8",
+ "version": "0.9.9",
"description": "Matterbridge shelly plugin",
"author": "https://github.com/Luligu",
"license": "Apache-2.0",
@@ -64,8 +64,8 @@
"test:utils": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/utils.spec.ts --coverage",
"test:shellyProperty": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/shellyProperty.test.ts --coverage",
"test:shellyComponent": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/shellyComponent.test.ts --coverage",
- "test:mock": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/shellyDevice.mock.test.ts --coverage",
- "test:real": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/shellyDevice.real.test.ts --coverage",
+ "test:mock": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/shellyDevice.mock.test.ts --runInBand --coverage",
+ "test:real": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/shellyDevice.real.test.ts --runInBand --coverage",
"test:shelly": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js src/shelly.test.ts --coverage",
"lint": "eslint --max-warnings=0 .",
"lint:fix": "eslint --fix --max-warnings=0 .",
@@ -75,7 +75,7 @@
"cleanBuild": "npm run clean && tsc",
"deepClean": "rimraf tsconfig.tsbuildinfo package-lock.json ./dist ./node_modules",
"deepCleanRebuild": "npm run deepClean && npm install && npm run build",
- "prepublishOnly": "npm run lint && npm run cleanBuild && npm shrinkwrap",
+ "prepublishOnly": "npm run lint && npm run cleanBuild && npm shrinkwrap --omit=dev",
"checkDependencies": "npx npm-check-updates",
"updateDependencies": "npx npm-check-updates -u && npm install & npm run cleanBuild",
"preversion": "npm run build && npm run lint",
@@ -110,9 +110,9 @@
"devDependencies": {
"@eslint/js": "9.10.0",
"@types/eslint__js": "8.42.3",
- "@types/jest": "29.5.12",
+ "@types/jest": "29.5.13",
"@types/multicast-dns": "7.2.4",
- "@types/node": "22.5.4",
+ "@types/node": "22.5.5",
"@types/ws": "8.5.12",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-jest": "28.8.3",
diff --git a/src/platform.ts b/src/platform.ts
index 8a05dd6..e2ef3eb 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -462,6 +462,9 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
mbDevice.addCommandHandler('off', async (data) => {
this.shellyLightCommandHandler(mbDevice, data.endpoint.number, device, 'Off', false);
});
+ mbDevice.addCommandHandler('toggle', async (data) => {
+ this.shellyLightCommandHandler(mbDevice, data.endpoint.number, device, 'Toggle', false);
+ });
// Add event handler
switchComponent.on('update', (component: string, property: string, value: ShellyDataType) => {
@@ -802,12 +805,20 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
override async onConfigure() {
this.log.info(`Configuring platform ${idn}${this.config.name}${rs}${nf}`);
this.bridgedDevices.forEach(async (mbDevice) => {
- if (!mbDevice.serialNumber) return;
- this.log.info(`Configuring device ${dn}${mbDevice.deviceName}${nf} shelly ${hk}${mbDevice.serialNumber}${nf}`);
- const shellyDevice = this.shelly.getDevice(mbDevice.serialNumber);
- if (!shellyDevice) return;
+ if (!mbDevice.serialNumber) {
+ this.log.error(`Shelly device ${dn}${mbDevice.deviceName}${er} has no serial number`);
+ return;
+ }
+ const serial = isValidString(this.config.postfix, 1, 3) ? mbDevice.serialNumber.replace('-' + this.config.postfix, '') : mbDevice.serialNumber;
+ this.log.info(`Configuring device ${dn}${mbDevice.deviceName}${nf} shelly ${hk}${serial}${nf}`);
+ const shellyDevice = this.shelly.getDevice(serial);
+ if (!shellyDevice) {
+ this.log.error(`Shelly device with serial number ${hk}${serial}${er} not found`);
+ return;
+ }
mbDevice.getChildEndpoints().forEach(async (childEndpoint) => {
- const label = mbDevice.getEndpointLabel(childEndpoint.number);
+ // const label = mbDevice.getEndpointLabel(childEndpoint.number);
+ const label = childEndpoint.uniqueStorageKey;
// Configure the cluster OnOff attribute onOff
if (label?.startsWith('switch') || label?.startsWith('relay') || label?.startsWith('light') || label?.startsWith('rgb')) {
const switchComponent = shellyDevice.getComponent(label) as ShellySwitchComponent;
@@ -1026,7 +1037,8 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
return false;
}
// Get the Shelly switch component
- const componentName = matterbridgeDevice.getEndpointLabel(endpointNumber);
+ // const componentName = matterbridgeDevice.getEndpointLabel(endpointNumber);
+ const componentName = endpoint.uniqueStorageKey;
if (!componentName) {
shellyDevice.log.error(`shellyCommandHandler error: componentName not found for shelly device ${dn}${shellyDevice?.id}${er}`);
return false;
@@ -1081,7 +1093,8 @@ export class ShellyPlatform extends MatterbridgeDynamicPlatform {
return false;
}
// Get the Shelly cover component
- const componentName = matterbridgeDevice.getEndpointLabel(endpointNumber);
+ // const componentName = matterbridgeDevice.getEndpointLabel(endpointNumber);
+ const componentName = endpoint.uniqueStorageKey;
if (!componentName) {
shellyDevice.log.error(`shellyCoverCommandHandler error: endpointName not found for shelly device ${dn}${shellyDevice?.id}${er}`);
return false;
diff --git a/src/shellyDevice.real.test.ts b/src/shellyDevice.real.test.ts
index b43c4ed..32397a6 100644
--- a/src/shellyDevice.real.test.ts
+++ b/src/shellyDevice.real.test.ts
@@ -1,24 +1,24 @@
-/* eslint-disable no-console */
-/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable jest/no-conditional-expect */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Shelly } from './shelly.js';
import { ShellyDevice } from './shellyDevice.js';
// import { ShellyCoverComponent, ShellySwitchComponent } from './shellyComponent';
-import { AnsiLogger, TimestampFormat } from 'matterbridge/logger';
-import { getMacAddress } from 'matterbridge/utils';
+import { AnsiLogger, LogLevel, TimestampFormat } from 'matterbridge/logger';
+import { getMacAddress, wait, waiter } from 'matterbridge/utils';
import { jest } from '@jest/globals';
-import { ShellyCoverComponent, ShellySwitchComponent } from './shellyComponent.js';
+import { isCoverComponent, isLightComponent, isSwitchComponent, ShellyCoverComponent, ShellySwitchComponent } from './shellyComponent.js';
describe('Shellies', () => {
let consoleLogSpy: jest.SpiedFunction;
- const log = new AnsiLogger({ logName: 'shellyDeviceTest', logTimestampFormat: TimestampFormat.TIME_MILLIS, logDebug: false });
+ const log = new AnsiLogger({ logName: 'ShellyDeviceRealTest', logTimestampFormat: TimestampFormat.TIME_MILLIS, logLevel: LogLevel.DEBUG });
const shelly = new Shelly(log, 'admin', 'tango');
- const firmwareGen1 = '1.14.0-gcb84623';
+ const firmwareGen1 = 'v1.14.0-gcb84623';
const firmwareGen2 = '1.4.2-gc2639da';
beforeAll(() => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation((...args: any[]) => {
// console.error(`Mocked console.log: ${args}`);
});
@@ -33,7 +33,7 @@ describe('Shellies', () => {
});
afterAll(() => {
- //
+ shelly.destroy();
});
test('Create AnsiLogger and Shelly', () => {
@@ -53,44 +53,25 @@ describe('Shellies', () => {
}, 300000);
});
- describe('create real gen 1 shellydimmer2 219', () => {
- if (getMacAddress() !== '30:f6:ef:69:2b:c5') return;
- test('create a gen 1 device and update', async () => {
- const device = await ShellyDevice.create(shelly, log, '192.168.1.219');
- if (!device) return;
- expect(device).not.toBeUndefined();
- expect(device?.gen).toBe(1);
- expect(device?.host).toBe('192.168.1.219');
- expect(device?.model).toBe('SHDM-2');
- expect(device?.id).toBe('shellydimmer2-98CDAC0D01BB');
- expect(device?.firmware).toBe('v1.14.0-gcb84623');
- expect(device?.auth).toBe(true);
-
- await device.fetchUpdate();
-
- device.destroy();
- });
- });
-
describe('create real gen 2 shellyplus1pm 217', () => {
if (getMacAddress() !== '30:f6:ef:69:2b:c5') return;
test('create a gen 2 device and update', async () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.217');
if (!device) return;
expect(device).not.toBeUndefined();
- expect(device?.gen).toBe(2);
- expect(device?.host).toBe('192.168.1.217');
- expect(device?.model).toBe('SNSW-001P16EU');
- expect(device?.id).toBe('shellyplus1pm-441793D69718');
- expect(device?.firmware).toBe(firmwareGen2);
- expect(device?.auth).toBe(false);
+ expect(device.gen).toBe(2);
+ expect(device.host).toBe('192.168.1.217');
+ expect(device.model).toBe('SNSW-001P16EU');
+ expect(device.id).toBe('shellyplus1pm-441793D69718');
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
await device.fetchUpdate();
device.destroy();
});
- test('send legacy command to a gen 2 device and update', async () => {
+ test('send legacy command to a gen 2 shellyplus1pm device and update', async () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.217');
expect(device).not.toBeUndefined();
if (!device) return;
@@ -110,7 +91,7 @@ describe('Shellies', () => {
expect(outputProp).not.toBeUndefined();
const state = stateProp?.value;
const output = outputProp?.value;
- console.log(`state: ${state} output: ${output}`);
+ // console.log(`state: ${state} output: ${output}`);
expect(state === output).toBeTruthy();
const response = await ShellyDevice.fetch(shelly, log, '192.168.1.217', 'relay/0', { 'turn': 'toggle' });
expect(response).not.toBeUndefined();
@@ -119,15 +100,15 @@ describe('Shellies', () => {
expect(stateProp2).not.toBeUndefined();
const outputProp2 = component.getProperty('output');
expect(outputProp2).not.toBeUndefined();
- console.log(`state2: ${stateProp2?.value} output2: ${outputProp2?.value}`);
+ // console.log(`state2: ${stateProp2?.value} output2: ${outputProp2?.value}`);
expect(stateProp2?.value === outputProp2?.value).toBeTruthy();
- console.log(`state: ${state} state2: ${stateProp2?.value}`);
+ // console.log(`state: ${state} state2: ${stateProp2?.value}`);
expect(state === stateProp2?.value).toBeTruthy();
expect(stateProp?.value === stateProp2?.value).toBeTruthy();
device.destroy();
});
- test('send rpc command to a gen 2 device', async () => {
+ test('send rpc command to a gen 2 shellyplus1pm device', async () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.217');
expect(device).not.toBeUndefined();
if (!device) return;
@@ -148,10 +129,12 @@ describe('Shellies', () => {
expect(response).not.toBeUndefined();
await device.fetchUpdate();
response = await ShellyDevice.fetch(shelly, log, '192.168.1.217', 'Switch.Set', { 'id': 0, 'on': false });
+ expect(response).not.toBeUndefined();
+ await device.fetchUpdate();
device.destroy();
});
- test('send legacy command relay to a gen 2 device', async () => {
+ test('send legacy command relay to a gen 2 shellyplus1pm device', async () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.217');
expect(device).not.toBeUndefined();
if (!device) return;
@@ -174,7 +157,7 @@ describe('Shellies', () => {
device.destroy();
});
- test('execute On() Off() Toggle() for a gen 2 device', async () => {
+ test('execute On() Off() Toggle() for a gen 2 shellyplus1pm device', async () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.217');
expect(device).not.toBeUndefined();
if (!device) return;
@@ -196,7 +179,7 @@ describe('Shellies', () => {
device.destroy();
});
- test('execute Open() CLose() Stop() for a gen 2 device', async () => {
+ test('execute Open() Close() Stop() for a gen 2 device', async () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.218');
expect(device).not.toBeUndefined();
if (!device) return;
@@ -242,16 +225,16 @@ describe('Shellies', () => {
const outputP = component.getProperty('output');
const state = stateP?.value;
const output = outputP?.value;
- console.log(`state: ${state} output: ${output}`);
+ // console.log(`state: ${state} output: ${output}`);
expect(state === output).toBeTruthy();
const res = await ShellyDevice.fetch(shelly, log, '192.168.1.218', 'relay/1', { 'turn': 'toggle' });
expect(res).not.toBeUndefined();
await device.fetchUpdate();
const state2 = component.getProperty('state');
const output2 = component.getProperty('output');
- console.log(`state2: ${state2?.value} output2: ${output2?.value}`);
+ // console.log(`state2: ${state2?.value} output2: ${output2?.value}`);
expect(state2?.value === output2?.value).toBeTruthy();
- console.log(`state: ${state} state2: ${state2?.value}`);
+ // console.log(`state: ${state} state2: ${state2?.value}`);
expect(state === state2?.value).toBeTruthy();
expect(stateP?.value === state2?.value).toBeTruthy();
device.destroy();
@@ -261,25 +244,335 @@ describe('Shellies', () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.218');
expect(device).not.toBeUndefined();
if (!device) return;
- console.log('send wrong command to a gen 2 device and update');
+ // console.log('send wrong command to a gen 2 device and update');
let res = await ShellyDevice.fetch(shelly, log, '192.168.1.218', 'relay/5', { 'turn': 'toggle' });
expect(res).toBeNull();
- console.log('send wrong command to a gen 2 device and update');
+ // console.log('send wrong command to a gen 2 device and update');
res = await ShellyDevice.fetch(shelly, log, '192.168.1.218', 'relay/0', { 'turn': 'toggle' });
expect(res).toBeNull();
device.destroy();
});
});
- describe('create real gen 3 shelly1minig3 221 with auth', () => {
+ describe('test real gen 1 shellydimmer2 119 with auth', () => {
if (getMacAddress() !== '30:f6:ef:69:2b:c5') return;
- test('send command to a gen 2 device and update', async () => {
+ test('Create a gen 1 shellydimmer2 device and update', async () => {
+ const device = await ShellyDevice.create(shelly, log, '192.168.1.219');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ expect(device.host).toBe('192.168.1.219');
+ expect(device.model).toBe('SHDM-2');
+ expect(device.id).toBe('shellydimmer2-98CDAC0D01BB');
+ expect(device.firmware).toBe(firmwareGen1);
+ expect(device.auth).toBe(true);
+ expect(device.gen).toBe(1);
+ expect(device.username).toBe('admin');
+ expect(device.password).toBe('tango');
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ device.destroy();
+ }, 60000);
+
+ test('send command to a gen 1 shellydimmer2 device', async () => {
+ const device = await ShellyDevice.create(shelly, log, '192.168.1.219');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+
+ const component = device.getComponent('light:0');
+ expect(component).not.toBeUndefined();
+
+ if (isLightComponent(component)) {
+ component.On();
+ await waiter(
+ 'On',
+ () => {
+ return component.getValue('state') === true;
+ },
+ false,
+ 10000,
+ );
+
+ component.Level(100);
+ await waiter(
+ 'Level(100)',
+ () => {
+ return component.getValue('brightness') === 100;
+ },
+ false,
+ 10000,
+ );
+
+ component.Off();
+ await waiter(
+ 'Off',
+ () => {
+ return component.getValue('state') === false;
+ },
+ false,
+ 10000,
+ );
+
+ component.Level(50);
+ await waiter(
+ 'Level(50)',
+ () => {
+ return component.getValue('brightness') === 50;
+ },
+ false,
+ 10000,
+ );
+
+ component.Toggle();
+ await waiter(
+ 'Toggle',
+ () => {
+ return component.getValue('state') === true;
+ },
+ false,
+ 10000,
+ );
+
+ component.Off();
+ await waiter(
+ 'Off',
+ () => {
+ return component.getValue('state') === false;
+ },
+ false,
+ 10000,
+ );
+
+ component.Level(1);
+ await waiter(
+ 'Level(1)',
+ () => {
+ return component.getValue('brightness') === 1;
+ },
+ false,
+ 10000,
+ );
+ }
+
+ device.destroy();
+ }, 60000);
+ });
+
+ describe('test real gen 2 shellyplus1pm 217 with auth', () => {
+ if (getMacAddress() !== '30:f6:ef:69:2b:c5') return;
+
+ test('create a gen 2 shellyplus1pm device and update', async () => {
+ const device = await ShellyDevice.create(shelly, log, '192.168.1.217');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ expect(device.gen).toBe(2);
+ expect(device.host).toBe('192.168.1.217');
+ expect(device.model).toBe('SNSW-001P16EU');
+ expect(device.mac).toBe('441793D69718');
+ expect(device.id).toBe('shellyplus1pm-441793D69718');
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ device.destroy();
+ }, 60000);
+
+ test('send command to a gen 2 shellyplus1pm device', async () => {
+ const device = await ShellyDevice.create(shelly, log, '192.168.1.217');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter(
+ 'On',
+ () => {
+ return component.getValue('state') === true;
+ },
+ false,
+ 10000,
+ );
+
+ component.Off();
+ await waiter(
+ 'Off',
+ () => {
+ return component.getValue('state') === false;
+ },
+ false,
+ 10000,
+ );
+
+ component.Toggle();
+ await waiter(
+ 'Toggle',
+ () => {
+ return component.getValue('state') === true;
+ },
+ false,
+ 10000,
+ );
+
+ component.Off();
+ await waiter(
+ 'Off',
+ () => {
+ return component.getValue('state') === false;
+ },
+ false,
+ 10000,
+ );
+ }
+
+ device.destroy();
+ }, 60000);
+ });
+
+ describe('test real gen 2 shellyplus2pm 218 with auth', () => {
+ if (getMacAddress() !== '30:f6:ef:69:2b:c5') return;
+
+ test('create a gen 2 shellyplus2pm device and update', async () => {
+ const device = await ShellyDevice.create(shelly, log, '192.168.1.218');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+ expect(device.gen).toBe(2);
+ expect(device.host).toBe('192.168.1.218');
+ expect(device.model).toBe('SNSW-102P16EU');
+ expect(device.mac).toBe('5443B23D81F8');
+ expect(device.id).toBe('shellyplus2pm-5443B23D81F8');
+ expect(device.firmware).toBe(firmwareGen2);
+ expect(device.auth).toBe(false);
+
+ await device.fetchUpdate();
+
+ await device.saveDevicePayloads('temp');
+
+ device.destroy();
+ }, 60000);
+
+ test('send command to a gen 2 shellyplus2pm device', async () => {
+ const device = await ShellyDevice.create(shelly, log, '192.168.1.218');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+
+ const cover = device.getComponent('cover:0');
+ expect(cover).not.toBeUndefined();
+
+ if (isCoverComponent(cover)) {
+ cover.Open();
+ await waiter(
+ 'Open()',
+ () => {
+ return cover.getValue('state') === 'opening';
+ },
+ false,
+ 20000,
+ );
+ await waiter(
+ 'Open()',
+ () => {
+ return cover.getValue('state') === 'stopped';
+ },
+ false,
+ 20000,
+ );
+ await wait(2000);
+ await device.fetchUpdate();
+ expect(cover.getValue('source')).toMatch(/^(limit_switch|timeout)$/); // 'limit_switch' if not stopped for timeout
+ expect(cover.getValue('state')).toBe('stopped'); // 'open' if not stopped for timeout
+ expect(cover.getValue('last_direction')).toBe('open');
+ expect(cover.getValue('current_pos')).toBe(100);
+
+ cover.Stop();
+ await waiter(
+ 'Stop()',
+ () => {
+ return cover.getValue('state') === 'stopped';
+ },
+ false,
+ 20000,
+ );
+ await wait(2000);
+ await device.fetchUpdate();
+ expect(cover.getValue('state')).toBe('stopped');
+ expect(cover.getValue('last_direction')).toBe('open');
+ expect(cover.getValue('current_pos')).toBe(100);
+
+ cover.Close();
+ await waiter(
+ 'Close()',
+ () => {
+ return cover.getValue('state') === 'closing';
+ },
+ false,
+ 20000,
+ );
+ await waiter(
+ 'Close()',
+ () => {
+ return cover.getValue('state') === 'stopped';
+ },
+ false,
+ 20000,
+ );
+ await wait(2000);
+ await device.fetchUpdate();
+ expect(cover.getValue('source')).toBe('timeout'); // 'limit_switch' if not stopped for timeout
+ expect(cover.getValue('state')).toBe('stopped'); // 'open' if not stopped for timeout
+ expect(cover.getValue('last_direction')).toBe('close');
+ expect(cover.getValue('current_pos')).toBe(0);
+
+ cover.GoToPosition(10);
+ await waiter(
+ 'GoToPosition(10)',
+ () => {
+ return cover.getValue('state') === 'opening';
+ },
+ false,
+ 20000,
+ );
+ await waiter(
+ 'GoToPosition(10)',
+ () => {
+ return cover.getValue('state') === 'stopped';
+ },
+ false,
+ 20000,
+ );
+ await wait(2000);
+ await device.fetchUpdate();
+ expect(cover.getValue('source')).toBe('timeout');
+ expect(cover.getValue('state')).toBe('stopped');
+ expect(cover.getValue('last_direction')).toBe('open');
+ expect(cover.getValue('current_pos')).toBe(10);
+
+ // Close for next test
+ cover.Close();
+ await wait(2000);
+ }
+ device.destroy();
+ }, 120000);
+ });
+
+ describe('test real gen 3 shelly1minig3 221 with auth', () => {
+ if (getMacAddress() !== '30:f6:ef:69:2b:c5') return;
+
+ test('create a gen 3 shelly1minig3 device and update', async () => {
const device = await ShellyDevice.create(shelly, log, '192.168.1.221');
expect(device).not.toBeUndefined();
if (!device) return;
expect(device.gen).toBe(3);
expect(device.host).toBe('192.168.1.221');
expect(device.model).toBe('S3SW-001X8EU');
+ expect(device.mac).toBe('543204547478');
expect(device.id).toBe('shelly1minig3-543204547478');
expect(device.firmware).toBe(firmwareGen2);
expect(device.auth).toBe(true);
@@ -289,6 +582,56 @@ describe('Shellies', () => {
await device.saveDevicePayloads('temp');
device.destroy();
- });
+ }, 60000);
+
+ test('send command to a gen 3 shelly1minig3 device', async () => {
+ const device = await ShellyDevice.create(shelly, log, '192.168.1.221');
+ expect(device).not.toBeUndefined();
+ if (!device) return;
+
+ const component = device.getComponent('switch:0');
+ expect(component).not.toBeUndefined();
+
+ if (isSwitchComponent(component)) {
+ component.On();
+ await waiter(
+ 'On',
+ () => {
+ return component.getValue('state') === true;
+ },
+ false,
+ 10000,
+ );
+ component.Off();
+ await waiter(
+ 'Off',
+ () => {
+ return component.getValue('state') === false;
+ },
+ false,
+ 10000,
+ );
+ component.Toggle();
+ await waiter(
+ 'Toggle',
+ () => {
+ return component.getValue('state') === true;
+ },
+ false,
+ 10000,
+ );
+ component.Off();
+ await waiter(
+ 'Off',
+ () => {
+ return component.getValue('state') === false;
+ },
+ false,
+ 10000,
+ );
+ }
+
+ device.destroy();
+ }, 60000);
});
});