From c5b6330b421a6bed57f27cc31c9b6db7b34085be Mon Sep 17 00:00:00 2001 From: GermanBluefox Date: Sat, 11 Nov 2023 20:39:35 +0100 Subject: [PATCH] Working on controller --- io-package.json | 1 + package.json | 2 +- src-admin/package.json | 3 +- src-admin/src/App.js | 8 +- src-admin/src/Tabs/Controller.js | 260 ++++++++++++++++++- src-admin/src/Tabs/Options.js | 14 +- src-admin/src/components/ConfigHandler.js | 1 + src/ioBrokerStorageTypes.ts | 1 + src/main.ts | 55 ++++- src/matter/ControllerNode.ts | 288 ++++++++++------------ 10 files changed, 456 insertions(+), 177 deletions(-) diff --git a/io-package.json b/io-package.json index bdea7de4..11c7230c 100644 --- a/io-package.json +++ b/io-package.json @@ -123,6 +123,7 @@ ], "native": { "interface": "", + "debug": false, "login": "", "pass": "" }, diff --git a/package.json b/package.json index 4e0793a1..9e0ef91d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@iobroker/adapter-core": "^3.0.4", - "@project-chip/matter-node.js": "0.6.1-alpha.0-20231110-2ec0f70", + "@project-chip/matter-node.js": "0.6.1-alpha.0-20231111-0f49576", "@iobroker/type-detector": "^3.0.5", "axios": "^1.6.1", "jsonwebtoken": "^9.0.2" diff --git a/src-admin/package.json b/src-admin/package.json index bd6c3c0c..7a3db156 100644 --- a/src-admin/package.json +++ b/src-admin/package.json @@ -5,6 +5,7 @@ "dependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@iobroker/adapter-react-v5": "^4.7.3", + "@iobroker/type-detector": "^3.0.5", "@mui/icons-material": "^5.14.16", "@mui/material": "5.14.14", "@mui/styles": "5.14.14", @@ -19,7 +20,7 @@ "eslint-plugin-only-warn": "^1.1.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", - "@iobroker/type-detector": "^3.0.5", + "qr-scanner": "^1.4.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^4.11.0", diff --git a/src-admin/src/App.js b/src-admin/src/App.js index 74e3567a..bb58ab84 100644 --- a/src-admin/src/App.js +++ b/src-admin/src/App.js @@ -82,12 +82,14 @@ class App extends GenericApp { bridges: {}, devices: {}, }; + this.state.detectedDevices = {}; - this.state.detectedDevices = null; this.configHandler = null; this.intervalSubscribe = null; this.alert = window.alert; window.alert = text => this.showToast(text); + + this.controllerMessageHandler = null; } refreshBackendSubscription() { @@ -163,7 +165,7 @@ class App extends GenericApp { } else if (update?.command === 'stopped') { setTimeout(() => this.refreshBackendSubscription(), 5000); } else { - console.log(`Unknown update: ${JSON.stringify(update)}`); + this.controllerMessageHandler && this.controllerMessageHandler(update); } }; @@ -201,6 +203,8 @@ class App extends GenericApp { onChange={(id, value) => { this.updateNativeValue(id, value); }} + registerMessageHandler={handler => this.controllerMessageHandler = handler} + alive={this.state.alive} socket={this.socket} native={this.state.native} themeType={this.state.themeType} diff --git a/src-admin/src/Tabs/Controller.js b/src-admin/src/Tabs/Controller.js index 471ab623..79b03438 100644 --- a/src-admin/src/Tabs/Controller.js +++ b/src-admin/src/Tabs/Controller.js @@ -1,37 +1,256 @@ import React from 'react'; import PropTypes from 'prop-types'; import { withStyles } from '@mui/styles'; +import QrScanner from 'qr-scanner'; import { - Switch, + Button, CircularProgress, Dialog, + DialogActions, DialogContent, DialogTitle, IconButton, + Switch, Table, TableBody, + TableCell, TableHead, TableRow, TextField, + LinearProgress, Select, MenuItem, } from '@mui/material'; +import { + Add, Close, + LeakAdd, Search, +} from '@mui/icons-material'; import { I18n } from '@iobroker/adapter-react-v5'; const styles = () => ({ - address: { - fontSize: 'smaller', - opacity: 0.5, - marginLeft: 8, - }, panel: { width: '100%', display: 'flex', flexDirection: 'column', gap: 8, }, - input: { - width: '100%', - maxWidth: 300, + qrScanner: { + width: 400, + height: 250, }, }); class Controller extends React.Component { + constructor(props) { + super(props); + this.state = { + discovered: [], + discoveryRunning: false, + discoveryDone: false, + qrCode: null, + manualCode: '', + cameras: [], + camera: '', + hideVideo: false, + }; + + this.refQrScanner = React.createRef(); + this.qrScanner = null; + } + + componentDidMount() { + this.props.registerMessageHandler(this.onMessage); + } + + componentWillUnmount() { + this.props.registerMessageHandler(null); + this.destroyQrCode(); + } + + async initQrCode() { + if (!this.qrScanner && this.refQrScanner.current) { + this.qrScanner = true; + + this.qrScanner = new QrScanner( + this.refQrScanner.current, + result => { + if (result?.data && result.data !== this.state.qrCode) { + this.setState({ qrCode: result.data }); + } + }, + { + returnDetailedScanResult: true, + highlightCodeOutline: true, + maxScansPerSecond: 5, + // preferredCamera: camera, + }, + ); + + const cameras = await QrScanner.listCameras(true); + + const camera = window.localStorage.getItem('camera') || (cameras.length ? cameras[0].id : ''); + + await this.qrScanner.setCamera(camera); + + this.setState({ cameras, camera, hideVideo: !cameras.length }); + } + + if (this.qrScanner && this.qrScanner !== true) { + await this.qrScanner.start(); + } + } + + onMessage = message => { + if (message?.command === 'discoveredDevice') { + const discovered = JSON.parse(JSON.stringify(this.state.discovered)); + discovered.push(message.device); + this.setState({ discovered }); + } else { + console.log(`Unknown update: ${JSON.stringify(message)}`); + } + }; + + destroyQrCode() { + if (this.qrScanner && this.qrScanner !== true) { + this.qrScanner.destroy(); + } + this.qrScanner = null; + } + + renderQrCodeDialog() { + if (!this.state.showQrCodeDialog) { + return null; + } + return this.setState({ showQrCodeDialog: false }, () => this.destroyQrCode())} + > + {I18n.t('QR Code')} + + this.setState({ manualCode: e.target.value })} + /> + + {this.state.camera ?
: null} + {/* eslint-disable-next-line jsx-a11y/media-has-caption */} +
+ + + + +
; + } + + renderShowDiscoveredDevices() { + if (!this.state.discoveryRunning && !this.state.discoveryDone) { + return null; + } + return this.setState({ discoveryDone: false })} + > + {I18n.t('Discovered devices')} + + {this.state.discoveryRunning ? : null} + + + + + {I18n.t('Name')} + + + {I18n.t('Identifier')} + + + + + + {this.state.discovered.map(device => + + {device.DN} + + + {device.deviceIdentifier} + + + { + this.setState({ showQrCodeDialog: device, manualCode: '', qrCode: '' }); + setTimeout(async () => this.initQrCode(), 500); + }} + > + + + + )} + +
+
+ + + +
; + } + render() { return
+ {this.renderShowDiscoveredDevices()} + {this.renderQrCodeDialog()}
{I18n.t('Off')} { const matter = JSON.parse(JSON.stringify(this.props.matter)); @@ -41,6 +260,27 @@ class Controller extends React.Component { /> {I18n.t('On')}
+ {this.props.matter.controller.enabled && this.props.alive ?
+ +
: null}
; } } @@ -48,6 +288,8 @@ class Controller extends React.Component { Controller.propTypes = { matter: PropTypes.object, updateConfig: PropTypes.func, + alive: PropTypes.bool, + registerMessageHandler: PropTypes.func, }; export default withStyles(styles)(Controller); diff --git a/src-admin/src/Tabs/Options.js b/src-admin/src/Tabs/Options.js index d2fce85c..11021432 100644 --- a/src-admin/src/Tabs/Options.js +++ b/src-admin/src/Tabs/Options.js @@ -11,7 +11,7 @@ import { InputLabel, MenuItem, Select, - TextField, + TextField, FormControlLabel, Checkbox, } from '@mui/material'; import { I18n, Logo } from '@iobroker/adapter-react-v5'; @@ -239,6 +239,18 @@ class Options extends React.Component { {I18n.t('Sync credentials with %s', this.state.iotInstance.replace('system.adapter.', ''))} : null} +
+ this.props.onChange('debug', e.target.checked)} + color="primary" + /> + } + label={I18n.t('Show all debug logs')} + /> +