kvm/ui/src/components/extensions/ATXPowerControl.tsx

174 lines
5.4 KiB
TypeScript

import { LuHardDrive, LuPower, LuRotateCcw } from "react-icons/lu";
import { useEffect, useState } from "react";
import { Button } from "@components/Button";
import Card from "@components/Card";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import notifications from "@/notifications";
import LoadingSpinner from "@/components/LoadingSpinner";
import { useJsonRpc } from "../../hooks/useJsonRpc";
const LONG_PRESS_DURATION = 3000; // 3 seconds for long press
interface ATXState {
power: boolean;
hdd: boolean;
}
export function ATXPowerControl() {
const [isPowerPressed, setIsPowerPressed] = useState(false);
const [powerPressTimer, setPowerPressTimer] = useState<ReturnType<
typeof setTimeout
> | null>(null);
const [atxState, setAtxState] = useState<ATXState | null>(null);
const [send] = useJsonRpc(function onRequest(resp) {
if (resp.method === "atxState") {
setAtxState(resp.params as ATXState);
}
});
// Request initial state
useEffect(() => {
send("getATXState", {}, resp => {
if ("error" in resp) {
notifications.error(
`Failed to get ATX state: ${resp.error.data || "Unknown error"}`,
);
return;
}
setAtxState(resp.result as ATXState);
});
}, [send]);
const handlePowerPress = (pressed: boolean) => {
// Prevent phantom releases
if (!pressed && !isPowerPressed) return;
setIsPowerPressed(pressed);
// Handle button press
if (pressed) {
// Start long press timer
const timer = setTimeout(() => {
// Send long press action
console.log("Sending long press ATX power action");
send("setATXPowerAction", { action: "power-long" }, resp => {
if ("error" in resp) {
notifications.error(
`Failed to send ATX power action: ${resp.error.data || "Unknown error"}`,
);
}
setIsPowerPressed(false);
});
}, LONG_PRESS_DURATION);
setPowerPressTimer(timer);
}
// Handle button release
else {
// If timer exists, was a short press
if (powerPressTimer) {
clearTimeout(powerPressTimer);
setPowerPressTimer(null);
// Send short press action
console.log("Sending short press ATX power action");
send("setATXPowerAction", { action: "power-short" }, resp => {
if ("error" in resp) {
notifications.error(
`Failed to send ATX power action: ${resp.error.data || "Unknown error"}`,
);
}
});
}
}
};
// Cleanup timer on unmount
useEffect(() => {
return () => {
if (powerPressTimer) {
clearTimeout(powerPressTimer);
}
};
}, [powerPressTimer]);
return (
<div className="space-y-4">
<SettingsPageHeader
title="ATX Power Control"
description="Control your ATX power settings"
/>
{atxState === null ? (
<Card className="flex h-[120px] items-center justify-center p-3">
<LoadingSpinner className="h-6 w-6 text-blue-500 dark:text-blue-400" />
</Card>
) : (
<Card className="h-[120px] animate-fadeIn opacity-0">
<div className="space-y-4 p-3">
{/* Control Buttons */}
<div className="flex items-center space-x-2">
<Button
size="SM"
theme="light"
LeadingIcon={LuPower}
text="Power"
onMouseDown={() => handlePowerPress(true)}
onMouseUp={() => handlePowerPress(false)}
onMouseLeave={() => handlePowerPress(false)}
className={isPowerPressed ? "opacity-75" : ""}
/>
<Button
size="SM"
theme="light"
LeadingIcon={LuRotateCcw}
text="Reset"
onClick={() => {
send("setATXPowerAction", { action: "reset" }, resp => {
if ("error" in resp) {
notifications.error(
`Failed to send ATX power action: ${resp.error.data || "Unknown error"}`,
);
return;
}
});
}}
/>
</div>
<hr className="border-slate-700/30 dark:border-slate-600/30" />
{/* Status Indicators */}
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2">
<span className="text-sm text-slate-600 dark:text-slate-400">
<LuPower
strokeWidth={3}
className={`mr-1 inline ${
atxState?.power ? "text-green-600" : "text-slate-300"
}`}
/>
Power LED
</span>
</div>
<div className="flex items-center space-x-2">
<span className="text-sm text-slate-600 dark:text-slate-400">
<LuHardDrive
strokeWidth={3}
className={`mr-1 inline ${
atxState?.hdd ? "text-blue-400" : "text-slate-300"
}`}
/>
HDD LED
</span>
</div>
</div>
</div>
</Card>
)}
</div>
);
}