A dashboard application that displays battery, network, display, and thermal information using Nib's system services. Demonstrates on_appear for automatic data refresh, card-style layouts, and conditional styling.
Full Source¶
import nib
from nib.services.battery import ThermalState
def main(app: nib.App):
app.title = "System Info"
app.icon = nib.SFSymbol("info.circle.fill")
app.width = 360
app.height = 520
# Theme colors
card_bg = "#1C1C1E"
text_secondary = "#8E8E93"
green = "#30D158"
blue = "#0A84FF"
orange = "#FF9F0A"
red = "#FF453A"
# ── Helpers ──────────────────────────────────────────────
def make_card(title, *controls):
return nib.VStack(
controls=[
nib.Text(title, font=nib.Font.CAPTION, foreground_color=text_secondary),
*controls,
],
alignment=nib.HorizontalAlignment.LEADING,
spacing=8,
padding=12,
background=nib.Rectangle(corner_radius=10, fill=card_bg, opacity=0.6),
)
def make_row(icon, label, value_view, icon_color=None):
return nib.HStack(
controls=[
nib.SFSymbol(icon, font=nib.Font.system(14),
foreground_color=icon_color or text_secondary),
nib.Text(label, foreground_color=text_secondary),
nib.Spacer(),
value_view,
],
spacing=8,
)
# ── Battery ──────────────────────────────────────────────
battery_level = nib.Text("--", font=nib.Font.system(24, weight=nib.FontWeight.BOLD))
battery_state = nib.Text("--", font=nib.Font.CAPTION, foreground_color=text_secondary)
battery_time = nib.Text("", font=nib.Font.CAPTION, foreground_color=text_secondary)
def refresh_battery():
try:
info = app.battery.get_status()
if info.has_battery and info.level is not None:
battery_level.content = f"{info.level:.0f}%"
if info.level >= 50:
battery_level.foreground_color = green
elif info.level >= 20:
battery_level.foreground_color = orange
else:
battery_level.foreground_color = red
battery_state.content = info.state.value.replace("_", " ").title()
if info.is_charging and info.time_to_full_formatted:
battery_time.content = f"Full in {info.time_to_full_formatted}"
elif info.time_remaining_formatted:
battery_time.content = f"{info.time_remaining_formatted} remaining"
else:
battery_time.content = ""
else:
battery_level.content = "AC"
battery_level.foreground_color = blue
battery_state.content = "No Battery"
except Exception:
battery_level.content = "N/A"
battery_card = make_card(
"BATTERY",
nib.HStack(
controls=[
nib.VStack(
controls=[battery_level, battery_state],
alignment=nib.HorizontalAlignment.LEADING, spacing=2,
),
nib.Spacer(),
battery_time,
],
),
)
# ── Battery Health ───────────────────────────────────────
health_pct = nib.Text("--", font=nib.Font.system(18, weight=nib.FontWeight.SEMIBOLD))
health_cycles = nib.Text("--", foreground_color=text_secondary)
health_temp = nib.Text("--", foreground_color=text_secondary)
health_cond = nib.Text("--", foreground_color=text_secondary)
def refresh_health():
try:
h = app.battery.get_health()
if h.health_percent is not None:
health_pct.content = f"{h.health_percent:.0f}%"
health_pct.foreground_color = green if h.health_percent >= 80 else orange
health_cycles.content = f"{h.cycle_count} cycles" if h.cycle_count else "N/A"
health_temp.content = f"{h.temperature_celsius:.1f} C" if h.temperature_celsius else "N/A"
health_cond.content = h.condition or "N/A"
except Exception:
health_pct.content = "N/A"
health_card = make_card(
"BATTERY HEALTH",
make_row("heart.fill", "Health", health_pct, green),
make_row("arrow.triangle.2.circlepath", "Cycles", health_cycles),
make_row("thermometer", "Temp", health_temp),
make_row("checkmark.shield", "Condition", health_cond),
)
# ── Thermal ──────────────────────────────────────────────
thermal_text = nib.Text("--", font=nib.Font.system(16, weight=nib.FontWeight.SEMIBOLD))
def refresh_thermal():
try:
t = app.battery.get_thermal_state()
thermal_text.content = t.state.value.title()
if t.state == ThermalState.NOMINAL:
thermal_text.foreground_color = green
elif t.state == ThermalState.FAIR:
thermal_text.foreground_color = orange
else:
thermal_text.foreground_color = red
except Exception:
thermal_text.content = "N/A"
thermal_card = make_card("THERMAL", thermal_text)
# ── Display ──────────────────────────────────────────────
display_name = nib.Text("--", font=nib.Font.system(14, weight=nib.FontWeight.SEMIBOLD))
display_res = nib.Text("--", foreground_color=text_secondary)
display_bright = nib.Text("--", foreground_color=text_secondary)
display_refresh = nib.Text("--", foreground_color=text_secondary)
def refresh_display():
try:
info = app.screen.get_info()
display_name.content = info.name or "Display"
display_res.content = f"{info.width:.0f} x {info.height:.0f} @{info.scale:.0f}x"
display_bright.content = (
f"{info.brightness * 100:.0f}%" if info.brightness is not None else "N/A"
)
display_refresh.content = (
f"{info.refresh_rate:.0f} Hz" if info.refresh_rate else "N/A"
)
except Exception:
display_name.content = "N/A"
display_card = make_card(
"DISPLAY",
display_name,
make_row("rectangle.on.rectangle", "Resolution", display_res),
make_row("sun.max", "Brightness", display_bright, orange),
make_row("speedometer", "Refresh", display_refresh),
)
# ── Network ──────────────────────────────────────────────
net_status = nib.Text("--", font=nib.Font.system(16, weight=nib.FontWeight.SEMIBOLD))
net_type = nib.Text("--", foreground_color=text_secondary)
net_ssid = nib.Text("", foreground_color=text_secondary)
def refresh_network():
try:
info = app.connectivity.get_status()
if info.is_connected:
net_status.content = "Connected"
net_status.foreground_color = green
net_type.content = info.type.value.title()
net_ssid.content = info.ssid or ""
else:
net_status.content = "Offline"
net_status.foreground_color = red
net_type.content = "No Connection"
net_ssid.content = ""
except Exception:
net_status.content = "N/A"
network_card = make_card(
"NETWORK",
nib.HStack(controls=[net_status, nib.Spacer(), net_type], spacing=8),
net_ssid,
)
# ── Refresh All ──────────────────────────────────────────
def refresh_all():
refresh_battery()
refresh_health()
refresh_thermal()
refresh_display()
refresh_network()
app.on_appear = refresh_all
# ── Build ────────────────────────────────────────────────
app.build(
nib.ScrollView(
controls=[
nib.VStack(
controls=[
battery_card,
health_card,
thermal_card,
display_card,
network_card,
nib.Button(
"Refresh All",
action=refresh_all,
style=nib.ButtonStyle.BORDERED,
),
],
spacing=10,
padding=12,
),
],
)
)
nib.run(main)
Walkthrough¶
Using system services¶
Each card fetches data from a different system service:
| Card | Service | Method |
|---|---|---|
| Battery | app.battery |
get_status() |
| Battery Health | app.battery |
get_health() |
| Thermal | app.battery |
get_thermal_state() |
| Display | app.screen |
get_info() |
| Network | app.connectivity |
get_status() |
Services are synchronous -- call the method and use the result immediately:
info = app.battery.get_status()
battery_level.content = f"{info.level:.0f}%"
Refreshing on popover open¶
app.on_appear = refresh_all
The on_appear callback fires every time the popover opens. This ensures data is fresh each time the user clicks the menu bar icon, without polling in the background.
Card layout pattern¶
The make_card helper creates a consistent card style:
def make_card(title, *controls):
return nib.VStack(
controls=[
nib.Text(title, font=nib.Font.CAPTION, foreground_color=text_secondary),
*controls,
],
alignment=nib.HorizontalAlignment.LEADING,
spacing=8,
padding=12,
background=nib.Rectangle(corner_radius=10, fill=card_bg, opacity=0.6),
)
The background modifier accepts a shape view (Rectangle, Circle, etc.) to draw behind the content. Using corner_radius and a dark fill creates the card appearance.
Conditional styling¶
The battery level color changes based on the value:
if info.level >= 50:
battery_level.foreground_color = green
elif info.level >= 20:
battery_level.foreground_color = orange
else:
battery_level.foreground_color = red
Assigning foreground_color triggers a UI patch, so the color updates immediately without rebuilding the entire view tree.
Error handling¶
Each refresh function wraps the service call in a try/except block:
try:
info = app.battery.get_status()
# ... update views
except Exception:
battery_level.content = "N/A"
This prevents a service timeout or unavailable hardware (e.g., no battery on a desktop Mac) from crashing the app.
Running¶
nib run system_monitor.py