Skip to content

Commit 0329dcd

Browse files
committed
Implemented support of plugins
1 parent 82be5d8 commit 0329dcd

10 files changed

Lines changed: 464 additions & 374 deletions

File tree

Lines changed: 196 additions & 196 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

admin/index_m.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/>
2323
<script type="text/javascript" onerror="setTimeout(function(){window.location.reload()}, 5000)" src="./lib/js/socket.io.js"></script>
2424
<title>Devices</title>
25-
<script type="module" crossorigin src="./assets/index-BhcBQqRL.js"></script>
25+
<script type="module" crossorigin src="./assets/index-1HzRpVZL.js"></script>
2626
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
2727
<link rel="modulepreload" crossorigin href="./assets/virtual_mf-REMOTE_ENTRY_ID_iobroker_devices__remoteEntry_js-D5_qLLYC.js">
2828
<link rel="stylesheet" crossorigin href="./assets/index-pCeIEsc7.css">

admin/tab.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/>
2323
<script type="text/javascript" onerror="setTimeout(function(){window.location.reload()}, 5000)" src="./lib/js/socket.io.js"></script>
2424
<title>Devices</title>
25-
<script type="module" crossorigin src="./assets/index-BhcBQqRL.js"></script>
25+
<script type="module" crossorigin src="./assets/index-1HzRpVZL.js"></script>
2626
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
2727
<link rel="modulepreload" crossorigin href="./assets/virtual_mf-REMOTE_ENTRY_ID_iobroker_devices__remoteEntry_js-D5_qLLYC.js">
2828
<link rel="stylesheet" crossorigin href="./assets/index-pCeIEsc7.css">

src-admin/src/WidgetsManager/Widgets/ChartDialog.tsx

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,59 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
233233
globalTsMin = Date.now() - 3_600_000;
234234
globalTsMax = Date.now();
235235
}
236-
const valRange = globalValMax - globalValMin || 1;
236+
let valRange = globalValMax - globalValMin || 1;
237237
globalValMin -= valRange * 0.05;
238238
globalValMax += valRange * 0.05;
239239

240+
// Detect dual-axis mode: exactly 2 series with different, non-empty units
241+
const dualAxis =
242+
series.length === 2 && !!series[0]?.unit && !!series[1]?.unit && series[0].unit !== series[1].unit;
243+
244+
let series1ValMin = 0;
245+
let series1ValMax = 1;
246+
247+
if (dualAxis) {
248+
// Override global range to only reflect series[0] (left axis)
249+
globalValMin = Infinity;
250+
globalValMax = -Infinity;
251+
for (const p of series[0].data) {
252+
if (p.val < globalValMin) {
253+
globalValMin = p.val;
254+
}
255+
if (p.val > globalValMax) {
256+
globalValMax = p.val;
257+
}
258+
}
259+
if (globalValMin === Infinity) {
260+
globalValMin = 0;
261+
globalValMax = 1;
262+
}
263+
valRange = globalValMax - globalValMin || 1;
264+
globalValMin -= valRange * 0.05;
265+
globalValMax += valRange * 0.05;
266+
267+
// Compute series[1] range (right axis)
268+
series1ValMin = Infinity;
269+
series1ValMax = -Infinity;
270+
for (const p of series[1].data) {
271+
if (p.val < series1ValMin) {
272+
series1ValMin = p.val;
273+
}
274+
if (p.val > series1ValMax) {
275+
series1ValMax = p.val;
276+
}
277+
}
278+
if (series1ValMin === Infinity) {
279+
series1ValMin = 0;
280+
series1ValMax = 1;
281+
}
282+
const range1 = series1ValMax - series1ValMin || 1;
283+
series1ValMin -= range1 * 0.05;
284+
series1ValMax += range1 * 0.05;
285+
}
286+
287+
const marginRight = dualAxis ? 52 : MARGIN.right;
288+
240289
// Pan/zoom state: visible time range
241290
const [viewStart, setViewStart] = useState(globalTsMin);
242291
const [viewEnd, setViewEnd] = useState(globalTsMax);
@@ -278,7 +327,7 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
278327
[onViewSettle],
279328
);
280329

281-
const plotW = width - MARGIN.left - MARGIN.right;
330+
const plotW = width - MARGIN.left - marginRight;
282331
const plotH = height - MARGIN.top - MARGIN.bottom;
283332

284333
// Scales
@@ -290,6 +339,10 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
290339
(val: number) => MARGIN.top + plotH - ((val - globalValMin) / (globalValMax - globalValMin)) * plotH,
291340
[globalValMin, globalValMax, plotH],
292341
);
342+
const valToY2 = useCallback(
343+
(val: number) => MARGIN.top + plotH - ((val - series1ValMin) / (series1ValMax - series1ValMin)) * plotH,
344+
[series1ValMin, series1ValMax, plotH],
345+
);
293346
const xToTs = useCallback(
294347
(x: number) => viewStart + ((x - MARGIN.left) / plotW) * (viewEnd - viewStart),
295348
[viewStart, viewEnd, plotW],
@@ -327,7 +380,7 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
327380
}
328381
const mx = e.clientX - rect.left;
329382
const my = e.clientY - rect.top;
330-
if (mx < MARGIN.left || mx > width - MARGIN.right || my < MARGIN.top || my > height - MARGIN.bottom) {
383+
if (mx < MARGIN.left || mx > width - marginRight || my < MARGIN.top || my > height - MARGIN.bottom) {
331384
setTooltip(null);
332385
return;
333386
}
@@ -346,18 +399,20 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
346399
}
347400
// Build entries for all series at bestTs (interpolate if needed)
348401
const entries: { val: number; y: number; color: string; name?: string; unit?: string }[] = [];
349-
for (const s of series) {
402+
for (let si = 0; si < series.length; si++) {
403+
const s = series[si];
350404
const val = interpolateAt(s.data, bestTs);
351405
if (val != null) {
352-
entries.push({ val, y: valToY(val), color: s.color, name: s.name, unit: s.unit });
406+
const yFn = dualAxis && si === 1 ? valToY2 : valToY;
407+
entries.push({ val, y: yFn(val), color: s.color, name: s.name, unit: s.unit });
353408
}
354409
}
355410
if (entries.length) {
356411
setTooltip({ x: tsToX(bestTs), ts: bestTs, entries });
357412
}
358413
}
359414
},
360-
[series, plotW, width, height, xToTs, tsToX, valToY],
415+
[series, plotW, width, height, xToTs, tsToX, valToY, dualAxis, valToY2, marginRight],
361416
);
362417

363418
const handlePointerUp = useCallback(
@@ -412,15 +467,16 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
412467
continue;
413468
}
414469
const smoothed = smoothing > 0 ? smoothData(visible, smoothing) : visible;
415-
const points = smoothed.map(p => ({ x: tsToX(p.ts), y: valToY(p.val) }));
470+
const yFn = dualAxis && i === 1 ? valToY2 : valToY;
471+
const points = smoothed.map(p => ({ x: tsToX(p.ts), y: yFn(p.val) }));
416472
const d = buildLinePath(points, chartType);
417473
if (!d) {
418474
continue;
419475
}
420476
// Area fill — need first/last X for closing the area path
421477
const firstX = points[0].x.toFixed(1);
422478
const lastX = points[points.length - 1].x.toFixed(1);
423-
const baseY = valToY(globalValMin).toFixed(1);
479+
const baseY = (MARGIN.top + plotH).toFixed(1);
424480
paths.push(
425481
<React.Fragment key={i}>
426482
<path
@@ -448,14 +504,24 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
448504
timeTicks.push(t);
449505
}
450506

451-
// Value axis ticks
507+
// Value axis ticks (left axis)
452508
const valStep = niceStep(globalValMax - globalValMin, Math.max(3, Math.floor(plotH / 40)));
453509
const valTicks: number[] = [];
454510
const firstVal = Math.ceil(globalValMin / valStep) * valStep;
455511
for (let v = firstVal; v <= globalValMax; v += valStep) {
456512
valTicks.push(v);
457513
}
458514

515+
// Right Y-axis ticks (dual-axis mode)
516+
const val2Ticks: number[] = [];
517+
if (dualAxis) {
518+
const val2Step = niceStep(series1ValMax - series1ValMin, Math.max(3, Math.floor(plotH / 40)));
519+
const firstVal2 = Math.ceil(series1ValMin / val2Step) * val2Step;
520+
for (let v = firstVal2; v <= series1ValMax; v += val2Step) {
521+
val2Ticks.push(v);
522+
}
523+
}
524+
459525
return (
460526
<svg
461527
ref={svgRef}
@@ -484,7 +550,7 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
484550
<line
485551
key={`vg${v}`}
486552
x1={MARGIN.left}
487-
x2={width - MARGIN.right}
553+
x2={width - marginRight}
488554
y1={y}
489555
y2={y}
490556
stroke="currentColor"
@@ -538,7 +604,7 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
538604
);
539605
})}
540606

541-
{/* Value axis labels */}
607+
{/* Value axis labels (left) */}
542608
{valTicks.map(v => {
543609
const y = valToY(v);
544610
return (
@@ -548,14 +614,33 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
548614
y={y + 4}
549615
textAnchor="end"
550616
fontSize={11}
551-
fill="currentColor"
552-
opacity={0.5}
617+
fill={dualAxis ? series[0].color : 'currentColor'}
618+
opacity={dualAxis ? 0.7 : 0.5}
553619
>
554620
{formatValue(v, isFloatComma)}
555621
</text>
556622
);
557623
})}
558624

625+
{/* Value axis labels (right, dual-axis mode) */}
626+
{dualAxis &&
627+
val2Ticks.map(v => {
628+
const y = valToY2(v);
629+
return (
630+
<text
631+
key={`vr${v}`}
632+
x={width - marginRight + 6}
633+
y={y + 4}
634+
textAnchor="start"
635+
fontSize={11}
636+
fill={series[1].color}
637+
opacity={0.7}
638+
>
639+
{formatValue(v, isFloatComma)}
640+
</text>
641+
);
642+
})}
643+
559644
{/* Tooltip crosshair + dots + label */}
560645
{tooltip
561646
? (() => {
@@ -587,8 +672,8 @@ function InteractiveChart(props: InteractiveChartProps): React.JSX.Element {
587672
if (boxX < MARGIN.left) {
588673
boxX = MARGIN.left;
589674
}
590-
if (boxX + boxW > width - MARGIN.right) {
591-
boxX = width - MARGIN.right - boxW;
675+
if (boxX + boxW > width - marginRight) {
676+
boxX = width - marginRight - boxW;
592677
}
593678

594679
return (

src-admin/src/WidgetsManager/Widgets/PluginWidget.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,12 @@ export class PluginWidget extends Component<PluginWidgetProps, PluginWidgetState
9494
alignItems: 'center',
9595
justifyContent: 'center',
9696
width: '100%',
97-
aspectRatio: this.props.settings.size === '2x0.5' ? undefined : '1',
97+
aspectRatio:
98+
this.props.settings.size === '2x0.5'
99+
? undefined
100+
: this.props.settings.size === '2x1'
101+
? '2'
102+
: '1',
98103
height: this.props.settings.size === '2x0.5' ? 80 : undefined,
99104
overflow: 'hidden',
100105
...getTileStyles(theme, false, this.props.settings.color, false),

src-admin/src/WidgetsManager/pluginLoader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export async function loadPluginComponent(
8585
registerRemotes([
8686
{
8787
name: uniqueName,
88-
entry: admin ? `../adapter/${adapterName}/dm-widgets/${url}` : `../files/${adapterName}/${url}`,
88+
entry: admin ? `../adapter/${adapterName}/dm-widgets/${url}` : `../${adapterName}/${url}`,
8989
type: 'module',
9090
},
9191
]);
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 156 additions & 156 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

www/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
/>
2121
<script type="text/javascript" onerror="setTimeout(function(){window.location.reload()}, 5000)" src="./lib/js/socket.io.js"></script>
2222
<title>Devices</title>
23-
<script type="module" crossorigin src="./assets/index-CIqAyh6p.js"></script>
23+
<script type="module" crossorigin src="./assets/index-XkCLkGou.js"></script>
2424
<link rel="stylesheet" crossorigin href="./assets/index-CmwOqvIb.css">
2525
</head>
2626
<body>

0 commit comments

Comments
 (0)