From da7b478d81cbec0b1802ff969b6b10d6d50d8db8 Mon Sep 17 00:00:00 2001
From: MauriceNino
Date: Mon, 27 Jun 2022 17:27:59 +0200
Subject: [PATCH 1/7] feat: add dash. integration
---
src/components/AppShelf/AddAppShelfItem.tsx | 27 +-
src/components/layout/Header.tsx | 21 +-
src/components/layout/Widgets.tsx | 2 +
.../modules/dash./DashdotModule.tsx | 246 ++++++++++++++++++
src/components/modules/dash./index.ts | 1 +
src/components/modules/index.ts | 9 +-
src/tools/types.ts | 15 +-
7 files changed, 275 insertions(+), 46 deletions(-)
create mode 100644 src/components/modules/dash./DashdotModule.tsx
create mode 100644 src/components/modules/dash./index.ts
diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx
index 2a0db4a98..80ee6c259 100644
--- a/src/components/AppShelf/AddAppShelfItem.tsx
+++ b/src/components/AppShelf/AddAppShelfItem.tsx
@@ -1,27 +1,13 @@
import {
- Modal,
- Center,
- Group,
- TextInput,
- Image,
- Button,
- Select,
- LoadingOverlay,
- ActionIcon,
- Tooltip,
- Title,
- Anchor,
- Text,
- Tabs,
- MultiSelect,
- ScrollArea,
- Switch,
+ ActionIcon, Anchor, Button, Center,
+ Group, Image, LoadingOverlay, Modal, MultiSelect,
+ ScrollArea, Select, Switch, Tabs, Text, TextInput, Title, Tooltip
} from '@mantine/core';
import { useForm } from '@mantine/form';
-import { useEffect, useState } from 'react';
-import { IconApps as Apps } from '@tabler/icons';
-import { v4 as uuidv4 } from 'uuid';
import { useDebouncedValue } from '@mantine/hooks';
+import { IconApps as Apps } from '@tabler/icons';
+import { useEffect, useState } from 'react';
+import { v4 as uuidv4 } from 'uuid';
import { useConfig } from '../../tools/state';
import { ServiceTypeList, StatusCodes } from '../../tools/types';
import Tip from '../layout/Tip';
@@ -85,6 +71,7 @@ function MatchPort(name: string, form: any) {
{ name: 'readarr', value: '8686' },
{ name: 'deluge', value: '8112' },
{ name: 'transmission', value: '9091' },
+ { name: 'dash.', value: '3001' },
];
// Match name with portmap key
const port = portmap.find((p) => p.name === name.toLowerCase());
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index cbfd807be..c3457a244 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -1,23 +1,23 @@
-import React from 'react';
import {
- createStyles,
- Header as Head,
- Group,
+ ActionIcon,
Box,
Burger,
+ createStyles,
Drawer,
- Title,
+ Group,
+ Header as Head,
ScrollArea,
- ActionIcon,
+ Title,
Transition,
} from '@mantine/core';
import { useBooleanToggle } from '@mantine/hooks';
-import { Logo } from './Logo';
-import SearchBar from '../modules/search/SearchModule';
import { AddItemShelfButton } from '../AppShelf/AddAppShelfItem';
-import { SettingsMenuButton } from '../Settings/SettingsMenu';
+import { CalendarModule, DateModule, TotalDownloadsModule, WeatherModule } from '../modules';
+import { DashdotModule } from '../modules/dash.';
import { ModuleWrapper } from '../modules/moduleWrapper';
-import { CalendarModule, TotalDownloadsModule, WeatherModule, DateModule } from '../modules';
+import SearchBar from '../modules/search/SearchModule';
+import { SettingsMenuButton } from '../Settings/SettingsMenu';
+import { Logo } from './Logo';
const HEADER_HEIGHT = 60;
@@ -84,6 +84,7 @@ export function Header(props: any) {
+
diff --git a/src/components/layout/Widgets.tsx b/src/components/layout/Widgets.tsx
index b82f489a9..0eceae4c4 100644
--- a/src/components/layout/Widgets.tsx
+++ b/src/components/layout/Widgets.tsx
@@ -1,6 +1,7 @@
import { Group } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import { CalendarModule, DateModule, TotalDownloadsModule, WeatherModule } from '../modules';
+import { DashdotModule } from '../modules/dash.';
import { ModuleWrapper } from '../modules/moduleWrapper';
export default function Widgets(props: any) {
@@ -14,6 +15,7 @@ export default function Widgets(props: any) {
+
)}
>
diff --git a/src/components/modules/dash./DashdotModule.tsx b/src/components/modules/dash./DashdotModule.tsx
new file mode 100644
index 000000000..979fdba76
--- /dev/null
+++ b/src/components/modules/dash./DashdotModule.tsx
@@ -0,0 +1,246 @@
+import { createStyles, useMantineColorScheme, useMantineTheme } from '@mantine/core';
+import { IconCalendar as CalendarIcon } from '@tabler/icons';
+import axios from 'axios';
+import { useEffect, useState } from 'react';
+import { useConfig } from '../../../tools/state';
+import { serviceItem } from '../../../tools/types';
+import { IModule } from '../modules';
+
+const asModule = (t: T) => t;
+export const DashdotModule = asModule({
+ title: 'Dash.',
+ description: 'A module for displaying the graphs of your running Dash. instance.',
+ icon: CalendarIcon,
+ component: DashdotComponent,
+ options: {
+ cpuMultiView: {
+ name: 'CPU Multi-Core View',
+ value: false,
+ },
+ storageMultiView: {
+ name: 'Storage Multi-Drive View',
+ value: false,
+ },
+ useCompactView: {
+ name: 'Use Compact View',
+ value: false,
+ },
+ showCpu: {
+ name: 'Show CPU Graph',
+ value: true,
+ },
+ showStorage: {
+ name: 'Show Storage Graph',
+ value: true,
+ },
+ showRam: {
+ name: 'Show RAM Graph',
+ value: true,
+ },
+ showNetwork: {
+ name: 'Show Network Graphs',
+ value: true,
+ },
+ showGpu: {
+ name: 'Show GPU Graph',
+ value: false,
+ },
+ },
+});
+
+const useStyles = createStyles((theme, _params) => ({
+ heading: {
+ marginTop: 0,
+ marginBottom: 10,
+ },
+ table: {
+ display: 'table',
+ },
+ tableRow: {
+ display: 'table-row',
+ },
+ tableLabel: {
+ display: 'table-cell',
+ paddingRight: 10,
+ },
+ tableValue: {
+ display: 'table-cell',
+ whiteSpace: 'pre-wrap',
+ paddingBottom: 5,
+ },
+ graphsContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ rowGap: 15,
+ columnGap: 10,
+ },
+ iframe: {
+ flex: '1 0 auto',
+ maxWidth: '100%',
+ height: '140px',
+ borderRadius: theme.radius.lg,
+ },
+}));
+
+const bpsPrettyPrint = (bits?: number) =>
+ !bits
+ ? '-'
+ : bits > 1000 * 1000 * 1000
+ ? `${(bits / 1000 / 1000 / 1000).toFixed(1)} Gb/s`
+ : bits > 1000 * 1000
+ ? `${(bits / 1000 / 1000).toFixed(1)} Mb/s`
+ : bits > 1000
+ ? `${(bits / 1000).toFixed(1)} Kb/s`
+ : `${bits.toFixed(1)} b/s`;
+
+const bytePrettyPrint = (byte: number): string =>
+ byte > 1024 * 1024 * 1024
+ ? `${(byte / 1024 / 1024 / 1024).toFixed(1)} GiB`
+ : byte > 1024 * 1024
+ ? `${(byte / 1024 / 1024).toFixed(1)} MiB`
+ : byte > 1024
+ ? `${(byte / 1024).toFixed(1)} KiB`
+ : `${byte.toFixed(1)} B`;
+
+const useJson = (service: serviceItem | undefined, url: string) => {
+ const [data, setData] = useState();
+
+ const doRequest = async () => {
+ try {
+ const resp = await axios.get(url, { baseURL: service?.url });
+
+ setData(resp.data);
+ // eslint-disable-next-line no-empty
+ } catch (e) {}
+ };
+
+ useEffect(() => {
+ if (service?.url) {
+ doRequest();
+ }
+ }, [service?.url]);
+
+ return data;
+};
+
+export function DashdotComponent() {
+ const { config } = useConfig();
+ const theme = useMantineTheme();
+ const { classes } = useStyles();
+ const { colorScheme } = useMantineColorScheme();
+
+ const dashConfig = config.modules?.[DashdotModule.title]
+ .options as typeof DashdotModule['options'];
+ const isCompact = dashConfig?.useCompactView?.value ?? false;
+ const dashdotService = config.services.filter((service) => service.type === 'Dash.')[0];
+
+ const cpuEnabled = dashConfig?.showCpu?.value ?? true;
+ const storageEnabled = dashConfig?.showStorage?.value ?? true;
+ const ramEnabled = dashConfig?.showRam?.value ?? true;
+ const networkEnabled = dashConfig?.showNetwork?.value ?? true;
+ const gpuEnabled = dashConfig?.showGpu?.value ?? false;
+
+ const info = useJson(dashdotService, '/info');
+ const storageLoad = useJson(dashdotService, '/load/storage');
+
+ const totalUsed =
+ (storageLoad?.layout as any[])?.reduce((acc, curr) => (curr.load ?? 0) + acc, 0) ?? 0;
+ const totalSize =
+ (info?.storage?.layout as any[])?.reduce((acc, curr) => (curr.size ?? 0) + acc, 0) ?? 0;
+
+ const graphs = [
+ {
+ name: 'CPU',
+ enabled: cpuEnabled,
+ params: {
+ multiView: dashConfig?.cpuMultiView?.value ?? false,
+ },
+ },
+ {
+ name: 'Storage',
+ enabled: storageEnabled && !isCompact,
+ params: {
+ multiView: dashConfig?.storageMultiView?.value ?? false,
+ },
+ },
+ {
+ name: 'RAM',
+ enabled: ramEnabled,
+ },
+ {
+ name: 'Network',
+ enabled: networkEnabled,
+ spanTwo: true,
+ },
+ {
+ name: 'GPU',
+ enabled: gpuEnabled,
+ spanTwo: true,
+ },
+ ].filter((g) => g.enabled);
+
+ return (
+
+
Dash.
+
+ {!dashdotService ? (
+
No dash. service found. Please add one to your Homarr dashboard.
+ ) : !info ? (
+
Cannot acquire information from dash. - are you running the latest version?
+ ) : (
+
+
+ {storageEnabled && isCompact && (
+
+
Storage:
+
+ {(totalUsed / (totalSize || 1)).toFixed(1)}%{'\n'}
+ {bytePrettyPrint(totalUsed)} / {bytePrettyPrint(totalSize)}
+
+
+ )}
+
+
+
Network:
+
+ {bpsPrettyPrint(info?.network?.speedUp)} Up{'\n'}
+ {bpsPrettyPrint(info?.network?.speedDown)} Down
+
+
+
+
+ {graphs.map((graph) => (
+
+ )}
+
+ );
+}
diff --git a/src/components/modules/dash./index.ts b/src/components/modules/dash./index.ts
new file mode 100644
index 000000000..4d483ea4d
--- /dev/null
+++ b/src/components/modules/dash./index.ts
@@ -0,0 +1 @@
+export { DashdotModule } from './DashdotModule';
diff --git a/src/components/modules/index.ts b/src/components/modules/index.ts
index 410bf3b56..85b4b765b 100644
--- a/src/components/modules/index.ts
+++ b/src/components/modules/index.ts
@@ -1,6 +1,7 @@
-export * from './date';
export * from './calendar';
-export * from './search';
-export * from './ping';
-export * from './weather';
+export * from './dash.';
+export * from './date';
export * from './downloads';
+export * from './ping';
+export * from './search';
+export * from './weather';
diff --git a/src/tools/types.ts b/src/tools/types.ts
index c66197147..5146c30d5 100644
--- a/src/tools/types.ts
+++ b/src/tools/types.ts
@@ -61,6 +61,7 @@ export const Targets = [
export const ServiceTypeList = [
'Other',
'Emby',
+ 'Dash.',
'Deluge',
'Lidarr',
'Plex',
@@ -69,18 +70,8 @@ export const ServiceTypeList = [
'Sonarr',
'qBittorrent',
'Transmission',
-];
-export type ServiceType =
- | 'Other'
- | 'Emby'
- | 'Deluge'
- | 'Lidarr'
- | 'Plex'
- | 'Radarr'
- | 'Readarr'
- | 'Sonarr'
- | 'qBittorrent'
- | 'Transmission';
+] as const;
+export type ServiceType = typeof ServiceTypeList[number];
export interface serviceItem {
id: string;
From c0ecc3d4c6bdf412f9fb5e9230386349279b4a56 Mon Sep 17 00:00:00 2001
From: MauriceNino
Date: Mon, 27 Jun 2022 21:29:44 +0200
Subject: [PATCH 2/7] fix: types
---
src/tools/types.ts | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/tools/types.ts b/src/tools/types.ts
index 5146c30d5..72b35bc56 100644
--- a/src/tools/types.ts
+++ b/src/tools/types.ts
@@ -70,8 +70,19 @@ export const ServiceTypeList = [
'Sonarr',
'qBittorrent',
'Transmission',
-] as const;
-export type ServiceType = typeof ServiceTypeList[number];
+];
+export type ServiceType =
+ | 'Other'
+ | 'Emby'
+ | 'Dash.'
+ | 'Deluge'
+ | 'Lidarr'
+ | 'Plex'
+ | 'Radarr'
+ | 'Readarr'
+ | 'Sonarr'
+ | 'qBittorrent'
+ | 'Transmission';
export interface serviceItem {
id: string;
From eb0313f551058cd71aa5b133cdbaba5d2346cb8a Mon Sep 17 00:00:00 2001
From: MauriceNino
Date: Mon, 27 Jun 2022 21:30:08 +0200
Subject: [PATCH 3/7] fix: transform dash. -> dashdot for icon find
---
src/components/AppShelf/AddAppShelfItem.tsx | 25 +++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx
index 80ee6c259..8cae277ab 100644
--- a/src/components/AppShelf/AddAppShelfItem.tsx
+++ b/src/components/AppShelf/AddAppShelfItem.tsx
@@ -1,7 +1,21 @@
import {
- ActionIcon, Anchor, Button, Center,
- Group, Image, LoadingOverlay, Modal, MultiSelect,
- ScrollArea, Select, Switch, Tabs, Text, TextInput, Title, Tooltip
+ ActionIcon,
+ Anchor,
+ Button,
+ Center,
+ Group,
+ Image,
+ LoadingOverlay,
+ Modal,
+ MultiSelect,
+ ScrollArea,
+ Select,
+ Switch,
+ Tabs,
+ Text,
+ TextInput,
+ Title,
+ Tooltip,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { useDebouncedValue } from '@mantine/hooks';
@@ -42,10 +56,13 @@ export function AddItemShelfButton(props: any) {
}
function MatchIcon(name: string, form: any) {
+ console.log(name, name.replace(/^dash\.$/, 'dashdot'));
+
fetch(
`https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/${name
.replace(/\s+/g, '-')
- .toLowerCase()}.png`
+ .toLowerCase()
+ .replace(/^dash\.$/, 'dashdot')}.png`
).then((res) => {
if (res.ok) {
form.setFieldValue('icon', res.url);
From 72832a57670658fdd3efe1bfad808a067a089cdf Mon Sep 17 00:00:00 2001
From: MauriceNino
Date: Tue, 28 Jun 2022 12:51:44 +0200
Subject: [PATCH 4/7] fix: move enabled options to multi-select
---
.../modules/dash./DashdotModule.tsx | 34 ++++++-------------
1 file changed, 10 insertions(+), 24 deletions(-)
diff --git a/src/components/modules/dash./DashdotModule.tsx b/src/components/modules/dash./DashdotModule.tsx
index 979fdba76..4bc73b038 100644
--- a/src/components/modules/dash./DashdotModule.tsx
+++ b/src/components/modules/dash./DashdotModule.tsx
@@ -25,25 +25,10 @@ export const DashdotModule = asModule({
name: 'Use Compact View',
value: false,
},
- showCpu: {
- name: 'Show CPU Graph',
- value: true,
- },
- showStorage: {
- name: 'Show Storage Graph',
- value: true,
- },
- showRam: {
- name: 'Show RAM Graph',
- value: true,
- },
- showNetwork: {
- name: 'Show Network Graphs',
- value: true,
- },
- showGpu: {
- name: 'Show GPU Graph',
- value: false,
+ graphs: {
+ name: 'Graphs',
+ value: ['CPU', 'RAM', 'Storage', 'Network'],
+ options: ['CPU', 'RAM', 'Storage', 'Network', 'GPU'],
},
},
});
@@ -135,11 +120,12 @@ export function DashdotComponent() {
const isCompact = dashConfig?.useCompactView?.value ?? false;
const dashdotService = config.services.filter((service) => service.type === 'Dash.')[0];
- const cpuEnabled = dashConfig?.showCpu?.value ?? true;
- const storageEnabled = dashConfig?.showStorage?.value ?? true;
- const ramEnabled = dashConfig?.showRam?.value ?? true;
- const networkEnabled = dashConfig?.showNetwork?.value ?? true;
- const gpuEnabled = dashConfig?.showGpu?.value ?? false;
+ const enabledGraphs = dashConfig?.graphs?.value ?? ['CPU', 'RAM', 'Storage', 'Network'];
+ const cpuEnabled = enabledGraphs.includes('CPU');
+ const storageEnabled = enabledGraphs.includes('Storage');
+ const ramEnabled = enabledGraphs.includes('RAM');
+ const networkEnabled = enabledGraphs.includes('Network');
+ const gpuEnabled = enabledGraphs.includes('GPU');
const info = useJson(dashdotService, '/info');
const storageLoad = useJson(dashdotService, '/load/storage');
From 60b88389a6a1f1f597b2ad338620ccc3f9eb9a55 Mon Sep 17 00:00:00 2001
From: MauriceNino
Date: Wed, 29 Jun 2022 13:52:14 +0200
Subject: [PATCH 5/7] fix: remove leftover console.log
---
src/components/AppShelf/AddAppShelfItem.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx
index 8cae277ab..fedf01df0 100644
--- a/src/components/AppShelf/AddAppShelfItem.tsx
+++ b/src/components/AppShelf/AddAppShelfItem.tsx
@@ -56,8 +56,6 @@ export function AddItemShelfButton(props: any) {
}
function MatchIcon(name: string, form: any) {
- console.log(name, name.replace(/^dash\.$/, 'dashdot'));
-
fetch(
`https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/${name
.replace(/\s+/g, '-')
From 0bf95483f9b85e6afae2ee1001b6d4e6385afd3d Mon Sep 17 00:00:00 2001
From: MauriceNino
Date: Thu, 30 Jun 2022 16:08:26 +0200
Subject: [PATCH 6/7] fix: styles for dash. widget
---
src/components/modules/dash./DashdotModule.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/modules/dash./DashdotModule.tsx b/src/components/modules/dash./DashdotModule.tsx
index 4bc73b038..b8b6b8120 100644
--- a/src/components/modules/dash./DashdotModule.tsx
+++ b/src/components/modules/dash./DashdotModule.tsx
@@ -57,7 +57,7 @@ const useStyles = createStyles((theme, _params) => ({
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
- rowGap: 15,
+ rowGap: 10,
columnGap: 10,
},
iframe: {
@@ -214,7 +214,7 @@ export function DashdotComponent() {
'dark'
? theme.colors.dark[7]
: theme.colors.gray[0]
- ).substring(1)}${isCompact ? '&gap=1' : `&gap=5&innerRadius=${theme.radius.md}`}${
+ ).substring(1)}${isCompact ? '&gap=10' : '&gap=5'}&innerRadius=${theme.radius.lg}${
graph.params
? `&${Object.entries(graph.params)
.map(([key, value]) => `${key}=${value.toString()}`)
From c313eacefd5c12ce5b1c56d679f6c4bef369d0bf Mon Sep 17 00:00:00 2001
From: "Thomas \"ajnart\" Camlong"
Date: Wed, 20 Jul 2022 14:47:51 +0200
Subject: [PATCH 7/7] :bug: Fix small bug with the network module
---
src/components/modules/dash./DashdotModule.tsx | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/components/modules/dash./DashdotModule.tsx b/src/components/modules/dash./DashdotModule.tsx
index b8b6b8120..ff6175629 100644
--- a/src/components/modules/dash./DashdotModule.tsx
+++ b/src/components/modules/dash./DashdotModule.tsx
@@ -186,14 +186,15 @@ export function DashdotComponent() {
)}
-
-
-
Network:
-
- {bpsPrettyPrint(info?.network?.speedUp)} Up{'\n'}
- {bpsPrettyPrint(info?.network?.speedDown)} Down
-
-
+ {networkEnabled && (
+
+
Network:
+
+ {bpsPrettyPrint(info?.network?.speedUp)} Up{'\n'}
+ {bpsPrettyPrint(info?.network?.speedDown)} Down
+
+
+ )}
{graphs.map((graph) => (