mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-01 11:47:46 +02:00
Added Wifi signal strength to printer card
This commit is contained in:
@@ -241,13 +241,20 @@ class BambuFTPClient:
|
||||
# Try AVBL command (available space) - some FTP servers support this
|
||||
try:
|
||||
response = self._ftp.sendcmd("AVBL")
|
||||
logger.debug(f"AVBL response: {response}")
|
||||
# Response format: "213 <bytes available>"
|
||||
if response.startswith("213"):
|
||||
parts = response.split()
|
||||
if len(parts) >= 2:
|
||||
result["free_bytes"] = int(parts[1])
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug(f"AVBL command not supported: {e}")
|
||||
# Try STAT command as fallback
|
||||
try:
|
||||
response = self._ftp.sendcmd("STAT")
|
||||
logger.debug(f"STAT response: {response}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Calculate used space by listing root directories
|
||||
try:
|
||||
|
||||
@@ -347,6 +347,17 @@ class BambuMQTTClient:
|
||||
logger.info(f"[{self.serial_number}] Received system data: {system_data}")
|
||||
self._handle_system_response(system_data)
|
||||
|
||||
# Parse WiFi signal at top level (some printers send it here)
|
||||
if "wifi_signal" in payload:
|
||||
wifi_signal = payload["wifi_signal"]
|
||||
if isinstance(wifi_signal, (int, float)):
|
||||
self.state.wifi_signal = int(wifi_signal)
|
||||
elif isinstance(wifi_signal, str):
|
||||
try:
|
||||
self.state.wifi_signal = int(wifi_signal.replace("dBm", "").strip())
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if "print" in payload:
|
||||
print_data = payload["print"]
|
||||
|
||||
@@ -1220,8 +1231,8 @@ class BambuMQTTClient:
|
||||
# Parse WiFi signal strength (dBm)
|
||||
if "wifi_signal" in data:
|
||||
wifi_signal = data["wifi_signal"]
|
||||
logger.info(f"[{self.serial_number}] wifi_signal received: {wifi_signal}")
|
||||
if isinstance(wifi_signal, (int, float)):
|
||||
# Convert string dBm to int (e.g., "-52dBm" -> -52)
|
||||
self.state.wifi_signal = int(wifi_signal)
|
||||
elif isinstance(wifi_signal, str):
|
||||
# Handle string format like "-52dBm"
|
||||
|
||||
@@ -287,6 +287,8 @@ def printer_state_to_dict(state: PrinterState, printer_id: int | None = None) ->
|
||||
"ams_status_main": state.ams_status_main,
|
||||
"ams_status_sub": state.ams_status_sub,
|
||||
"tray_now": state.tray_now,
|
||||
# WiFi signal strength
|
||||
"wifi_signal": state.wifi_signal,
|
||||
}
|
||||
# Add cover URL if there's an active print and printer_id is provided
|
||||
if printer_id and state.state == "RUNNING" and state.gcode_file:
|
||||
|
||||
@@ -155,9 +155,17 @@ export function FileManagerModal({ printerId, printerName, onClose }: FileManage
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Storage info */}
|
||||
{storageData?.used_bytes != null && storageData.used_bytes > 0 && (
|
||||
<div className="text-sm text-bambu-gray">
|
||||
Used: {formatStorageSize(storageData.used_bytes)}
|
||||
{storageData && (storageData.used_bytes != null || storageData.free_bytes != null) && (
|
||||
<div className="text-sm text-bambu-gray flex items-center gap-2">
|
||||
{storageData.used_bytes != null && (
|
||||
<span>Used: {formatStorageSize(storageData.used_bytes)}</span>
|
||||
)}
|
||||
{storageData.used_bytes != null && storageData.free_bytes != null && (
|
||||
<span className="text-bambu-dark-tertiary">|</span>
|
||||
)}
|
||||
{storageData.free_bytes != null && (
|
||||
<span>Free: {formatStorageSize(storageData.free_bytes)}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
|
||||
@@ -75,10 +75,17 @@ export function useWebSocket() {
|
||||
if (message.printer_id !== undefined) {
|
||||
queryClient.setQueryData(
|
||||
['printerStatus', message.printer_id],
|
||||
(old: Record<string, unknown> | undefined) => ({
|
||||
...old,
|
||||
...message.data,
|
||||
})
|
||||
(old: Record<string, unknown> | undefined) => {
|
||||
const merged = {
|
||||
...old,
|
||||
...message.data,
|
||||
};
|
||||
// Preserve last known wifi_signal if new value is null
|
||||
if (merged.wifi_signal == null && old?.wifi_signal != null) {
|
||||
merged.wifi_signal = old.wifi_signal;
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Plus,
|
||||
Wifi,
|
||||
WifiOff,
|
||||
Signal,
|
||||
Thermometer,
|
||||
Clock,
|
||||
MoreVertical,
|
||||
@@ -37,6 +38,15 @@ function formatTime(seconds: number): string {
|
||||
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
|
||||
}
|
||||
|
||||
function getWifiStrength(rssi: number | null | undefined): { label: string; color: string; bars: number } {
|
||||
if (rssi == null) return { label: '', color: 'text-bambu-gray', bars: 0 };
|
||||
if (rssi >= -50) return { label: 'Excellent', color: 'text-bambu-green', bars: 4 };
|
||||
if (rssi >= -60) return { label: 'Good', color: 'text-bambu-green', bars: 3 };
|
||||
if (rssi >= -70) return { label: 'Fair', color: 'text-yellow-400', bars: 2 };
|
||||
if (rssi >= -80) return { label: 'Weak', color: 'text-orange-400', bars: 1 };
|
||||
return { label: 'Very weak', color: 'text-red-400', bars: 1 };
|
||||
}
|
||||
|
||||
function CoverImage({ url, printName }: { url: string | null; printName?: string }) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
@@ -118,6 +128,15 @@ function PrinterCard({
|
||||
refetchInterval: 30000, // Fallback polling, WebSocket handles real-time
|
||||
});
|
||||
|
||||
// Cache WiFi signal to prevent it disappearing on updates
|
||||
const [cachedWifiSignal, setCachedWifiSignal] = useState<number | null>(null);
|
||||
useEffect(() => {
|
||||
if (status?.wifi_signal != null) {
|
||||
setCachedWifiSignal(status.wifi_signal);
|
||||
}
|
||||
}, [status?.wifi_signal]);
|
||||
const wifiSignal = status?.wifi_signal ?? cachedWifiSignal;
|
||||
|
||||
// Fetch smart plug for this printer
|
||||
const { data: smartPlug } = useQuery({
|
||||
queryKey: ['smartPlugByPrinter', printer.id],
|
||||
@@ -190,6 +209,7 @@ function PrinterCard({
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Connection status badge */}
|
||||
<span
|
||||
className={`flex items-center gap-1.5 px-2 py-1 rounded-full text-xs ${
|
||||
status?.connected
|
||||
@@ -204,6 +224,26 @@ function PrinterCard({
|
||||
)}
|
||||
{status?.connected ? 'Connected' : 'Offline'}
|
||||
</span>
|
||||
{/* WiFi signal strength indicator */}
|
||||
{status?.connected && wifiSignal != null && (
|
||||
<span
|
||||
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs ${
|
||||
wifiSignal >= -50
|
||||
? 'bg-bambu-green/20 text-bambu-green'
|
||||
: wifiSignal >= -60
|
||||
? 'bg-bambu-green/20 text-bambu-green'
|
||||
: wifiSignal >= -70
|
||||
? 'bg-amber-500/20 text-amber-600'
|
||||
: wifiSignal >= -80
|
||||
? 'bg-orange-500/20 text-orange-600'
|
||||
: 'bg-red-500/20 text-red-600'
|
||||
}`}
|
||||
title={`WiFi: ${wifiSignal} dBm - ${getWifiStrength(wifiSignal).label}`}
|
||||
>
|
||||
<Signal className="w-3 h-3" />
|
||||
{wifiSignal}dBm
|
||||
</span>
|
||||
)}
|
||||
{/* HMS Status Indicator */}
|
||||
{status?.connected && (
|
||||
<button
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
@@ -7,8 +7,8 @@
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png" />
|
||||
<script type="module" crossorigin src="/assets/index-BFqTr5zP.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-B01KJJLK.css">
|
||||
<script type="module" crossorigin src="/assets/index-DeHI9Fkw.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BCXYYo2S.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Reference in New Issue
Block a user