|
@@ -0,0 +1,152 @@
|
|
|
|
|
+import { consoleFetchJSON } from '@openshift-console/dynamic-plugin-sdk';
|
|
|
|
|
+
|
|
|
|
|
+import {
|
|
|
|
|
+ Alert,
|
|
|
|
|
+ Card,
|
|
|
|
|
+ CardBody,
|
|
|
|
|
+ CardTitle,
|
|
|
|
|
+ Spinner,
|
|
|
|
|
+} from '@patternfly/react-core';
|
|
|
|
|
+
|
|
|
|
|
+import type { FC } from 'react';
|
|
|
|
|
+import { useEffect, useState } from 'react';
|
|
|
|
|
+
|
|
|
|
|
+interface GameStatus {
|
|
|
|
|
+ game: {
|
|
|
|
|
+ running: boolean;
|
|
|
|
|
+ appid: number | null;
|
|
|
|
|
+ name: string | null;
|
|
|
|
|
+ processes: number;
|
|
|
|
|
+ cpuPercent: number;
|
|
|
|
|
+ memoryMiB: number;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ sunshine: {
|
|
|
|
|
+ sessionActive: boolean;
|
|
|
|
|
+ sessions: number;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ updatedAt: string | null;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const STATUS_URL =
|
|
|
|
|
+ '/api/proxy/plugin/homelab-dashboard/game-status/api/status';
|
|
|
|
|
+
|
|
|
|
|
+const REFRESH_INTERVAL_MS = 5_000;
|
|
|
|
|
+const REQUEST_TIMEOUT_MS = 1_000;
|
|
|
|
|
+
|
|
|
|
|
+const GamingCard: FC = () => {
|
|
|
|
|
+ const [status, setStatus] = useState<GameStatus>();
|
|
|
|
|
+ const [fetchError, setFetchError] = useState(false);
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ let stopped = false;
|
|
|
|
|
+ let timer: number | undefined;
|
|
|
|
|
+
|
|
|
|
|
+ const poll = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const result = (await consoleFetchJSON(
|
|
|
|
|
+ STATUS_URL,
|
|
|
|
|
+ 'GET',
|
|
|
|
|
+ {},
|
|
|
|
|
+ REQUEST_TIMEOUT_MS,
|
|
|
|
|
+ )) as GameStatus;
|
|
|
|
|
+
|
|
|
|
|
+ if (!stopped) {
|
|
|
|
|
+ setStatus(result);
|
|
|
|
|
+ setFetchError(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ if (!stopped) {
|
|
|
|
|
+ setFetchError(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (!stopped) {
|
|
|
|
|
+ timer = window.setTimeout(
|
|
|
|
|
+ poll,
|
|
|
|
|
+ REFRESH_INTERVAL_MS,
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ void poll();
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ stopped = true;
|
|
|
|
|
+
|
|
|
|
|
+ if (timer !== undefined) {
|
|
|
|
|
+ window.clearTimeout(timer);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <CardTitle>Gaming</CardTitle>
|
|
|
|
|
+
|
|
|
|
|
+ <CardBody>
|
|
|
|
|
+ {!status && !fetchError ? (
|
|
|
|
|
+ <Spinner size="md" />
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <>
|
|
|
|
|
+ {fetchError && (
|
|
|
|
|
+ <Alert
|
|
|
|
|
+ isInline
|
|
|
|
|
+ variant="warning"
|
|
|
|
|
+ title="Gaming status unavailable"
|
|
|
|
|
+ >
|
|
|
|
|
+ The Sunshine status service did not respond
|
|
|
|
|
+ within one second.
|
|
|
|
|
+ </Alert>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
|
|
+ {status && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ Current game:{' '}
|
|
|
|
|
+ <strong>
|
|
|
|
|
+ {status.game.running
|
|
|
|
|
+ ? status.game.name ?? 'Unknown game'
|
|
|
|
|
+ : 'None'}
|
|
|
|
|
+ </strong>
|
|
|
|
|
+ </p>
|
|
|
|
|
+
|
|
|
|
|
+ {status.game.running && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <p>
|
|
|
|
|
+ CPU:{' '}
|
|
|
|
|
+ <strong>
|
|
|
|
|
+ {status.game.cpuPercent.toFixed(1)}%
|
|
|
|
|
+ </strong>
|
|
|
|
|
+ </p>
|
|
|
|
|
+
|
|
|
|
|
+ <p>
|
|
|
|
|
+ RAM:{' '}
|
|
|
|
|
+ <strong>
|
|
|
|
|
+ {status.game.memoryMiB.toFixed(1)} MiB
|
|
|
|
|
+ </strong>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
|
|
+ <p>
|
|
|
|
|
+ Sunshine session:{' '}
|
|
|
|
|
+ <strong>
|
|
|
|
|
+ {status.sunshine.sessionActive
|
|
|
|
|
+ ? status.sunshine.sessions > 1
|
|
|
|
|
+ ? `Yes (${status.sunshine.sessions})`
|
|
|
|
|
+ : 'Yes'
|
|
|
|
|
+ : 'No'}
|
|
|
|
|
+ </strong>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </CardBody>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ );
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+export default GamingCard;
|