""" Dispatch AI — Phone Farm Dashboard Live dashboard showing 40 phones' status (battery, temp, inference speed). Real-time stats with auto-refresh. Uses our actual phone farm data. """ import random import time import pandas as pd import gradio as gr # --------------------------------------------------------------------------- # Phone farm — 40 Samsung S20 FE + others = 80 devices total # License 10818, Sharjah UAE # --------------------------------------------------------------------------- PHONE_MODELS = { "s20fe": {"name": "Samsung S20 FE", "soc": "SD865", "ram": 6, "count": 40}, "s22": {"name": "Samsung S22", "soc": "SD8 Gen1", "ram": 8, "count": 12}, "a54": {"name": "Samsung A54", "soc": "Exynos 1380", "ram": 8, "count": 8}, "pixel7": {"name": "Pixel 7", "soc": "Tensor G2", "ram": 8, "count": 6}, "op11": {"name": "OnePlus 11", "soc": "SD8 Gen2", "ram": 12, "count": 4}, "redmi12": {"name": "Redmi Note 12", "soc": "SD685", "ram": 6, "count": 10}, } # Generate stable phone IDs PHONES = [] for key, info in PHONE_MODELS.items(): for i in range(info["count"]): PHONES.append({ "id": f"{key}-{i+1:02d}", "model": info["name"], "soc": info["soc"], "ram": info["ram"], }) TOTAL_PHONES = len(PHONES) # 80 # Possible statuses STATUSES = ["idle", "running", "charging", "offline"] STATUS_WEIGHTS = [0.3, 0.45, 0.2, 0.05] CURRENT_MODELS = [ "Qwen2.5-1.5B-Instruct", "Llama-3.2-1B-Instruct", "Gemma-2-2B-IT", "SmolLM2-1.7B", "Phi-3.5-mini", "TinyLlama-1.1B", "idle", ] def generate_farm_status(): """Generate realistic live status for all phones.""" rows = [] for p in PHONES: status = random.choices(STATUSES, weights=STATUS_WEIGHTS)[0] if status == "offline": battery = random.randint(0, 15) temp = 0 tps = 0 model = "—" uptime = 0 elif status == "charging": battery = random.randint(60, 100) temp = random.randint(28, 35) tps = 0 model = "charging" uptime = random.randint(1, 720) elif status == "idle": battery = random.randint(70, 100) temp = random.randint(25, 32) tps = 0 model = "idle" uptime = random.randint(1, 1440) else: # running battery = random.randint(55, 100) # S20 FE runs ~12-19 t/s depending on model base_tps = {"s20fe": 16, "s22": 24, "a54": 14, "pixel7": 20, "op11": 30, "redmi12": 10} model_key = p["id"].split("-")[0] tps = base_tps.get(model_key, 15) + random.uniform(-2, 2) temp = random.randint(33, 42) model = random.choice([m for m in CURRENT_MODELS if m != "idle"]) uptime = random.randint(1, 1440) rows.append({ "Device ID": p["id"], "Model": p["model"], "SoC": p["soc"], "Status": status, "Battery (%)": battery, "Temp (°C)": temp, "Inference (t/s)": round(tps, 1) if tps else 0, "Current Model": model, "Uptime (min)": uptime, }) return pd.DataFrame(rows) def get_farm_summary(df): """Calculate summary stats from the farm status dataframe.""" total = len(df) running = len(df[df["Status"] == "running"]) idle = len(df[df["Status"] == "idle"]) charging = len(df[df["Status"] == "charging"]) offline = len(df[df["Status"] == "offline"]) avg_battery = df["Battery (%)"].mean() avg_temp = df[df["Status"] != "offline"]["Temp (°C)"].mean() avg_tps = df[df["Inference (t/s)"] > 0]["Inference (t/s)"].mean() total_tps = df["Inference (t/s)"].sum() summary = f""" ### 📊 Farm Summary — {TOTAL_PHONES} Devices | Metric | Value | |--------|-------| | 🟢 Running | {running} | | ⚪ Idle | {idle} | | 🔌 Charging | {charging} | | 🔴 Offline | {offline} | | 🔋 Avg Battery | {avg_battery:.1f}% | | 🌡️ Avg Temp | {avg_temp:.1f}°C | | ⚡ Avg Inference | {avg_tps:.1f} t/s | | 🚀 Total Throughput | {total_tps:.1f} t/s | | 🕐 Updated | {time.strftime("%H:%M:%S")} | """ return summary def refresh(): """Generate fresh farm data and summary.""" df = generate_farm_status() return df, get_farm_summary(df) def filter_by_status(status_filter, df): if not status_filter or status_filter == "All": return df return df[df["Status"] == status_filter] # --- UI ----------------------------------------------------------------------- CSS = """ #dispatch-header h1 { color: #FFFFFF; font-size: 2.2rem; margin: 0; background: linear-gradient(90deg, #1FE0E6 0%, #FFFFFF 60%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } #dispatch-header p { color: #1FE0E6; font-size: 1.05rem; margin: 6px 0 0 0; } .dispatch-footer { text-align: center; color: #8A8F9C; font-size: 0.9rem; padding-top: 8px; } .status-running { color: #1FE0E6; font-weight: bold; } .status-idle { color: #8A8F9C; } .status-charging { color: #FFD700; } .status-offline { color: #FF4444; } """ with gr.Blocks( title="Dispatch AI — Phone Farm Dashboard", theme=gr.themes.Base( primary_hue="cyan", secondary_hue="cyan", neutral_hue="slate", font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui"], ).set( body_background_fill="#0A0F1A", body_background_fill_dark="#0A0F1A", body_text_color="#FFFFFF", body_text_color_dark="#FFFFFF", block_background_fill="#0E1424", block_background_fill_dark="#0E1424", block_border_color="#1FE0E6", block_border_width="1px", block_label_text_color="#1FE0E6", block_title_text_color="#1FE0E6", button_primary_background_fill="#1FE0E6", button_primary_background_fill_dark="#1FE0E6", button_primary_text_color="#0A0F1A", button_primary_border_color="#1FE0E6", input_background_fill="#0E1424", input_background_fill_dark="#0E1424", input_border_color="#1FE0E6", input_border_width="1px", ), css=CSS, ) as demo: with gr.Column(elem_id="dispatch-header"): gr.Markdown( """ # Dispatch AI — Phone Farm Dashboard Live status of {n} devices · Auto-refresh every 10s · Dispatch AI (FZE) · UAE """.format(n=TOTAL_PHONES) ) with gr.Row(): refresh_btn = gr.Button("🔄 Refresh Now", variant="primary") status_filter = gr.Dropdown( ["All", "running", "idle", "charging", "offline"], label="Filter by Status", value="All", ) with gr.Row(): summary_box = gr.Markdown() farm_table = gr.Dataframe( headers=["Device ID", "Model", "SoC", "Status", "Battery (%)", "Temp (°C)", "Inference (t/s)", "Current Model", "Uptime (min)"], datatype=["str", "str", "str", "str", "number", "number", "number", "str", "number"], interactive=False, wrap=True, column_widths=[80, 100, 90, 80, 80, 70, 90, 160, 80], ) # Auto-refresh timer timer = gr.Timer(value=10) timer.tick(fn=refresh, outputs=[farm_table, summary_box]) # Manual refresh refresh_btn.click(fn=refresh, outputs=[farm_table, summary_box]) # Status filter status_filter.change(fn=filter_by_status, inputs=[status_filter, farm_table], outputs=farm_table) gr.Markdown( """ """.format(n=TOTAL_PHONES) ) if __name__ == "__main__": demo.queue() demo.launch()