Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update 09.01.25 #293

Merged
merged 6 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
versionBump:
description: 'Type of version bump'
required: true
default: 'prerelease'
default: 'patch'
type: choice
options:
- prerelease
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,10 @@ TBD
### **WORK IN PROGRESS**
* (@GermanBluefox) Fixed GUI errors
* (@GermanBluefox) Added `Controller fabric label` to configuration
* (@GermanBluefox) Solution for QR-Code scanning on non HTTPS pages
* (@GermanBluefox) Added solution for QR-Code scanning on non HTTPS pages
* (@Apollon77) Fixed Generic Switch Device type for controller
* (@Apollon77) Fixed Controller BLE initialization and activation
* (@Apollon77) Added serialNumber to all devices and bridges for better device re-detection by controllers

### 0.3.4 (2024-12-31)
* (@Apollon77) Updates matter.js to address several issues
Expand Down
386 changes: 108 additions & 278 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,35 @@
"url": "https://github.com/ioBroker/ioBroker.matter"
},
"optionalDependencies": {
"@matter/nodejs-ble": "0.12.0-alpha.0-20241231-9ac20db97"
"@matter/nodejs-ble": "0.12.0-alpha.0-20250108-7ae2a767d"
},
"dependencies": {
"@iobroker/adapter-core": "^3.2.3",
"@iobroker/i18n": "^0.3.1",
"@iobroker/dm-utils": "^0.6.11",
"@iobroker/type-detector": "^4.1.1",
"@matter/main": "0.12.0-alpha.0-20241231-9ac20db97",
"@matter/nodejs": "0.12.0-alpha.0-20241231-9ac20db97",
"@project-chip/matter.js": "0.12.0-alpha.0-20241231-9ac20db97",
"@matter/main": "0.12.0-alpha.0-20250108-7ae2a767d",
"@matter/nodejs": "0.12.0-alpha.0-20250108-7ae2a767d",
"@project-chip/matter.js": "0.12.0-alpha.0-20250108-7ae2a767d",
"axios": "^1.7.9",
"jsonwebtoken": "^9.0.2"
},
"devDependencies": {
"@alcalzone/release-script": "^3.8.0",
"@alcalzone/release-script-plugin-iobroker": "^3.7.2",
"@alcalzone/release-script-plugin-license": "^3.7.0",
"@iobroker/build-tools": "^2.0.12",
"@iobroker/build-tools": "^2.0.14",
"@iobroker/dev-server": "^0.7.3",
"@iobroker/eslint-config": "^1.0.0",
"@iobroker/legacy-testing": "^2.0.1",
"@iobroker/types": "^7.0.6",
"@types/jsonwebtoken": "^9.0.7",
"@types/node": "^22.10.2",
"@types/node": "^22.10.5",
"chai": "^4.5.0",
"colorette": "^2.0.20",
"mocha": "^11.0.1",
"puppeteer": "^23.11.1",
"typescript": "~5.7.2"
"typescript": "~5.7.3"
},
"bugs": {
"url": "https://github.com/ioBroker/ioBroker.matter/issues"
Expand Down
50 changes: 25 additions & 25 deletions src-admin/src/Tabs/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,31 @@ class Options extends Component<OptionsProps, OptionsState> {
</FormControl>
</Box>

<div style={{ marginTop: 50 }}>
<Typography sx={styles.header}>{I18n.t('Controller Settings')}</Typography>
<TextField
variant="standard"
label={I18n.t('Controller fabric label')}
value={this.props.native.controllerFabricLabel}
type="text"
onChange={e => this.props.onChange('controllerFabricLabel', e.target.value)}
margin="normal"
slotProps={{
input: {
endAdornment: this.props.native.controllerFabricLabel ? (
<IconButton
size="small"
onClick={() => this.props.onChange('controllerFabricLabel', '')}
>
<Clear />
</IconButton>
) : null,
},
}}
style={styles.input}
/>
</div>

<div style={{ marginTop: 50 }}>
<Typography sx={styles.header}>{I18n.t('Cloud Account')}</Typography>
<InfoBox type="info">
Expand Down Expand Up @@ -572,31 +597,6 @@ class Options extends Component<OptionsProps, OptionsState> {
/>
</div>

<div style={{ marginTop: 50 }}>
<Typography sx={styles.header}>{I18n.t('Controller Settings')}</Typography>
<TextField
variant="standard"
label={I18n.t('Controller fabric label')}
value={this.props.native.controllerFabricLabel}
type="text"
onChange={e => this.props.onChange('controllerFabricLabel', e.target.value)}
margin="normal"
slotProps={{
input: {
endAdornment: this.props.native.controllerFabricLabel ? (
<IconButton
size="small"
onClick={() => this.props.onChange('controllerFabricLabel', '')}
>
<Clear />
</IconButton>
) : null,
},
}}
style={styles.input}
/>
</div>

<div style={{ marginTop: 50 }}>
<Typography sx={styles.header}>{I18n.t('Maintenance Settings')}</Typography>
<Button
Expand Down
5 changes: 4 additions & 1 deletion src/matter/BridgedDevicesNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class BridgedDevices extends BaseServerNode {
if (mappingDevice) {
const name = mappingDevice.name;
const endpoints = mappingDevice.matterEndpoints;
const serialNumber = deviceOptions.uuid.replace(/-/g, '');
if (endpoints.length === 1 || deviceOptions.noComposed) {
let erroredCount = 0;
// When only one endpoint or non-composed we simply add all endpoints for itself to the bridge
Expand All @@ -94,6 +95,7 @@ class BridgedDevices extends BaseServerNode {
nodeLabel: matterName,
productName: matterName,
productLabel: name.substring(0, 64),
serialNumber,
uniqueId: md5(endpoint.id),
reachable: true,
});
Expand Down Expand Up @@ -133,6 +135,7 @@ class BridgedDevices extends BaseServerNode {
nodeLabel: matterName,
productName: matterName,
productLabel: name.substring(0, 64),
serialNumber,
uniqueId: md5(id),
reachable: true,
},
Expand Down Expand Up @@ -189,7 +192,7 @@ class BridgedDevices extends BaseServerNode {
const vendorId = this.#parameters.vendorId; // 0xfff1;
const productId = this.#parameters.productId; // 0x8000;

const uniqueId = this.#parameters.uuid.replace(/-/g, '').split('.').pop();
const uniqueId = this.#parameters.uuid.replace(/-/g, '');
if (uniqueId === undefined) {
throw new Error(`Could not determine device unique id from ${this.#parameters.uuid}`);
}
Expand Down
51 changes: 30 additions & 21 deletions src/matter/ControllerNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,37 +64,46 @@ class Controller implements GeneralNode {
}

init(): void {
this.applyConfiguration(this.#parameters);
}

applyConfiguration(config: MatterControllerConfig): MessageResponse {
const currentConfig: MatterControllerConfig = {
enabled: true,
defaultExposeMatterApplicationClusterData: false,
defaultExposeMatterSystemClusterData: false,
...(this.#parameters as Partial<MatterControllerConfig>),
};

this.#useBle = false;
if (config.ble !== currentConfig.ble || config.hciId !== currentConfig.hciId) {
if (this.#parameters.ble) {
if (
config.ble &&
((config.wifiSSID && config.wifiPassword) ||
(config.threadNetworkName !== undefined && config.threadOperationalDataSet !== undefined))
(this.#parameters.wifiSSID && this.#parameters.wifiPassword) ||
(this.#parameters.threadNetworkName !== undefined &&
this.#parameters.threadOperationalDataSet !== undefined)
) {
try {
const hciId = config.hciId === undefined ? undefined : parseInt(config.hciId);
const hciId = this.#parameters.hciId === undefined ? undefined : parseInt(this.#parameters.hciId);
Ble.get = singleton(() => new NodeJsBle({ hciId }));
this.#useBle = true;
} catch (error) {
this.#adapter.log.warn(`Failed to initialize BLE: ${error.message}`);
config.ble = false;
return {
error: `Can not adjust configuration and enable BLE because of error: ${error.message}`,
};
this.#parameters.ble = false;
return;
}
} else {
this.#adapter.log.warn(
`BLE enabled but no WiFi or Thread configuration provided. BLE will stay disabled.`,
);
this.#parameters.ble = false;
}
}
this.applyConfiguration(this.#parameters, true);
}

applyConfiguration(config: MatterControllerConfig, isInit = false): MessageResponse {
const currentConfig: MatterControllerConfig = {
enabled: true,
defaultExposeMatterApplicationClusterData: false,
defaultExposeMatterSystemClusterData: false,
...(this.#parameters as Partial<MatterControllerConfig>),
};

if (!isInit && (config.ble !== currentConfig.ble || config.hciId !== currentConfig.hciId)) {
this.#adapter.setTimeout(() => this.#adapter.restart(), 5000);
// Restart of the adapter needed
return {
error: `BLE configuration adjusted. The adapter will restart in 5 seconds.`,
};
}
this.#parameters = config;
return { result: true };
}
Expand Down
2 changes: 1 addition & 1 deletion src/matter/DeviceNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Device extends BaseServerNode {
const productName = this.#deviceOptions.name || this.#parameters.productName;
const productId = this.#parameters.productId; // 0x8000;

const uniqueId = this.#parameters.uuid.replace(/-/g, '').split('.').pop();
const uniqueId = this.#parameters.uuid.replace(/-/g, '');
if (uniqueId === undefined) {
throw new Error(`Could not determine device unique id from ${this.#parameters.uuid}`);
}
Expand Down
57 changes: 29 additions & 28 deletions src/matter/to-iobroker/GenericSwitchToIoBroker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,51 +38,52 @@ export class GenericSwitchToIoBroker extends GenericDeviceToIoBroker {
this.#ioBrokerDevice = new ButtonSensor(
{ ...ChannelDetector.getPatterns().buttonSensor, isIoBrokerDevice: false } as DetectedDevice,
adapter,
this.enableDeviceTypeStates(),
this.enableMomentarySwitchDeviceTypeStates(),
);
} else {
// A Latching Switch (only other option) is mapped to a Socket
this.#ioBrokerDevice = new Socket(
{ ...ChannelDetector.getPatterns().socket, isIoBrokerDevice: false } as DetectedDevice,
adapter,
this.enableDeviceTypeStates(),
this.enableLatchingSwitchDeviceTypeStates(),
);
}
}

protected enableDeviceTypeStates(): DeviceOptions {
if (this.#ioBrokerDevice instanceof ButtonSensor) {
this.enableDeviceTypeStateForAttribute(PropertyType.Press, {
protected enableMomentarySwitchDeviceTypeStates(): DeviceOptions {
this.enableDeviceTypeStateForAttribute(PropertyType.Press, {
endpointId: this.appEndpoint.getNumber(),
clusterId: Switch.Cluster.id,
attributeName: 'currentPosition',
convertValue: value => value !== 0,
});

const hasLongPress = this.appEndpoint.getClusterClient(Switch.Complete)?.supportedFeatures
.momentarySwitchLongPress;
if (hasLongPress) {
this.enableDeviceTypeStateForEvent(PropertyType.PressLong, {
endpointId: this.appEndpoint.getNumber(),
clusterId: Switch.Cluster.id,
attributeName: 'currentPosition',
convertValue: value => value !== 0,
eventName: 'longPress',
convertValue: () => true,
});

const hasLongPress = this.appEndpoint.getClusterClient(Switch.Complete)?.supportedFeatures
.momentarySwitchLongPress;
if (hasLongPress) {
this.enableDeviceTypeStateForEvent(PropertyType.PressLong, {
endpointId: this.appEndpoint.getNumber(),
clusterId: Switch.Cluster.id,
eventName: 'longPress',
convertValue: () => true,
});
this.enableDeviceTypeStateForEvent(PropertyType.PressLong, {
endpointId: this.appEndpoint.getNumber(),
clusterId: Switch.Cluster.id,
eventName: 'longRelease',
convertValue: () => false,
});
}
} else {
this.enableDeviceTypeStateForAttribute(PropertyType.PowerActual, {
this.enableDeviceTypeStateForEvent(PropertyType.PressLong, {
endpointId: this.appEndpoint.getNumber(),
clusterId: Switch.Cluster.id,
attributeName: 'currentPosition',
convertValue: value => value !== 0,
eventName: 'longRelease',
convertValue: () => false,
});
}
return super.enableDeviceTypeStates();
}

protected enableLatchingSwitchDeviceTypeStates(): DeviceOptions {
this.enableDeviceTypeStateForAttribute(PropertyType.PowerActual, {
endpointId: this.appEndpoint.getNumber(),
clusterId: Switch.Cluster.id,
attributeName: 'currentPosition',
convertValue: value => value !== 0,
});

return super.enableDeviceTypeStates();
}
Expand Down
Loading