@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.
- package/Communication.d.ts +62 -0
- package/Communication.js +257 -0
- package/DeviceActionButton.d.ts +10 -0
- package/DeviceActionButton.js +15 -0
- package/DeviceCard.d.ts +20 -0
- package/DeviceCard.js +208 -0
- package/DeviceControl.d.ts +44 -0
- package/DeviceControl.js +146 -0
- package/DeviceImageUpload.d.ts +12 -0
- package/DeviceImageUpload.js +69 -0
- package/DeviceList.d.ts +50 -0
- package/DeviceList.js +205 -0
- package/DeviceStatus.d.ts +13 -0
- package/DeviceStatus.js +111 -0
- package/InstanceActionButton.d.ts +7 -0
- package/InstanceActionButton.js +16 -0
- package/JsonConfig.d.ts +11 -0
- package/JsonConfig.js +94 -0
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/TooltipButton.d.ts +10 -0
- package/TooltipButton.js +22 -0
- package/Utils.d.ts +12 -0
- package/Utils.js +157 -0
- package/i18n/de.json +21 -0
- package/i18n/en.json +21 -0
- package/i18n/es.json +21 -0
- package/i18n/fr.json +21 -0
- package/i18n/it.json +21 -0
- package/i18n/nl.json +21 -0
- package/i18n/pl.json +21 -0
- package/i18n/pt.json +21 -0
- package/i18n/ru.json +21 -0
- package/i18n/uk.json +21 -0
- package/i18n/zh-cn.json +21 -0
- package/index.d.ts +2 -0
- package/index.js +7 -0
- package/package.json +49 -0
package/DeviceControl.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const react_1 = __importStar(require("react"));
|
|
27
|
+
const material_1 = require("@mui/material");
|
|
28
|
+
const Utils_1 = require("./Utils");
|
|
29
|
+
/**
|
|
30
|
+
* Device Control component
|
|
31
|
+
* @param {object} props - Parameters
|
|
32
|
+
* @param {object} props.control - Control object
|
|
33
|
+
* @param {object} props.socket - Socket object
|
|
34
|
+
* @param {object} props.controlHandler - Control handler to set the state
|
|
35
|
+
* @param {object} props.controlStateHandler - Control handler to read the state
|
|
36
|
+
* @returns {React.JSX.Element|null}
|
|
37
|
+
* @constructor
|
|
38
|
+
*/
|
|
39
|
+
class DeviceControl extends react_1.Component {
|
|
40
|
+
constructor(props) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
super(props);
|
|
43
|
+
this.stateHandler = async (id, state) => {
|
|
44
|
+
if (id === this.props.control.stateId && state) {
|
|
45
|
+
// request new state
|
|
46
|
+
const newState = await (this.props.controlStateHandler(this.props.deviceId, this.props.control)());
|
|
47
|
+
if ((newState === null || newState === void 0 ? void 0 : newState.ts) && newState.ts > this.state.ts) {
|
|
48
|
+
this.setState({
|
|
49
|
+
value: newState.val,
|
|
50
|
+
ts: newState.ts,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
this.state = {
|
|
56
|
+
value: (_a = props.control.state) === null || _a === void 0 ? void 0 : _a.val,
|
|
57
|
+
ts: (_b = props.control.state) === null || _b === void 0 ? void 0 : _b.ts,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
componentDidMount() {
|
|
61
|
+
if (this.props.control.stateId) {
|
|
62
|
+
this.props.socket.subscribeState(this.props.control.stateId, this.stateHandler);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
componentWillUnmount() {
|
|
66
|
+
if (this.props.control.stateId) {
|
|
67
|
+
this.props.socket.unsubscribeState(this.props.control.stateId, this.stateHandler);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
static getDerivedStateFromProps(props, state) {
|
|
71
|
+
var _a;
|
|
72
|
+
if (((_a = props.control.state) === null || _a === void 0 ? void 0 : _a.ts) > state.ts) {
|
|
73
|
+
return {
|
|
74
|
+
value: props.control.state.val,
|
|
75
|
+
ts: props.control.state.ts,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
async sendControl(deviceId, control, value) {
|
|
81
|
+
const result = await (this.props.controlHandler(deviceId, control, value)());
|
|
82
|
+
if ((result === null || result === void 0 ? void 0 : result.ts) && (result === null || result === void 0 ? void 0 : result.ts) > this.state.ts) {
|
|
83
|
+
this.setState({
|
|
84
|
+
value: result.val,
|
|
85
|
+
ts: result.ts,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
renderButton() {
|
|
90
|
+
const tooltip = (0, Utils_1.getTranslation)(this.props.control.description);
|
|
91
|
+
const icon = (0, Utils_1.renderIcon)(this.props.control, this.props.colors, this.state.value);
|
|
92
|
+
if (!this.props.control.label) {
|
|
93
|
+
return react_1.default.createElement(material_1.Fab, { title: tooltip, onClick: () => this.sendControl(this.props.deviceId, this.props.control, true) }, icon);
|
|
94
|
+
}
|
|
95
|
+
return react_1.default.createElement(material_1.Button, { title: tooltip, onClick: () => this.sendControl(this.props.deviceId, this.props.control, true), startIcon: icon }, this.props.control.label);
|
|
96
|
+
}
|
|
97
|
+
renderSwitch() {
|
|
98
|
+
const tooltip = (0, Utils_1.getTranslation)(this.props.control.description);
|
|
99
|
+
// const icon = renderIcon(this.props.control, this.props.colors, this.state.value);
|
|
100
|
+
return react_1.default.createElement(material_1.Switch, { title: tooltip, checked: this.state.value, onChange: e => this.sendControl(this.props.deviceId, this.props.control, e.target.checked) });
|
|
101
|
+
}
|
|
102
|
+
getColor() {
|
|
103
|
+
let color;
|
|
104
|
+
if (this.state.value) {
|
|
105
|
+
color = this.props.control.colorOn || 'primary';
|
|
106
|
+
}
|
|
107
|
+
else if (this.props.control.type === 'switch') {
|
|
108
|
+
color = this.props.control.color;
|
|
109
|
+
}
|
|
110
|
+
if (color === 'primary') {
|
|
111
|
+
return this.props.colors.primary;
|
|
112
|
+
}
|
|
113
|
+
if (color === 'secondary') {
|
|
114
|
+
return this.props.colors.secondary;
|
|
115
|
+
}
|
|
116
|
+
return color;
|
|
117
|
+
}
|
|
118
|
+
renderSelect() {
|
|
119
|
+
}
|
|
120
|
+
renderSlider() {
|
|
121
|
+
}
|
|
122
|
+
renderColor() {
|
|
123
|
+
}
|
|
124
|
+
renderIcon() {
|
|
125
|
+
const tooltip = (0, Utils_1.getTranslation)(this.props.control.description);
|
|
126
|
+
const icon = (0, Utils_1.renderIcon)(this.props.control, this.props.colors, this.state.value);
|
|
127
|
+
const color = this.getColor();
|
|
128
|
+
if (!this.props.control.label) {
|
|
129
|
+
return react_1.default.createElement(material_1.Fab, { size: "small", title: tooltip, color: color === this.props.colors.primary ? 'primary' : (color === this.props.colors.secondary ? 'secondary' : undefined), style: color === this.props.colors.primary || color === this.props.colors.secondary ? undefined : { color }, onClick: () => this.sendControl(this.props.deviceId, this.props.control, !this.state.value) }, icon);
|
|
130
|
+
}
|
|
131
|
+
return react_1.default.createElement(material_1.Button, { title: tooltip, color: color === this.props.colors.primary ? 'primary' : (color === this.props.colors.secondary ? 'secondary' : undefined), style: color === this.props.colors.primary || color === this.props.colors.secondary ? undefined : { color }, onClick: () => this.sendControl(this.props.deviceId, this.props.control, !this.state.value), startIcon: icon }, this.props.control.label);
|
|
132
|
+
}
|
|
133
|
+
render() {
|
|
134
|
+
if (this.props.control.type === 'button') {
|
|
135
|
+
return this.renderButton();
|
|
136
|
+
}
|
|
137
|
+
if (this.props.control.type === 'icon') {
|
|
138
|
+
return this.renderIcon();
|
|
139
|
+
}
|
|
140
|
+
if (this.props.control.type === 'switch') {
|
|
141
|
+
return this.renderSwitch();
|
|
142
|
+
}
|
|
143
|
+
return react_1.default.createElement("div", { style: { color: 'red' } }, this.props.control.type);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.default = DeviceControl;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Connection } from '@iobroker/adapter-react-v5';
|
|
3
|
+
interface DeviceImageUploadProps {
|
|
4
|
+
socket: Connection;
|
|
5
|
+
manufacturer?: string;
|
|
6
|
+
model?: string;
|
|
7
|
+
deviceId: string;
|
|
8
|
+
onImageSelect: (image: string) => void;
|
|
9
|
+
uploadImagesToInstance: string;
|
|
10
|
+
}
|
|
11
|
+
declare function DeviceImageUpload(params: DeviceImageUploadProps): React.JSX.Element | null;
|
|
12
|
+
export default DeviceImageUpload;
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
function DeviceImageUpload(params) {
|
|
8
|
+
const { socket, manufacturer, model, deviceId, onImageSelect, uploadImagesToInstance, } = params;
|
|
9
|
+
const handleImageUpload = async (event) => {
|
|
10
|
+
const target = event.target;
|
|
11
|
+
const files = target.files;
|
|
12
|
+
if (!files || files.length === 0) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const file = files[0];
|
|
16
|
+
if (file) {
|
|
17
|
+
const reader = new FileReader();
|
|
18
|
+
reader.onload = async (e) => {
|
|
19
|
+
if (!e.target || !e.target.result) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const img = new Image();
|
|
23
|
+
img.src = e.target.result;
|
|
24
|
+
img.onload = async () => {
|
|
25
|
+
const maxWidth = 50;
|
|
26
|
+
const maxHeight = 50;
|
|
27
|
+
let width = img.width;
|
|
28
|
+
let height = img.height;
|
|
29
|
+
if (width > height) {
|
|
30
|
+
if (width > maxWidth) {
|
|
31
|
+
height *= maxWidth / width;
|
|
32
|
+
width = maxWidth;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (height > maxHeight) {
|
|
36
|
+
width *= maxHeight / height;
|
|
37
|
+
height = maxHeight;
|
|
38
|
+
}
|
|
39
|
+
const canvas = document.createElement('canvas');
|
|
40
|
+
const ctx = canvas.getContext('2d');
|
|
41
|
+
if (ctx) {
|
|
42
|
+
canvas.width = width;
|
|
43
|
+
canvas.height = height;
|
|
44
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
45
|
+
const resizedImage = canvas.toDataURL('image/webp');
|
|
46
|
+
// Build the file name from a manufacturer and model, if not available, use device id
|
|
47
|
+
const fileName = `${manufacturer ? `${manufacturer}_` : ''}${model || deviceId}`;
|
|
48
|
+
const base64Data = resizedImage.replace(/^data:image\/webp;base64,/, '');
|
|
49
|
+
const response = await socket.writeFile64(uploadImagesToInstance, fileName, base64Data);
|
|
50
|
+
console.log(`saveImage response: ${JSON.stringify(response)}`);
|
|
51
|
+
onImageSelect && onImageSelect(resizedImage);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
reader.readAsDataURL(file);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const imageUploadButtonStyle = {
|
|
59
|
+
// make the button invisible but still clickable
|
|
60
|
+
opacity: 0,
|
|
61
|
+
position: 'absolute',
|
|
62
|
+
width: '45px',
|
|
63
|
+
height: '45px',
|
|
64
|
+
zIndex: 3,
|
|
65
|
+
};
|
|
66
|
+
return react_1.default.createElement("div", null,
|
|
67
|
+
react_1.default.createElement("input", { style: imageUploadButtonStyle, type: "file", accept: "image/*", onChange: handleImageUpload }));
|
|
68
|
+
}
|
|
69
|
+
exports.default = DeviceImageUpload;
|
package/DeviceList.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DeviceInfo, InstanceDetails } from '@iobroker/dm-utils';
|
|
3
|
+
import Communication, { CommunicationProps, CommunicationState } from './Communication';
|
|
4
|
+
interface DeviceListProps extends CommunicationProps {
|
|
5
|
+
uploadImagesToInstance?: string;
|
|
6
|
+
filter?: string;
|
|
7
|
+
empbedded?: boolean;
|
|
8
|
+
title?: string;
|
|
9
|
+
style: React.CSSProperties;
|
|
10
|
+
}
|
|
11
|
+
interface DeviceListState extends CommunicationState {
|
|
12
|
+
devices: DeviceInfo[];
|
|
13
|
+
filteredDevices: DeviceInfo[];
|
|
14
|
+
filter: string;
|
|
15
|
+
instanceInfo: InstanceDetails;
|
|
16
|
+
loading: boolean;
|
|
17
|
+
alive: boolean | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Device List Component
|
|
21
|
+
* @param {object} params - Component parameters
|
|
22
|
+
* @param {object} params.socket - socket object
|
|
23
|
+
* @param {string} params.selectedInstance - Selected instance
|
|
24
|
+
* @param {string} params.uploadImagesToInstance - Instance to upload images to
|
|
25
|
+
* @param {string} params.filter - Filter
|
|
26
|
+
* @param {string} params.empbedded - true if this list used with multiple instances and false if only with one
|
|
27
|
+
* @param {string} params.title - Title in appbar (only in non-embedded mode)
|
|
28
|
+
* @param {string} params.style - Style of devices list
|
|
29
|
+
* @returns {*[]} - Array of device cards
|
|
30
|
+
*/
|
|
31
|
+
export default class DeviceList extends Communication<DeviceListProps, DeviceListState> {
|
|
32
|
+
static i18nInitialized: boolean;
|
|
33
|
+
private lastPropsFilter;
|
|
34
|
+
private lastInstance;
|
|
35
|
+
private filterTimeout;
|
|
36
|
+
private language;
|
|
37
|
+
constructor(props: DeviceListProps);
|
|
38
|
+
componentDidMount(): Promise<void>;
|
|
39
|
+
componentWillUnmount(): void;
|
|
40
|
+
aliveHandler: ioBroker.StateChangeHandler;
|
|
41
|
+
/**
|
|
42
|
+
* Load devices
|
|
43
|
+
*/
|
|
44
|
+
loadData(): Promise<void>;
|
|
45
|
+
getText(text: ioBroker.StringOrTranslated): string;
|
|
46
|
+
applyFilter(): void;
|
|
47
|
+
handleFilterChange(filter: string): void;
|
|
48
|
+
renderContent(): React.JSX.Element | React.JSX.Element[] | null;
|
|
49
|
+
}
|
|
50
|
+
export {};
|
package/DeviceList.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
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 material_1 = require("@mui/material");
|
|
8
|
+
const icons_material_1 = require("@mui/icons-material");
|
|
9
|
+
const adapter_react_v5_1 = require("@iobroker/adapter-react-v5");
|
|
10
|
+
const DeviceCard_1 = __importDefault(require("./DeviceCard"));
|
|
11
|
+
const Utils_1 = require("./Utils");
|
|
12
|
+
const Communication_1 = __importDefault(require("./Communication"));
|
|
13
|
+
const InstanceActionButton_1 = __importDefault(require("./InstanceActionButton"));
|
|
14
|
+
const de_json_1 = __importDefault(require("./i18n/de.json"));
|
|
15
|
+
const en_json_1 = __importDefault(require("./i18n/en.json"));
|
|
16
|
+
const ru_json_1 = __importDefault(require("./i18n/ru.json"));
|
|
17
|
+
const pt_json_1 = __importDefault(require("./i18n/pt.json"));
|
|
18
|
+
const nl_json_1 = __importDefault(require("./i18n/nl.json"));
|
|
19
|
+
const fr_json_1 = __importDefault(require("./i18n/fr.json"));
|
|
20
|
+
const it_json_1 = __importDefault(require("./i18n/it.json"));
|
|
21
|
+
const es_json_1 = __importDefault(require("./i18n/es.json"));
|
|
22
|
+
const pl_json_1 = __importDefault(require("./i18n/pl.json"));
|
|
23
|
+
const uk_json_1 = __importDefault(require("./i18n/uk.json"));
|
|
24
|
+
const zh_cn_json_1 = __importDefault(require("./i18n/zh-cn.json"));
|
|
25
|
+
/**
|
|
26
|
+
* Device List Component
|
|
27
|
+
* @param {object} params - Component parameters
|
|
28
|
+
* @param {object} params.socket - socket object
|
|
29
|
+
* @param {string} params.selectedInstance - Selected instance
|
|
30
|
+
* @param {string} params.uploadImagesToInstance - Instance to upload images to
|
|
31
|
+
* @param {string} params.filter - Filter
|
|
32
|
+
* @param {string} params.empbedded - true if this list used with multiple instances and false if only with one
|
|
33
|
+
* @param {string} params.title - Title in appbar (only in non-embedded mode)
|
|
34
|
+
* @param {string} params.style - Style of devices list
|
|
35
|
+
* @returns {*[]} - Array of device cards
|
|
36
|
+
*/
|
|
37
|
+
class DeviceList extends Communication_1.default {
|
|
38
|
+
constructor(props) {
|
|
39
|
+
super(props);
|
|
40
|
+
this.aliveHandler = (id, state) => {
|
|
41
|
+
if (id === `system.adapter.${this.props.selectedInstance}.alive`) {
|
|
42
|
+
const alive = !!(state === null || state === void 0 ? void 0 : state.val);
|
|
43
|
+
if (alive !== this.state.alive) {
|
|
44
|
+
this.setState({ alive }, () => {
|
|
45
|
+
if (alive) {
|
|
46
|
+
this.componentDidMount().catch(console.error);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
if (!DeviceList.i18nInitialized) {
|
|
53
|
+
DeviceList.i18nInitialized = true;
|
|
54
|
+
// @ts-expect-error
|
|
55
|
+
adapter_react_v5_1.I18n.extendTranslations({
|
|
56
|
+
en: en_json_1.default,
|
|
57
|
+
de: de_json_1.default,
|
|
58
|
+
ru: ru_json_1.default,
|
|
59
|
+
pt: pt_json_1.default,
|
|
60
|
+
nl: nl_json_1.default,
|
|
61
|
+
fr: fr_json_1.default,
|
|
62
|
+
it: it_json_1.default,
|
|
63
|
+
es: es_json_1.default,
|
|
64
|
+
pl: pl_json_1.default,
|
|
65
|
+
uk: uk_json_1.default,
|
|
66
|
+
'zh-cn': zh_cn_json_1.default,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
Object.assing(this.state, {
|
|
71
|
+
devices: [],
|
|
72
|
+
filteredDevices: [],
|
|
73
|
+
filter: '',
|
|
74
|
+
instanceInfo: null,
|
|
75
|
+
loading: null,
|
|
76
|
+
alive: null,
|
|
77
|
+
});
|
|
78
|
+
this.lastPropsFilter = this.props.filter;
|
|
79
|
+
this.lastInstance = '';
|
|
80
|
+
this.filterTimeout = null;
|
|
81
|
+
this.language = adapter_react_v5_1.I18n.getLanguage();
|
|
82
|
+
}
|
|
83
|
+
async componentDidMount() {
|
|
84
|
+
if (!this.props.empbedded) {
|
|
85
|
+
try {
|
|
86
|
+
// check if instance is alive
|
|
87
|
+
const alive = await this.props.socket.getState(`system.adapter.${this.props.selectedInstance}.alive`);
|
|
88
|
+
this.props.socket.unsubscribeState(`system.adapter.${this.props.selectedInstance}.alive`, this.aliveHandler);
|
|
89
|
+
if (!alive || !alive.val) {
|
|
90
|
+
this.setState({ alive: false });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const instanceInfo = await this.loadInstanceInfos();
|
|
94
|
+
this.setState({ alive: true, instanceInfo });
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error(error);
|
|
98
|
+
this.setState({ alive: false });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await this.loadData();
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error(error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
componentWillUnmount() {
|
|
109
|
+
if (!this.props.empbedded) {
|
|
110
|
+
this.props.socket.unsubscribeState(`system.adapter.${this.props.selectedInstance}.alive`, this.aliveHandler);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Load devices
|
|
115
|
+
*/
|
|
116
|
+
async loadData() {
|
|
117
|
+
this.setState({ loading: true });
|
|
118
|
+
console.log(`Loading devices for ${this.props.selectedInstance}...`);
|
|
119
|
+
const devices = await this.loadDevices();
|
|
120
|
+
if (!devices || !Array.isArray(devices)) {
|
|
121
|
+
throw new Error(`Message returned from sendTo() doesn't look like one from DeviceManagement, did you accidentally handle the message in your adapter? ${JSON.stringify(devices)}`);
|
|
122
|
+
}
|
|
123
|
+
this.setState({ devices, loading: false }, () => this.applyFilter());
|
|
124
|
+
}
|
|
125
|
+
getText(text) {
|
|
126
|
+
if (typeof text === 'object') {
|
|
127
|
+
return text[this.language] || text.en;
|
|
128
|
+
}
|
|
129
|
+
return text;
|
|
130
|
+
}
|
|
131
|
+
applyFilter() {
|
|
132
|
+
const filter = this.props.empbedded ? this.props.filter : this.state.filter;
|
|
133
|
+
// filter devices name
|
|
134
|
+
if (filter) {
|
|
135
|
+
const filteredDevices = this.state.devices.filter(device => this.getText(device.name).toLowerCase().includes(filter.toLowerCase()));
|
|
136
|
+
this.setState({ filteredDevices });
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this.setState({ filteredDevices: this.state.devices });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
handleFilterChange(filter) {
|
|
143
|
+
this.setState({ filter }, () => {
|
|
144
|
+
this.filterTimeout && clearTimeout(this.filterTimeout);
|
|
145
|
+
this.filterTimeout = setTimeout(() => {
|
|
146
|
+
this.filterTimeout = null;
|
|
147
|
+
this.applyFilter();
|
|
148
|
+
}, 250);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
renderContent() {
|
|
152
|
+
var _a, _b;
|
|
153
|
+
/** @type {object} */
|
|
154
|
+
const emptyStyle = {
|
|
155
|
+
padding: 25,
|
|
156
|
+
};
|
|
157
|
+
if (this.props.empbedded && this.lastPropsFilter !== this.props.filter) {
|
|
158
|
+
this.lastPropsFilter = this.props.filter;
|
|
159
|
+
setTimeout(() => this.applyFilter(), 50);
|
|
160
|
+
}
|
|
161
|
+
if (this.props.empbedded && this.lastInstance !== this.props.selectedInstance) {
|
|
162
|
+
this.lastInstance = this.props.selectedInstance;
|
|
163
|
+
setTimeout(() => this.loadData().catch(console.error), 50);
|
|
164
|
+
}
|
|
165
|
+
let list;
|
|
166
|
+
if (!this.props.empbedded && !this.state.alive) {
|
|
167
|
+
list = react_1.default.createElement("div", { style: emptyStyle },
|
|
168
|
+
react_1.default.createElement("span", null, (0, Utils_1.getTranslation)('instanceNotAlive')));
|
|
169
|
+
}
|
|
170
|
+
else if (!this.state.devices.length && this.props.selectedInstance) {
|
|
171
|
+
list = react_1.default.createElement("div", { style: emptyStyle },
|
|
172
|
+
react_1.default.createElement("span", null, (0, Utils_1.getTranslation)('noDevicesFoundText')));
|
|
173
|
+
}
|
|
174
|
+
else if (this.state.devices.length && !this.state.filteredDevices.length) {
|
|
175
|
+
list = react_1.default.createElement("div", { style: emptyStyle },
|
|
176
|
+
react_1.default.createElement("span", null, (0, Utils_1.getTranslation)('allDevicesFilteredOut')));
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
list = this.state.filteredDevices.map(device => react_1.default.createElement(DeviceCard_1.default, { key: device.id, id: device.id, title: this.getText(device.name), device: device, instanceId: this.props.selectedInstance, uploadImagesToInstance: this.props.uploadImagesToInstance, deviceHandler: this.deviceHandler, controlHandler: this.controlHandler, controlStateHandler: this.controlStateHandler, socket: this.props.socket }));
|
|
180
|
+
}
|
|
181
|
+
if (this.props.empbedded) {
|
|
182
|
+
return list;
|
|
183
|
+
}
|
|
184
|
+
return react_1.default.createElement("div", { style: { width: '100%', height: '100%', overflow: 'hidden' } },
|
|
185
|
+
react_1.default.createElement(material_1.Toolbar, { variant: "dense", style: { backgroundColor: '#777', display: 'flex' } },
|
|
186
|
+
this.props.title,
|
|
187
|
+
this.state.alive && ((_b = (_a = this.state.instanceInfo) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.length) ? react_1.default.createElement("div", { style: {
|
|
188
|
+
marginLeft: 20,
|
|
189
|
+
} }, this.state.instanceInfo.actions.map(action => react_1.default.createElement(InstanceActionButton_1.default, { key: action.id, action: action, instanceHandler: this.instanceHandler }))) : null,
|
|
190
|
+
react_1.default.createElement("div", { style: { flexGrow: 1 } }),
|
|
191
|
+
this.state.alive ? react_1.default.createElement(material_1.TextField, { variant: "standard", style: { width: 200 }, size: "small", label: (0, Utils_1.getTranslation)('filterLabelText'), onChange: e => this.handleFilterChange(e.target.value), value: this.state.filter, autoComplete: "off", inputProps: {
|
|
192
|
+
autoComplete: 'new-password',
|
|
193
|
+
form: { autoComplete: 'off' },
|
|
194
|
+
},
|
|
195
|
+
// eslint-disable-next-line react/jsx-no-duplicate-props
|
|
196
|
+
InputProps: {
|
|
197
|
+
endAdornment: this.state.filter ? react_1.default.createElement(material_1.InputAdornment, { position: "end" },
|
|
198
|
+
react_1.default.createElement(material_1.IconButton, { onClick: () => this.handleFilterChange(''), edge: "end" },
|
|
199
|
+
react_1.default.createElement(icons_material_1.Clear, null))) : null,
|
|
200
|
+
} }) : null),
|
|
201
|
+
react_1.default.createElement("div", { style: Object.assign({ width: '100%', height: 'calc(100% - 56px)', marginTop: 8, overflow: 'auto', justifyContent: 'center', alignItems: 'stretch', display: 'grid', columnGap: 8, rowGap: 8 }, this.props.style) }, list));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
DeviceList.i18nInitialized = false;
|
|
205
|
+
exports.default = DeviceList;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface DeviceStatusProps {
|
|
3
|
+
status: any;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Device Status component
|
|
7
|
+
* @param {object} params - Parameters
|
|
8
|
+
* @param {object} params.status - Status object, e.g. { connection: 'connected', battery: 100, rssi: -50 }
|
|
9
|
+
* @returns {React.JSX.Element|null}
|
|
10
|
+
* @constructor
|
|
11
|
+
*/
|
|
12
|
+
export default function DeviceStatus(params: DeviceStatusProps): React.JSX.Element | null;
|
|
13
|
+
export {};
|
package/DeviceStatus.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
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 material_1 = require("@mui/material");
|
|
8
|
+
const icons_material_1 = require("@mui/icons-material");
|
|
9
|
+
const Utils_1 = require("./Utils");
|
|
10
|
+
/**
|
|
11
|
+
* Device Status component
|
|
12
|
+
* @param {object} params - Parameters
|
|
13
|
+
* @param {object} params.status - Status object, e.g. { connection: 'connected', battery: 100, rssi: -50 }
|
|
14
|
+
* @returns {React.JSX.Element|null}
|
|
15
|
+
* @constructor
|
|
16
|
+
*/
|
|
17
|
+
function DeviceStatus(params) {
|
|
18
|
+
if (!params.status) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
let status;
|
|
22
|
+
if (typeof params.status === 'string') {
|
|
23
|
+
status = {
|
|
24
|
+
connection: params.status,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
status = params.status;
|
|
29
|
+
}
|
|
30
|
+
/** @type {object} */
|
|
31
|
+
const iconStyleOK = {
|
|
32
|
+
fill: '#00ac00',
|
|
33
|
+
};
|
|
34
|
+
/** @type {object} */
|
|
35
|
+
const iconStyleNotOK = {
|
|
36
|
+
fill: '#ff0000',
|
|
37
|
+
};
|
|
38
|
+
/** @type {object} */
|
|
39
|
+
const iconStyleWarning = {
|
|
40
|
+
fill: '#ff9900',
|
|
41
|
+
};
|
|
42
|
+
/** @type {object} */
|
|
43
|
+
let batteryIconTooltip;
|
|
44
|
+
if (typeof status.battery === 'number') {
|
|
45
|
+
if (status.battery >= 96 && status.battery <= 100) {
|
|
46
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.BatteryFull, { style: iconStyleOK });
|
|
47
|
+
}
|
|
48
|
+
else if (status.battery >= 90 && status.battery <= 95) {
|
|
49
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.Battery90, { style: iconStyleOK });
|
|
50
|
+
}
|
|
51
|
+
else if (status.battery >= 80 && status.battery <= 89) {
|
|
52
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.Battery80, { style: iconStyleOK });
|
|
53
|
+
}
|
|
54
|
+
else if (status.battery >= 60 && status.battery <= 79) {
|
|
55
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.Battery60, { style: iconStyleOK });
|
|
56
|
+
}
|
|
57
|
+
else if (status.battery >= 50 && status.battery <= 59) {
|
|
58
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.Battery50, { style: iconStyleOK });
|
|
59
|
+
}
|
|
60
|
+
else if (status.battery >= 30 && status.battery <= 49) {
|
|
61
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.Battery30, { style: iconStyleOK });
|
|
62
|
+
}
|
|
63
|
+
else if (status.battery >= 20 && status.battery <= 29) {
|
|
64
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.Battery20, { style: iconStyleNotOK });
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
batteryIconTooltip = react_1.default.createElement(icons_material_1.BatteryAlert, { style: iconStyleNotOK });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
71
|
+
status.connection === 'connected' && react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
72
|
+
react_1.default.createElement(material_1.Tooltip, { title: (0, Utils_1.getTranslation)('connectedIconTooltip') },
|
|
73
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } },
|
|
74
|
+
react_1.default.createElement(icons_material_1.Link, { style: iconStyleOK })))),
|
|
75
|
+
status.connection === 'disconnected' && react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
76
|
+
react_1.default.createElement(material_1.Tooltip, { title: (0, Utils_1.getTranslation)('disconnectedIconTooltip') },
|
|
77
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } },
|
|
78
|
+
react_1.default.createElement(icons_material_1.LinkOff, { style: iconStyleNotOK })))),
|
|
79
|
+
status.rssi && react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
80
|
+
react_1.default.createElement(material_1.Tooltip, { title: "RSSI" },
|
|
81
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } },
|
|
82
|
+
react_1.default.createElement(icons_material_1.NetworkCheck, null),
|
|
83
|
+
react_1.default.createElement("p", { style: { fontSize: 'small', margin: 0 } }, status.rssi)))),
|
|
84
|
+
typeof status.battery === 'number' && react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
85
|
+
react_1.default.createElement(material_1.Tooltip, { title: (0, Utils_1.getTranslation)('batteryTooltip') },
|
|
86
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } },
|
|
87
|
+
batteryIconTooltip,
|
|
88
|
+
react_1.default.createElement("p", { style: { fontSize: 'small', margin: 0 } },
|
|
89
|
+
status.battery,
|
|
90
|
+
"%")))),
|
|
91
|
+
typeof status.battery === 'string' && react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
92
|
+
react_1.default.createElement(material_1.Tooltip, { title: (0, Utils_1.getTranslation)('batteryTooltip') },
|
|
93
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } },
|
|
94
|
+
status.battery === 'charging' ? react_1.default.createElement(icons_material_1.BatteryCharging50, null) : react_1.default.createElement(icons_material_1.BatteryFull, null),
|
|
95
|
+
status.battery !== 'charging' ? (status.battery.includes('V') || status.battery.includes('mV') ?
|
|
96
|
+
react_1.default.createElement("p", { style: { fontSize: 'small', margin: 0 } }, status.battery) :
|
|
97
|
+
react_1.default.createElement("p", { style: { fontSize: 'small', margin: 0 } },
|
|
98
|
+
react_1.default.createElement("span", { style: { marginRight: 4 } }, status.battery),
|
|
99
|
+
"mV")) : null))),
|
|
100
|
+
typeof status.battery === 'boolean' && react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
101
|
+
react_1.default.createElement(material_1.Tooltip, { title: (0, Utils_1.getTranslation)('batteryTooltip') },
|
|
102
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } }, status.battery ? react_1.default.createElement(icons_material_1.BatteryFull, { style: iconStyleOK }) :
|
|
103
|
+
react_1.default.createElement(icons_material_1.BatteryAlert, { style: iconStyleNotOK })))),
|
|
104
|
+
status.warning && react_1.default.createElement("div", { style: { display: 'flex', alignItems: 'center' } }, typeof status.warning === 'string' || typeof status.warning === 'object' ?
|
|
105
|
+
react_1.default.createElement(material_1.Tooltip, { title: (0, Utils_1.getTranslation)(status.warning) },
|
|
106
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } },
|
|
107
|
+
react_1.default.createElement(icons_material_1.Warning, { style: iconStyleWarning }))) :
|
|
108
|
+
react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } },
|
|
109
|
+
react_1.default.createElement(icons_material_1.Warning, { style: iconStyleWarning }))));
|
|
110
|
+
}
|
|
111
|
+
exports.default = DeviceStatus;
|
|
@@ -0,0 +1,16 @@
|
|
|
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_1 = __importDefault(require("./TooltipButton"));
|
|
8
|
+
const Utils_1 = require("./Utils");
|
|
9
|
+
function InstanceActionButton(params) {
|
|
10
|
+
const { action, instanceHandler } = params;
|
|
11
|
+
const tooltip = (0, Utils_1.getTranslation)((action === null || action === void 0 ? void 0 : action.description) ? action.description : '');
|
|
12
|
+
const title = (0, Utils_1.getTranslation)((action === null || action === void 0 ? void 0 : action.title) ? action.title : '');
|
|
13
|
+
const icon = (0, Utils_1.renderIcon)(action);
|
|
14
|
+
return react_1.default.createElement(TooltipButton_1.default, { tooltip: tooltip, label: title, disabled: action.disabled, Icon: icon, onClick: instanceHandler(action) });
|
|
15
|
+
}
|
|
16
|
+
exports.default = InstanceActionButton;
|
package/JsonConfig.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Connection } from '@iobroker/adapter-react-v5';
|
|
3
|
+
interface JsonConfigProps {
|
|
4
|
+
instanceId: string;
|
|
5
|
+
socket: Connection;
|
|
6
|
+
schema: Record<string, any>;
|
|
7
|
+
data: Record<string, any>;
|
|
8
|
+
onChange: (data: Record<string, any>) => void;
|
|
9
|
+
}
|
|
10
|
+
export default function JsonConfig(props: JsonConfigProps): React.JSX.Element | null;
|
|
11
|
+
export {};
|