Skip to content

Commit

Permalink
Custom object editor (#2829)
Browse files Browse the repository at this point in the history
  • Loading branch information
GermanBluefox authored Nov 25, 2024
1 parent 884c078 commit dddca8b
Show file tree
Hide file tree
Showing 15 changed files with 781 additions and 152 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ packages/admin/build-backend
packages/admin/README.md
/packages/jsonConfig/test/testSchema.js
/packages/jsonConfig/test/testSchema.js.map
/packages/adapter-react-v5/i18n/
4 changes: 2 additions & 2 deletions packages/adapter-react-v5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ If you want to create the configuration page with ReactJS:
- Change `name` from `src` to `ADAPTERNAME-admin` (Of course replace `ADAPTERNAME` with yours)
- Add to devDependencies:
```json
"@iobroker/adapter-react-v5": "^7.4.0",
"@iobroker/adapter-react-v5": "^7.4.1",
```
Versions can be higher.
So your `src/package.json` should look like:
Expand All @@ -24,7 +24,7 @@ If you want to create the configuration page with ReactJS:
"version": "0.1.0",
"private": true,
"dependencies": {
"@iobroker/adapter-react-v5": "^7.4.0",
"@iobroker/adapter-react-v5": "^7.4.1",
"@iobroker/build-tools": "^1.0.0",
"babel-eslint": "^10.1.0",
"react-scripts": "^5.0.1"
Expand Down
1 change: 1 addition & 0 deletions packages/adapter-react-v5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
],
"files": [
"build/",
"i18n/",
"LICENSE",
"tasksExample.js",
"craco-module-federation.js",
Expand Down
179 changes: 179 additions & 0 deletions packages/adapter-react-v5/src/Components/IobUri.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import type { Connection } from '@/index';

export type IobUri = string;
export type IobUriType = 'object' | 'state' | 'file' | 'http' | 'base64';

export type IobUriParsed = { type: IobUriType; address: string; path?: string };

export function iobUriToString(uri: IobUriParsed): IobUri {
if (uri.type === 'object') {
return `iobobject://${uri.address}/${uri.path || ''}`;
}
if (uri.type === 'state') {
return `iobstate://${uri.address}`;
}
if (uri.type === 'file') {
return `iobfile://${uri.address}/${uri.path || ''}`;
}
if (uri.type === 'http') {
return uri.address;
}
if (uri.path?.includes('/')) {
return `iobfile://${uri.address}/${uri.path}`;
}
if (uri.path) {
return `iobobject://${uri.address}/${uri.path}`;
}
return `iobstate://${uri.address}`;
}

/** Parse ioBroker URI */
export function iobUriParse(uri: string): IobUriParsed {
const result: IobUriParsed = {
type: 'object',
address: '',
};
if (uri.startsWith('iobobject://')) {
result.type = 'object';
uri = uri.replace('iobobject://', '');
const parts = uri.split('/');
result.address = parts[0];
result.path = parts[1]; // native.schemas.myObject
} else if (uri.startsWith('iobstate://')) {
result.type = 'state';
uri = uri.replace('iobstate://', '');
const parts = uri.split('/');
result.address = parts[0];
result.path = parts[1]; // val, ts, lc, from, q, ...
} else if (uri.startsWith('iobfile://')) {
result.type = 'file';
uri = uri.replace('iobfile://', '');
const parts = uri.split('/');
result.address = parts.shift();
result.path = parts.join('/'); // main/img/hello.png
} else if (uri.startsWith('http://') || uri.startsWith('https://')) {
result.type = 'http';
result.address = uri; // https://googlw.com/path/uri?lakds=7889
} else if (uri.startsWith('data:')) {
// data:image/jpeg;base64,
result.type = 'base64';
result.address = uri; // data:image/jpeg;base64,...
} else {
// no protocol provided
const parts = uri.split('/');
if (parts.length === 2) {
result.address = parts[0];
result.path = parts[1];
if (result.path.includes('.')) {
result.type = 'object';
} else if (result.path) {
if (
result.path === 'val' ||
result.path === 'q' ||
result.path === 'ack' ||
result.path === 'ts' ||
result.path === 'lc' ||
result.path === 'from' ||
result.path === 'user' ||
result.path === 'expire' ||
result.path === 'c'
) {
result.type = 'state';
} else if (
result.path === 'common' ||
result.path === 'native' ||
result.path === 'from' ||
result.path === 'acl' ||
result.path === 'type'
) {
result.type = 'object';
} else {
throw new Error(`Unknown path: ${result.path}`);
}
} else {
result.type = 'state';
}
} else if (parts.length === 1) {
result.address = parts[0];
result.type = 'state';
} else {
// it is a file
result.address = parts.shift();
result.type = 'file';
result.path = parts.join('/');
}
}
return result;
}

export function getAttrInObject(
obj: Record<string, any> | null | undefined,
path: string[] | undefined,
_position?: number,
): any {
_position = _position || 0;
if (obj === undefined || obj === null || !path) {
return obj;
}
if (path.length - 1 === _position) {
return obj[path[_position]];
}
if (typeof obj === 'object') {
return getAttrInObject(obj[path[_position]], path, _position + 1);
}
return undefined;
}

export function setAttrInObject(
obj: Record<string, any> | null | undefined,
path: string[] | undefined,
value: any,
_position?: number,
): any {
_position = _position || 0;
if (obj === undefined || obj === null || !path) {
return value;
}
if (path.length - 1 === _position) {
obj[path[_position]] = value;
return obj;
}
if (typeof obj === 'object') {
return setAttrInObject(obj[path[_position]], path, value, _position + 1);
}
}

export async function iobUriRead(uri: IobUri | IobUriParsed, socket: Connection): Promise<any> {
if (typeof uri === 'string') {
uri = iobUriParse(uri);
}
if (uri.type === 'object') {
const obj: ioBroker.Object | null | undefined = await socket.getObject(uri.address);
return getAttrInObject(obj, uri.path?.split('.'));
}
if (uri.type === 'state') {
const state: ioBroker.State | null | undefined = await socket.getState(uri.address);
if (!uri.path) {
return state;
}
return (state as Record<string, any>)?.[uri.path];
}
if (uri.type === 'file') {
return await socket.readFile(uri.address, uri.path, true);
}
if (uri.type === 'http') {
return fetch(uri.address)
.then(response => response.text())
.then(text => {
if ((text.startsWith('{') && text.endsWith('}')) || (text.startsWith('[') && text.endsWith(']'))) {
try {
return JSON.parse(text);
} catch {
// ignore
}
}
return text;
});
}
throw new Error(`Unknown type: ${uri.type}`);
}
2 changes: 1 addition & 1 deletion packages/adapter-react-v5/src/assets/devices.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/adapter-react-v5/src/assets/rooms.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions packages/adapter-react-v5/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ export {
pattern2RegEx,
} from './LegacyConnection';

export {
type IobUri,
type IobUriType,
type IobUriParsed,
getAttrInObject,
setAttrInObject,
iobUriToString,
iobUriParse,
iobUriRead,
} from './Components/IobUri';

export type {
Translate,
ConnectionProps,
Expand Down
1 change: 1 addition & 0 deletions packages/adapter-react-v5/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function copyAllFiles() {
['src/assets/lamp_ceiling.svg', 'src/assets/lamp_table.svg', 'src/assets/no_icon.svg'],
'build/assets',
);
copyFiles(['src/i18n/*.json'], 'i18n');
// copyFiles(['README.md', 'LICENSE'], 'build');
// copyFileSync('tasksExample.js', 'build/tasks.js');
copyFiles(['src/*.css'], 'build');
Expand Down
Loading

0 comments on commit dddca8b

Please sign in to comment.