@iobroker/dm-gui-components 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ import React, { Component } from 'react';
2
+ import { Connection } from '@iobroker/adapter-react-v5';
3
+ import { ActionBase, ControlBase, ControlState } from '@iobroker/dm-utils/build/types/base';
4
+ export type CommunicationProps = {
5
+ socket: Connection;
6
+ selectedInstance: string;
7
+ };
8
+ interface CommunicationForm {
9
+ title?: string | null | undefined;
10
+ schema?: Record<string, any>;
11
+ data?: Record<string, any>;
12
+ handleClose?: (data?: Record<string, any>) => void;
13
+ }
14
+ export type CommunicationState = {
15
+ showSpinner: boolean;
16
+ showToast: string | null;
17
+ message: {
18
+ message: string;
19
+ handleClose: () => void;
20
+ } | null;
21
+ confirm: {
22
+ message: string;
23
+ handleClose: (confirmation?: boolean) => void;
24
+ } | null;
25
+ form: CommunicationForm | null;
26
+ progress: {
27
+ open: boolean;
28
+ progress: number;
29
+ } | null;
30
+ };
31
+ /**
32
+ * Device List Component
33
+ * @param {object} params - Component parameters
34
+ * @param {object} params.socket - socket object
35
+ * @param {string} params.selectedInstance - Selected instance
36
+ * @returns {*[]} - Array of device cards
37
+ */
38
+ declare abstract class Communication<P extends CommunicationProps, S extends CommunicationState> extends Component<P, S> {
39
+ instanceHandler: (action: ActionBase<'api'>) => () => void;
40
+ deviceHandler: (deviceId: string, action: ActionBase<'api'>, refresh: () => void) => () => void;
41
+ controlHandler: (deviceId: string, control: ControlBase, state: ControlState) => () => Promise<ioBroker.State | null>;
42
+ controlStateHandler: (deviceId: string, control: ControlBase) => () => Promise<ioBroker.State | null>;
43
+ constructor(props: P);
44
+ abstract loadData(): Promise<void>;
45
+ sendActionToInstance: (command: string, messageToSend: any, refresh?: () => void) => void;
46
+ sendControlToInstance: (command: string, messageToSend: {
47
+ deviceId: string;
48
+ controlId: string;
49
+ state?: ControlState;
50
+ }) => Promise<ioBroker.State>;
51
+ loadDevices(): Promise<any>;
52
+ loadInstanceInfos(): Promise<any>;
53
+ renderMessageDialog(): React.JSX.Element | null;
54
+ renderConfirmDialog(): React.JSX.Element;
55
+ renderSnackbar(): React.JSX.Element;
56
+ renderFormDialog(): React.JSX.Element;
57
+ renderProgressDialog(): React.JSX.Element;
58
+ renderContent(): React.JSX.Element | React.JSX.Element[] | null;
59
+ renderSpinner(): React.JSX.Element;
60
+ render(): React.JSX.Element;
61
+ }
62
+ export default Communication;
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const react_1 = __importStar(require("react"));
30
+ const material_1 = require("@mui/material");
31
+ const Utils_1 = require("./Utils");
32
+ const JsonConfig_1 = __importDefault(require("./JsonConfig"));
33
+ /**
34
+ * Device List Component
35
+ * @param {object} params - Component parameters
36
+ * @param {object} params.socket - socket object
37
+ * @param {string} params.selectedInstance - Selected instance
38
+ * @returns {*[]} - Array of device cards
39
+ */
40
+ class Communication extends react_1.Component {
41
+ constructor(props) {
42
+ super(props);
43
+ this.sendActionToInstance = (command, messageToSend, refresh) => {
44
+ const send = async () => {
45
+ this.setState({ showSpinner: true });
46
+ /** @type {object} */
47
+ const response = await this.props.socket.sendTo(this.props.selectedInstance, command, messageToSend);
48
+ /** @type {string} */
49
+ const type = response.type;
50
+ console.log(`Response: ${response.type}`);
51
+ switch (type) {
52
+ case 'message':
53
+ console.log(`Message received: ${response.message}`);
54
+ if (response.message) {
55
+ this.setState({
56
+ message: {
57
+ message: response.message,
58
+ handleClose: () => this.setState({ message: null }, () => this.sendActionToInstance('dm:actionProgress', { origin: response.origin })),
59
+ },
60
+ });
61
+ }
62
+ break;
63
+ case 'confirm':
64
+ console.log(`Confirm received: ${response.confirm}`);
65
+ if (response.confirm) {
66
+ this.setState({
67
+ confirm: {
68
+ message: response.confirm,
69
+ handleClose: (confirm) => this.setState({ confirm: null }, () => this.sendActionToInstance('dm:actionProgress', {
70
+ origin: response.origin,
71
+ confirm,
72
+ })),
73
+ },
74
+ });
75
+ }
76
+ break;
77
+ case 'form':
78
+ console.log('Form received');
79
+ if (response.form) {
80
+ this.setState({
81
+ form: Object.assign(Object.assign({}, response.form), { handleClose: (data) => this.setState({ form: null }, () => {
82
+ console.log(`Form ${JSON.stringify(data)}`);
83
+ this.sendActionToInstance('dm:actionProgress', {
84
+ origin: response.origin,
85
+ data,
86
+ });
87
+ }) }),
88
+ });
89
+ }
90
+ break;
91
+ case 'progress':
92
+ if (response.progress) {
93
+ if (this.state.progress) {
94
+ const progress = Object.assign(Object.assign({}, this.state.progress), response.progress);
95
+ this.setState({ progress });
96
+ }
97
+ else {
98
+ this.setState({ progress: response.progress });
99
+ }
100
+ }
101
+ this.sendActionToInstance('dm:actionProgress', { origin: response.origin });
102
+ break;
103
+ case 'result':
104
+ console.log('Response content', response.result);
105
+ if (response.result.refresh) {
106
+ if (response.result.refresh === true) {
107
+ this.loadData().catch(console.error);
108
+ console.log('Refreshing all');
109
+ }
110
+ else if (response.result.refresh === 'instance') {
111
+ console.log(`Refreshing instance infos: ${this.props.selectedInstance}`);
112
+ }
113
+ else if (response.result.refresh === 'device') {
114
+ if (refresh) {
115
+ console.log(`Refreshing device infos: ${this.props.selectedInstance}`);
116
+ refresh();
117
+ }
118
+ }
119
+ else {
120
+ console.log('Not refreshing anything');
121
+ }
122
+ }
123
+ if (response.result.error) {
124
+ console.error(`Error: ${response.result.error}`);
125
+ this.setState({ showToast: response.result.error.message });
126
+ }
127
+ this.setState({ showSpinner: false });
128
+ break;
129
+ default:
130
+ console.log(`Unknown response type: ${type}`);
131
+ this.setState({ showSpinner: false });
132
+ break;
133
+ }
134
+ };
135
+ send().catch(console.error);
136
+ };
137
+ this.sendControlToInstance = async (command, messageToSend) => {
138
+ const response = await this.props.socket.sendTo(this.props.selectedInstance, command, messageToSend);
139
+ const type = response.type;
140
+ console.log(`Response: ${response.type}`);
141
+ if (response.type === 'result') {
142
+ console.log('Response content', response.result);
143
+ if (response.result.error) {
144
+ console.error(`Error: ${response.result.error}`);
145
+ this.setState({ showToast: response.result.error.message });
146
+ }
147
+ else if (response.result.state !== undefined) {
148
+ return response.result.state;
149
+ }
150
+ }
151
+ else {
152
+ console.warn('Unexpected response type', type);
153
+ }
154
+ return null;
155
+ };
156
+ // @ts-expect-error
157
+ this.state = {
158
+ showSpinner: false,
159
+ showToast: null,
160
+ message: null,
161
+ confirm: null,
162
+ form: null,
163
+ progress: null,
164
+ };
165
+ this.instanceHandler = action => () => this.sendActionToInstance('dm:instanceAction', { actionId: action.id });
166
+ this.deviceHandler = (deviceId, action, refresh) => () => this.sendActionToInstance('dm:deviceAction', { deviceId, actionId: action.id }, refresh);
167
+ this.controlHandler = (deviceId, control, state) => () => this.sendControlToInstance('dm:deviceControl', { deviceId, controlId: control.id, state });
168
+ this.controlStateHandler = (deviceId, control) => () => this.sendControlToInstance('dm:deviceControlState', { deviceId, controlId: control.id });
169
+ }
170
+ loadDevices() {
171
+ return this.props.socket.sendTo(this.props.selectedInstance, 'dm:listDevices');
172
+ }
173
+ loadInstanceInfos() {
174
+ return this.props.socket.sendTo(this.props.selectedInstance, 'dm:instanceInfo');
175
+ }
176
+ renderMessageDialog() {
177
+ var _a;
178
+ if (!this.state.message) {
179
+ return null;
180
+ }
181
+ return react_1.default.createElement(material_1.Dialog, { open: !0, onClose: () => { var _a; return (_a = this.state.message) === null || _a === void 0 ? void 0 : _a.handleClose(); }, hideBackdrop: true, "aria-describedby": "message-dialog-description" },
182
+ react_1.default.createElement(material_1.DialogContent, null,
183
+ react_1.default.createElement(material_1.DialogContentText, { id: "message-dialog-description" }, (_a = this.state.message) === null || _a === void 0 ? void 0 : _a.message)),
184
+ react_1.default.createElement(material_1.DialogActions, null,
185
+ react_1.default.createElement(material_1.Button, { onClick: () => { var _a; return (_a = this.state.message) === null || _a === void 0 ? void 0 : _a.handleClose(); }, autoFocus: true }, (0, Utils_1.getTranslation)('okButtonText'))));
186
+ }
187
+ renderConfirmDialog() {
188
+ var _a;
189
+ if (!this.state.confirm) {
190
+ return null;
191
+ }
192
+ // @ts-ignore
193
+ return react_1.default.createElement(material_1.Dialog, { open: !0, onClose: () => { var _a; return (_a = this.state.confirm) === null || _a === void 0 ? void 0 : _a.handleClose(); }, hideBackdrop: true, "aria-describedby": "confirm-dialog-description" },
194
+ react_1.default.createElement(material_1.DialogContent, null,
195
+ react_1.default.createElement(material_1.DialogContentText, { id: "confirm-dialog-description" }, (0, Utils_1.getTranslation)((_a = this.state.confirm) === null || _a === void 0 ? void 0 : _a.message))),
196
+ react_1.default.createElement(material_1.DialogActions, null,
197
+ react_1.default.createElement(material_1.Button, { variant: "contained", color: "primary", onClick: () => { var _a; return (_a = this.state.confirm) === null || _a === void 0 ? void 0 : _a.handleClose(true); }, autoFocus: true }, (0, Utils_1.getTranslation)('yesButtonText')),
198
+ react_1.default.createElement(material_1.Button, { variant: "contained",
199
+ // @ts-expect-error
200
+ color: "grey", onClick: () => { var _a; return (_a = this.state.confirm) === null || _a === void 0 ? void 0 : _a.handleClose(false); }, autoFocus: true, hideBackdrop: true }, (0, Utils_1.getTranslation)('noButtonText'))));
201
+ }
202
+ renderSnackbar() {
203
+ return react_1.default.createElement(material_1.Snackbar, { open: !!this.state.showToast, autoHideDuration: 6000, onClose: () => this.setState({ showToast: null }), message: this.state.showToast });
204
+ }
205
+ renderFormDialog() {
206
+ var _a, _b;
207
+ if (!this.state.form || !this.state.form.schema || !this.state.form.data) {
208
+ return null;
209
+ }
210
+ return react_1.default.createElement(material_1.Dialog, { open: !0, onClose: () => { var _a; return ((_a = this.state.form) === null || _a === void 0 ? void 0 : _a.handleClose) && this.state.form.handleClose(); }, hideBackdrop: true },
211
+ ((_a = this.state.form) === null || _a === void 0 ? void 0 : _a.title) ? react_1.default.createElement(material_1.DialogTitle, null, (0, Utils_1.getTranslation)((_b = this.state.form) === null || _b === void 0 ? void 0 : _b.title)) : null,
212
+ react_1.default.createElement(material_1.DialogContent, null,
213
+ react_1.default.createElement(JsonConfig_1.default, { instanceId: this.props.selectedInstance, schema: this.state.form.schema, data: this.state.form.data, socket: this.props.socket, onChange: (data) => {
214
+ console.log('handleFormChange', { data });
215
+ const form = Object.assign({}, this.state.form);
216
+ if (form) {
217
+ form.data = data;
218
+ this.setState({ form });
219
+ }
220
+ } })),
221
+ react_1.default.createElement(material_1.DialogActions, null,
222
+ react_1.default.createElement(material_1.Button, { variant: "contained", color: "primary", onClick: () => { var _a, _b; return ((_a = this.state.form) === null || _a === void 0 ? void 0 : _a.handleClose) && this.state.form.handleClose((_b = this.state.form) === null || _b === void 0 ? void 0 : _b.data); }, autoFocus: true }, (0, Utils_1.getTranslation)('okButtonText')),
223
+ react_1.default.createElement(material_1.Button, { variant: "contained",
224
+ // @ts-expect-error
225
+ color: "grey", onClick: () => { var _a; return ((_a = this.state.form) === null || _a === void 0 ? void 0 : _a.handleClose) && this.state.form.handleClose(); }, hideBackdrop: true }, (0, Utils_1.getTranslation)('cancelButtonText'))));
226
+ }
227
+ renderProgressDialog() {
228
+ var _a, _b;
229
+ if (!((_a = this.state.progress) === null || _a === void 0 ? void 0 : _a.open)) {
230
+ return null;
231
+ }
232
+ return react_1.default.createElement(material_1.Dialog, { open: !0, onClose: () => { }, hideBackdrop: true },
233
+ react_1.default.createElement(material_1.LinearProgress, { variant: "determinate", value: ((_b = this.state.progress) === null || _b === void 0 ? void 0 : _b.progress) || 0 }));
234
+ }
235
+ renderContent() {
236
+ return null;
237
+ }
238
+ renderSpinner() {
239
+ var _a;
240
+ if (!this.state.showSpinner && !((_a = this.state.progress) === null || _a === void 0 ? void 0 : _a.open) && !this.state.message && !this.state.confirm && !this.state.form) {
241
+ return null;
242
+ }
243
+ return react_1.default.createElement(material_1.Backdrop, { style: { zIndex: 1000 }, open: !0 },
244
+ react_1.default.createElement(material_1.CircularProgress, null));
245
+ }
246
+ render() {
247
+ return react_1.default.createElement(react_1.default.Fragment, null,
248
+ this.renderSnackbar(),
249
+ this.renderContent(),
250
+ this.renderSpinner(),
251
+ this.renderConfirmDialog(),
252
+ this.renderMessageDialog(),
253
+ this.renderFormDialog(),
254
+ this.renderProgressDialog());
255
+ }
256
+ }
257
+ exports.default = Communication;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { ActionBase } from "@iobroker/dm-utils/build/types/base";
3
+ interface DeviceActionButtonProps {
4
+ deviceId: string;
5
+ action: any;
6
+ refresh: () => void;
7
+ deviceHandler: (deviceId: string, action: ActionBase<'api'>, refresh: () => void) => () => void;
8
+ }
9
+ export default function DeviceActionButton(props: DeviceActionButtonProps): React.JSX.Element;
10
+ export {};
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const TooltipButton_js_1 = __importDefault(require("./TooltipButton.js"));
8
+ const Utils_js_1 = require("./Utils.js");
9
+ function DeviceActionButton(props) {
10
+ const { deviceId, action, refresh, deviceHandler, } = props;
11
+ const tooltip = (0, Utils_js_1.getTranslation)(action.description);
12
+ const icon = (0, Utils_js_1.renderIcon)(action);
13
+ return react_1.default.createElement(TooltipButton_js_1.default, { label: action.label || (icon ? null : action.id), tooltip: tooltip, disabled: action.disabled, Icon: icon, onClick: deviceHandler(deviceId, action, refresh) });
14
+ }
15
+ exports.default = DeviceActionButton;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { Connection } from '@iobroker/adapter-react-v5';
3
+ import { DeviceInfo } from '@iobroker/dm-utils';
4
+ import { ActionBase, ControlBase, ControlState } from '@iobroker/dm-utils/build/types/base';
5
+ interface DeviceCardProps {
6
+ title?: string;
7
+ id: string;
8
+ device: DeviceInfo;
9
+ instanceId: string;
10
+ socket: Connection;
11
+ uploadImagesToInstance?: string;
12
+ deviceHandler: (deviceId: string, action: ActionBase<'api'>, refresh: () => void) => () => void;
13
+ controlHandler: (deviceId: string, control: ControlBase, state: ControlState) => () => Promise<ioBroker.State | null>;
14
+ controlStateHandler: (deviceId: string, control: ControlBase) => () => Promise<ioBroker.State | null>;
15
+ }
16
+ /**
17
+ * Device Card Component
18
+ */
19
+ export default function DeviceCard(params: DeviceCardProps): React.JSX.Element;
20
+ export {};
package/DeviceCard.js ADDED
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const react_1 = __importStar(require("react"));
30
+ const material_1 = require("@mui/material");
31
+ const icons_material_1 = require("@mui/icons-material");
32
+ const adapter_react_v5_1 = require("@iobroker/adapter-react-v5");
33
+ const DeviceActionButton_1 = __importDefault(require("./DeviceActionButton"));
34
+ const DeviceControl_1 = __importDefault(require("./DeviceControl"));
35
+ const DeviceStatus_1 = __importDefault(require("./DeviceStatus"));
36
+ const JsonConfig_1 = __importDefault(require("./JsonConfig"));
37
+ const DeviceImageUpload_1 = __importDefault(require("./DeviceImageUpload"));
38
+ const Utils_1 = require("./Utils");
39
+ const NoImageIcon = (props) => react_1.default.createElement("svg", { viewBox: "0 0 24 24", width: "24", height: "24", style: props.style, className: props.className },
40
+ react_1.default.createElement("path", { fill: "currentColor", d: "M21.9,21.9l-8.49-8.49l0,0L3.59,3.59l0,0L2.1,2.1L0.69,3.51L3,5.83V19c0,1.1,0.9,2,2,2h13.17l2.31,2.31L21.9,21.9z M5,18 l3.5-4.5l2.5,3.01L12.17,15l3,3H5z M21,18.17L5.83,3H19c1.1,0,2,0.9,2,2V18.17z" }));
41
+ function getText(text) {
42
+ if (typeof text === 'object') {
43
+ return text[adapter_react_v5_1.I18n.getLanguage()] || text.en;
44
+ }
45
+ return text;
46
+ }
47
+ /**
48
+ * Device Card Component
49
+ */
50
+ function DeviceCard(params) {
51
+ var _a, _b, _c, _d;
52
+ const { title, device, instanceId, socket, deviceHandler, uploadImagesToInstance, controlHandler, controlStateHandler, } = params;
53
+ const [open, setOpen] = (0, react_1.useState)(false);
54
+ const [details, setDetails] = (0, react_1.useState)(null);
55
+ const [data, setData] = (0, react_1.useState)({});
56
+ const [icon, setIcon] = (0, react_1.useState)(device.icon);
57
+ const [showControlDialog, setShowControlDialog] = (0, react_1.useState)(false);
58
+ // const [uploadedImage, setUploadedImage] = useState(null);
59
+ const hasDetails = device.hasDetails;
60
+ const status = !device.status ? [] : Array.isArray(device.status) ? device.status : [device.status];
61
+ const colors = { primary: '#111', secondary: '#888' };
62
+ (0, react_1.useEffect)(() => {
63
+ async function fetchIcon() {
64
+ if (!device.icon) {
65
+ // try to load the icon from file storage
66
+ const fileName = `${device.manufacturer ? `${device.manufacturer}_` : ''}${device.model ? device.model : device.id}`;
67
+ try {
68
+ const file = await socket.readFile(instanceId.replace('system.adapter.', ''), `${fileName}.webp`, true);
69
+ setIcon(`data:image/${file.mimeType},${file}`);
70
+ // const response = await fetch(url);
71
+ // if (response.ok) {
72
+ // const blob = await response.blob();
73
+ // const reader = new FileReader();
74
+ // reader.onloadend = () => {
75
+ // setIcon(reader.result);
76
+ // };
77
+ // reader.readAsDataURL(blob);
78
+ // } else {
79
+ // throw new Error('Response not ok');
80
+ // }
81
+ }
82
+ catch (error) {
83
+ setIcon('');
84
+ }
85
+ }
86
+ }
87
+ fetchIcon()
88
+ .catch(e => console.error(e));
89
+ }, [device, instanceId]);
90
+ /**
91
+ * Load the device details
92
+ */
93
+ const loadDetails = async () => {
94
+ console.log(`Loading device details for ${device.id}... from ${instanceId}`);
95
+ const result = await socket.sendTo(instanceId, 'dm:deviceDetails', device.id);
96
+ console.log(`Got device details for ${device.id}:`, result);
97
+ setDetails(result);
98
+ };
99
+ /**
100
+ * Refresh the device details
101
+ */
102
+ const refresh = () => {
103
+ setDetails(null);
104
+ loadDetails().catch(console.error);
105
+ };
106
+ /**
107
+ * Open the modal
108
+ */
109
+ const openModal = () => {
110
+ if (!open) {
111
+ loadDetails().catch(console.error);
112
+ setOpen(true);
113
+ }
114
+ };
115
+ /**
116
+ * Close the modal
117
+ */
118
+ const handleClose = () => setOpen(false);
119
+ const handleImageClick = async (imageData) => imageData && setIcon(imageData);
120
+ /**
121
+ * Copy the device ID to the clipboard
122
+ * @returns {void}
123
+ */
124
+ const copyToClipboard = async () => {
125
+ const textToCopy = device.id;
126
+ adapter_react_v5_1.Utils.copyToClipboard(textToCopy);
127
+ alert(`${(0, Utils_1.getTranslation)('copied')} ${textToCopy} ${(0, Utils_1.getTranslation)('toClipboard')}!`);
128
+ };
129
+ react_1.default.useEffect(() => setData((details === null || details === void 0 ? void 0 : details.data) || {}), [details]);
130
+ const renderedDialog = open && details ? react_1.default.createElement(material_1.Dialog, { open: !0, maxWidth: "md", onClose: handleClose },
131
+ react_1.default.createElement(material_1.DialogContent, null,
132
+ react_1.default.createElement(JsonConfig_1.default, { instanceId: instanceId, socket: socket, schema: details.schema, data: data, onChange: setData })),
133
+ react_1.default.createElement(material_1.DialogActions, null,
134
+ react_1.default.createElement(material_1.Button, { variant: "contained", color: "primary", onClick: handleClose, autoFocus: true }, (0, Utils_1.getTranslation)('closeButtonText')))) : null;
135
+ let renderedControls;
136
+ let controlDialog;
137
+ const firstControl = (_a = device.controls) === null || _a === void 0 ? void 0 : _a[0];
138
+ if (((_b = device.controls) === null || _b === void 0 ? void 0 : _b.length) === 1 && firstControl && ((firstControl.type === 'icon' || firstControl.type === 'switch') && !firstControl.label)) {
139
+ // control can be placed in button icon
140
+ renderedControls = react_1.default.createElement(DeviceControl_1.default, { control: firstControl, colors: colors, socket: socket, deviceId: device.id, controlHandler: controlHandler, controlStateHandler: controlStateHandler });
141
+ }
142
+ else if ((_c = device.controls) === null || _c === void 0 ? void 0 : _c.length) {
143
+ // place button and show controls dialog
144
+ renderedControls = react_1.default.createElement(material_1.Fab, { size: "small", onClick: () => setShowControlDialog(true) },
145
+ react_1.default.createElement(icons_material_1.VideogameAsset, null));
146
+ if (showControlDialog) {
147
+ controlDialog = react_1.default.createElement(material_1.Dialog, { open: !0, onClose: () => setShowControlDialog(false) },
148
+ react_1.default.createElement(material_1.DialogTitle, null,
149
+ title,
150
+ react_1.default.createElement(material_1.IconButton, { style: {
151
+ position: 'absolute',
152
+ top: 5,
153
+ right: 5,
154
+ zIndex: 10,
155
+ }, onClick: () => setShowControlDialog(false) },
156
+ react_1.default.createElement(icons_material_1.Close, null))),
157
+ react_1.default.createElement(material_1.DialogContent, null, device.controls.map(control => react_1.default.createElement(DeviceControl_1.default, { key: control.id, control: control, socket: socket, colors: colors, deviceId: device.id, controlHandler: controlHandler, controlStateHandler: controlStateHandler }))));
158
+ }
159
+ }
160
+ const renderedActions = ((_d = device.actions) === null || _d === void 0 ? void 0 : _d.length) ? device.actions.map(a => react_1.default.createElement(DeviceActionButton_1.default, { key: a.id, deviceId: device.id, action: a, deviceHandler: deviceHandler, refresh: refresh })) : null;
161
+ return react_1.default.createElement(material_1.Card, { sx: {
162
+ maxWidth: 345,
163
+ minWidth: 200,
164
+ } },
165
+ react_1.default.createElement(material_1.CardHeader, { sx: theme => ({
166
+ backgroundColor: device.color || theme.palette.secondary.main,
167
+ color: device.color ? adapter_react_v5_1.Utils.invertColor(device.color, true) : theme.palette.secondary.contrastText,
168
+ }), avatar: react_1.default.createElement("div", null,
169
+ uploadImagesToInstance ? react_1.default.createElement(DeviceImageUpload_1.default, { uploadImagesToInstance: uploadImagesToInstance, deviceId: device.id, manufacturer: getText(device.manufacturer), model: getText(device.model), onImageSelect: handleImageClick, socket: socket }) : null,
170
+ icon ? react_1.default.createElement(adapter_react_v5_1.Icon, { src: icon }) : react_1.default.createElement(NoImageIcon, null)), action: hasDetails ? react_1.default.createElement(material_1.IconButton, { "aria-label": "settings", onClick: openModal },
171
+ react_1.default.createElement(icons_material_1.MoreVert, null)) : null, title: title, subheader: device.manufacturer ? react_1.default.createElement("span", null,
172
+ react_1.default.createElement("b", { style: { marginRight: 4 } },
173
+ (0, Utils_1.getTranslation)('manufacturer'),
174
+ ":"),
175
+ getText(device.manufacturer)) : null }),
176
+ react_1.default.createElement(material_1.CardContent, { style: { position: 'relative' } },
177
+ (status === null || status === void 0 ? void 0 : status.length) ? react_1.default.createElement("div", { style: {
178
+ display: 'flex',
179
+ position: 'absolute',
180
+ top: -11,
181
+ background: '#88888880',
182
+ padding: '0 8px',
183
+ borderRadius: 5,
184
+ width: 'calc(100% - 46px)',
185
+ } }, status.map((s, i) => react_1.default.createElement(DeviceStatus_1.default, { key: i, status: s }))) : null,
186
+ react_1.default.createElement("div", null,
187
+ react_1.default.createElement(material_1.Typography, { variant: "body1" },
188
+ react_1.default.createElement("div", { onClick: copyToClipboard },
189
+ react_1.default.createElement("b", null, "ID:"),
190
+ react_1.default.createElement("span", { style: { marginLeft: 4 } }, device.id.replace(/.*\.\d\./, ''))),
191
+ device.manufacturer ? react_1.default.createElement("div", null,
192
+ react_1.default.createElement("b", { style: { marginRight: 4 } },
193
+ (0, Utils_1.getTranslation)('manufacturer'),
194
+ ":"),
195
+ getText(device.manufacturer)) : null,
196
+ device.model ? react_1.default.createElement("div", null,
197
+ react_1.default.createElement("b", { style: { marginRight: 4 } },
198
+ (0, Utils_1.getTranslation)('model'),
199
+ ":"),
200
+ getText(device.model)) : null))),
201
+ react_1.default.createElement(material_1.CardActions, { disableSpacing: true },
202
+ renderedActions,
203
+ react_1.default.createElement("div", { style: { flexGrow: 1 } }),
204
+ renderedControls),
205
+ renderedDialog,
206
+ controlDialog);
207
+ }
208
+ exports.default = DeviceCard;
@@ -0,0 +1,44 @@
1
+ import React, { Component } from 'react';
2
+ import { ControlBase, ControlState } from "@iobroker/dm-utils/build/types/base";
3
+ interface DeviceControlProps {
4
+ deviceId: string;
5
+ control: any;
6
+ socket: any;
7
+ controlHandler: (deviceId: string, control: ControlBase, state: ControlState) => () => Promise<ioBroker.State | null>;
8
+ controlStateHandler: (deviceId: string, control: ControlBase) => () => Promise<ioBroker.State | null>;
9
+ colors: any;
10
+ }
11
+ interface DeviceControlState {
12
+ value: any;
13
+ ts: number;
14
+ }
15
+ /**
16
+ * Device Control component
17
+ * @param {object} props - Parameters
18
+ * @param {object} props.control - Control object
19
+ * @param {object} props.socket - Socket object
20
+ * @param {object} props.controlHandler - Control handler to set the state
21
+ * @param {object} props.controlStateHandler - Control handler to read the state
22
+ * @returns {React.JSX.Element|null}
23
+ * @constructor
24
+ */
25
+ export default class DeviceControl extends Component<DeviceControlProps, DeviceControlState> {
26
+ constructor(props: DeviceControlProps);
27
+ componentDidMount(): void;
28
+ stateHandler: (id: string, state: ioBroker.State) => Promise<void>;
29
+ componentWillUnmount(): void;
30
+ static getDerivedStateFromProps(props: DeviceControlProps, state: DeviceControlState): {
31
+ value: any;
32
+ ts: any;
33
+ };
34
+ sendControl(deviceId: string, control: ControlBase, value: ControlState): Promise<void>;
35
+ renderButton(): React.JSX.Element;
36
+ renderSwitch(): React.JSX.Element;
37
+ getColor(): any;
38
+ renderSelect(): void;
39
+ renderSlider(): void;
40
+ renderColor(): void;
41
+ renderIcon(): React.JSX.Element;
42
+ render(): React.JSX.Element;
43
+ }
44
+ export {};