Added Wifi signal strength to printer card

This commit is contained in:
maziggy
2025-12-07 11:30:19 +00:00
parent 0c452a12e9
commit cef6cd593b
10 changed files with 469 additions and 394 deletions
+9 -2
View File
@@ -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:
+12 -1
View File
@@ -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"
+2
View File
@@ -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:
+11 -3
View 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
+11 -4
View File
@@ -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;
+40
View File
@@ -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
View File
@@ -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>