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

Changed color name #347

Merged
merged 10 commits into from
Jan 25, 2025
959 changes: 412 additions & 547 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@iobroker/adapter-core": "^3.2.3",
"@iobroker/i18n": "^0.3.1",
"@iobroker/dm-utils": "1.0.7",
"@iobroker/dm-utils": "^1.0.9",
"@iobroker/type-detector": "^4.1.1",
"@matter/main": "0.12.1",
"@matter/nodejs": "0.12.1",
Expand All @@ -41,7 +41,7 @@
"@alcalzone/release-script-plugin-iobroker": "^3.7.2",
"@alcalzone/release-script-plugin-license": "^3.7.0",
"@iobroker/build-tools": "^2.0.15",
"@iobroker/dev-server": "0.7.3",
"@iobroker/dev-server": "^0.7.6",
"@iobroker/eslint-config": "^1.0.0",
"@iobroker/legacy-testing": "^2.0.2",
"@iobroker/types": "^7.0.6",
Expand Down
28 changes: 14 additions & 14 deletions src-admin/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 src-admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
},
"dependencies": {
"@foxriver76/iob-component-lib": "^0.2.0",
"@iobroker/adapter-react-v5": "^7.4.17",
"@iobroker/dm-gui-components": "^7.4.17",
"@iobroker/adapter-react-v5": "^7.4.18",
"@iobroker/dm-gui-components": "^7.4.18",
"@iobroker/type-detector": "^4.1.1",
"@types/react-dom": "^18.3.5",
"@types/uuid": "^10.0.0",
Expand Down
134 changes: 130 additions & 4 deletions src-admin/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StyledEngineProvider, ThemeProvider } from '@mui/material/styles';
import React from 'react';

import { IconButton } from '@foxriver76/iob-component-lib';
import { IconButton as IconButton76 } from '@foxriver76/iob-component-lib';
import {
AppBar,
Dialog,
Expand All @@ -13,9 +13,11 @@ import {
Tab,
Tabs,
Tooltip,
Snackbar,
IconButton,
} from '@mui/material';

import { Help as IconHelp, SignalCellularOff as IconNotAlive } from '@mui/icons-material';
import { Help as IconHelp, SignalCellularOff as IconNotAlive, Close } from '@mui/icons-material';

import {
AdminConnection,
Expand Down Expand Up @@ -107,6 +109,7 @@ interface AppState extends GenericAppState {
} | null;
welcomeDialogShowed: boolean;
updatePassTrigger: number;
identifyUuids: { uuid: string; ts: number }[];
}

class App extends GenericApp<GenericAppProps, AppState> {
Expand All @@ -116,6 +119,8 @@ class App extends GenericApp<GenericAppProps, AppState> {

private refreshTimer: ReturnType<typeof setTimeout> | null = null;

private readonly identifyTimers: Record<string, ReturnType<typeof setTimeout>> = {};

private connectToBackEndInterval: ReturnType<typeof setInterval> | null = null;

private connectToBackEndCounter = 0;
Expand Down Expand Up @@ -175,7 +180,8 @@ class App extends GenericApp<GenericAppProps, AppState> {
welcomeDialogShowed: false,
inProcessing: null,
updatePassTrigger: 1,
});
identifyUuids: [],
} as Partial<AppState>);

this.alert = window.alert;
window.alert = text => this.showToast(text);
Expand Down Expand Up @@ -407,6 +413,39 @@ class App extends GenericApp<GenericAppProps, AppState> {
this.refreshTimer = null;
this.refreshBackendSubscription();
}, 5_000);
} else if (update.command === 'identifyPopup') {
// Some device in ioBroker should be identified
if (update.identifyUuid) {
if (this.identifyTimers[update.identifyUuid]) {
clearTimeout(this.identifyTimers[update.identifyUuid]);
}
const identifyUuids = [...this.state.identifyUuids];
if (!identifyUuids.find(it => it.uuid === update.identifyUuid)) {
identifyUuids.push({
uuid: update.identifyUuid,
ts: Date.now() + (update.identifySeconds || 15) * 1000,
});
}

this.setState({ identifyUuids });

this.identifyTimers[update.identifyUuid || ''] = setTimeout(
() => {
const now = Date.now();
const identifyUuids = [...this.state.identifyUuids];
// Delete all outdated identifies
for (let i = identifyUuids.length - 1; i >= 0; i--) {
if (identifyUuids[i].ts <= now) {
identifyUuids.splice(i, 1);
}
}
this.setState({ identifyUuids });
},
(update.identifySeconds || 15) * 1000,
);
} else {
console.warn('No identifyUuid');
}
} else {
this.controllerMessageHandler && this.controllerMessageHandler(update);
}
Expand Down Expand Up @@ -451,6 +490,8 @@ class App extends GenericApp<GenericAppProps, AppState> {
this.refreshTimer = null;
}

Object.values(this.identifyTimers).forEach(timer => clearTimeout(timer));

try {
this.socket.unsubscribeState(`system.adapter.matter.${this.instance}.alive`, this.onAlive);
await this.socket.unsubscribeFromInstance(`matter.${this.instance}`, 'gui', this.onBackendUpdates);
Expand Down Expand Up @@ -555,6 +596,7 @@ class App extends GenericApp<GenericAppProps, AppState> {
checkLicenseOnAdd={(type: 'addBridge' | 'addDevice' | 'addDeviceToBridge', matter: MatterConfig) =>
this.checkLicenseOnAdd(type, matter)
}
identifyUuids={this.state.identifyUuids}
/>
);
}
Expand Down Expand Up @@ -590,6 +632,7 @@ class App extends GenericApp<GenericAppProps, AppState> {
}}
showToast={(text: string) => this.showToast(text)}
checkLicenseOnAdd={(matter: MatterConfig) => this.checkLicenseOnAdd('addDevice', matter)}
identifyUuids={this.state.identifyUuids}
/>
);
}
Expand Down Expand Up @@ -684,6 +727,88 @@ class App extends GenericApp<GenericAppProps, AppState> {
);
}

renderIdentifyToast(): React.JSX.Element[] | null {
if (!this.state.identifyUuids.length) {
return null;
}

return this.state.identifyUuids.map(it => {
// Try to find information about this device
let name;
if (this.state.matter?.bridges?.length) {
for (const bridge of this.state.matter.bridges) {
if (bridge.uuid === it.uuid) {
name = bridge.name;
break;
} else if (bridge.list?.length) {
for (const device of bridge.list) {
if (device.uuid === it.uuid) {
name =
typeof device.name === 'object'
? device.name[I18n.getLanguage()] || device.name.en
: device.name;
break;
}
}
}
}
}

if (!name && this.state.matter?.devices?.length) {
for (const device of this.state.matter.devices) {
if (device.uuid === it.uuid) {
name =
typeof device.name === 'object'
? device.name[I18n.getLanguage()] || device.name.en
: device.name;
break;
}
}
}

name = name || it.uuid;

return (
<Snackbar
key={it.uuid}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={!0}
onClose={() => {
const identifyUuids = [...this.state.identifyUuids];
const i = identifyUuids.findIndex(id => id.uuid === it.uuid);
if (i !== -1) {
identifyUuids.splice(i, 1);
this.setState({ identifyUuids });
}
}}
message={<span>{I18n.t(`Identifying device %s`, name)}</span>}
action={[
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={this.props.classes?.close}
onClick={() => {
const identifyUuids = [...this.state.identifyUuids];
const i = identifyUuids.findIndex(id => id.uuid === it.uuid);
if (i !== -1) {
identifyUuids.splice(i, 1);
this.setState({ identifyUuids });
}
}}
size="large"
>
<Close />
</IconButton>,
]}
/>
);
});
}

render(): React.JSX.Element {
if (!this.state.ready) {
return (
Expand All @@ -699,6 +824,7 @@ class App extends GenericApp<GenericAppProps, AppState> {
<StyledEngineProvider injectFirst>
<ThemeProvider theme={this.state.theme}>
{this.renderToast()}
{this.renderIdentifyToast()}
{this.renderProgressDialog()}
{this.renderWelcomeDialog()}
<div
Expand Down Expand Up @@ -766,7 +892,7 @@ class App extends GenericApp<GenericAppProps, AppState> {
justifyContent: 'center',
}}
>
<IconButton
<IconButton76
iconColor="warning"
noBackground
icon="noConnection"
Expand Down
5 changes: 3 additions & 2 deletions src-admin/src/Tabs/Bridges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
Tooltip,
} from '@mui/material';

import { I18n, SelectID, type IobTheme, IconDeviceType } from '@iobroker/adapter-react-v5';
import { I18n, SelectID, type IobTheme, IconDeviceType, Utils } from '@iobroker/adapter-react-v5';

import InfoBox from '../components/InfoBox';
import DeviceDialog, { SUPPORTED_DEVICES } from '../components/DeviceDialog';
Expand Down Expand Up @@ -1231,6 +1231,7 @@ export class Bridges extends BridgesAndDevices<BridgesProps, BridgesState> {
<TableRow
key={devIndex}
style={{ opacity: device.enabled && bridge.enabled ? 1 : 0.4 }}
sx={this.getBlinkingSx(device.uuid)}
>
<TableCell style={{ border: 0, borderBottomLeftRadius: isLast ? 4 : 0 }} />
<TableCell>
Expand Down Expand Up @@ -1364,7 +1365,7 @@ export class Bridges extends BridgesAndDevices<BridgesProps, BridgesState> {
<React.Fragment key={bridgeIndex}>
<TableRow
style={{ opacity: bridge.enabled ? 1 : 0.4, position: 'relative' }}
sx={styles.bridgeButtonsAndTitle}
sx={Utils.getStyle(this.props.theme, styles.bridgeButtonsAndTitle, this.getBlinkingSx(bridge.uuid))}
>
<TableCell
style={{
Expand Down
35 changes: 34 additions & 1 deletion src-admin/src/Tabs/BridgesAndDevices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,26 @@ import type {
import { formatPairingCode, getTranslation } from '../Utils';
import type { ActionButton, BackEndCommandJsonFormOptions, JsonFormSchema } from '@iobroker/dm-utils';

export const STYLES: Record<string, React.CSSProperties> = {
export const STYLES: Record<string, any> = {
vendorIcon: {
width: 24,
height: 24,
},
tooltip: {
pointerEvents: 'none',
},
animation: 'blink .5s linear infinite',
'@keyframes spin': (theme: IobTheme): any => ({
'0%': {
backgroundColor: theme.palette.background.paper,
},
'50%': {
backgroundColor: theme.palette.mode === 'dark' ? '#958200' : '#ffe441',
},
'100%': {
backgroundColor: theme.palette.background.paper,
},
}),
} as const;

export interface BridgesAndDevicesProps {
Expand All @@ -88,6 +100,7 @@ export interface BridgesAndDevicesProps {
updateConfig: (config: MatterConfig) => void;
updateNodeStates: (states: { [uuid: string]: NodeStateResponse }) => void;
inProcessing: Processing;
identifyUuids: { uuid: string; ts: number }[];
}

export interface BridgesAndDevicesState {
Expand Down Expand Up @@ -514,6 +527,26 @@ class BridgesAndDevices<TProps extends BridgesAndDevicesProps, TState extends Br
);
}

// eslint-disable-next-line react/no-unused-class-component-methods
getBlinkingSx(uuid: string): Record<string, any> | undefined {
return this.props.identifyUuids.find(it => it.uuid === uuid)
? {
animation: 'bd-blink .5s linear infinite',
'@keyframes bd-blink': {
'0%': {
backgroundColor: this.props.theme.palette.background.paper,
},
'50%': {
backgroundColor: this.props.theme.palette.mode === 'dark' ? '#958200' : '#ffe441',
},
'100%': {
backgroundColor: this.props.theme.palette.background.paper,
},
},
}
: undefined;
}

getInProcessing(uuid: string): false | 'inQueue' | 'processing' {
if (!this.props.inProcessing) {
return false;
Expand Down
Loading
Loading