badger-app-creator
Create MicroPython applications for Universe 2025 (Tufty) Badge including display graphics, button handling, and MonaOS app structure. Use when building badge apps, creating interactive displays, or developing MicroPython programs.
What this skill does
# Universe 2025 Badge App Creator
Create well-structured MicroPython applications for the **Universe 2025 (Tufty) Badge** with MonaOS integration, display graphics, button handling, and proper app architecture.
## Important: MonaOS App Structure
**Critical**: MonaOS apps follow a specific structure. Each app is a directory in `/system/apps/` containing:
```
/system/apps/my_app/
├── icon.png # 24x24 PNG icon
├── __init__.py # Entry point with update() function
└── assets/ # Optional: app assets (auto-added to path)
└── ...
```
### Required Functions
Your `__init__.py` must implement:
**`update()`** - Required, called every frame by MonaOS:
```python
def update():
# Called every frame
# Draw your UI, handle input, update state
pass
```
**`init()`** - Optional, called once when app launches:
```python
def init():
# Initialize app state, load resources
pass
```
**`on_exit()`** - Optional, called when HOME button pressed:
```python
def on_exit():
# Save state, cleanup resources
pass
```
## MonaOS App Template
```python
# __init__.py - MonaOS app template
from badgeware import screen, brushes, shapes, io, PixelFont, Image
# App state
app_state = {
"counter": 0,
"color": (255, 255, 255)
}
def init():
"""Called once when app launches"""
# Load font
screen.font = PixelFont.load("nope.ppf")
# Load saved state if exists
try:
with open("/storage/myapp_state.txt", "r") as f:
app_state["counter"] = int(f.read())
except:
pass
print("App initialized!")
def update():
"""Called every frame by MonaOS"""
# Clear screen
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# Draw UI
screen.brush = brushes.color(255, 255, 255)
screen.text("My App", 10, 10)
screen.text(f"Count: {app_state['counter']}", 10, 30)
# Handle buttons (checked every frame)
if io.BUTTON_A in io.pressed:
app_state["counter"] += 1
if io.BUTTON_B in io.pressed:
app_state["counter"] = 0
# HOME button exits automatically
def on_exit():
"""Called when returning to MonaOS menu"""
# Save state
with open("/storage/myapp_state.txt", "w") as f:
f.write(str(app_state["counter"]))
print("App exiting!")
```
## Display API (badgeware)
### Import Modules
```python
from badgeware import screen, brushes, shapes, Image, PixelFont, Matrix, io
```
### Screen Drawing (160x120 framebuffer)
The screen is a 160×120 RGB framebuffer that MonaOS automatically pixel-doubles to 320×240.
**Basic Drawing**:
```python
# Set brush color (RGB 0-255)
screen.brush = brushes.color(r, g, b)
# Clear screen
screen.clear()
# Draw text
screen.text("Hello", x, y)
# Draw shapes
screen.draw(shapes.rectangle(x, y, width, height))
screen.draw(shapes.circle(x, y, radius))
screen.draw(shapes.line(x1, y1, x2, y2))
screen.draw(shapes.arc(x, y, radius, start_angle, end_angle))
screen.draw(shapes.pie(x, y, radius, start_angle, end_angle))
```
**Antialiasing** (smooth edges):
```python
screen.antialias = Image.X4 # Enable 4x antialiasing
screen.antialias = Image.NONE # Disable
```
**No Manual Update Needed**: MonaOS automatically updates the display after each `update()` call.
### Shapes Module
Full documentation: https://github.com/badger/home/blob/main/badgerware/shapes.md
**Available Shapes**:
```python
from badgeware import shapes
# Rectangle
shapes.rectangle(x, y, width, height)
# Circle
shapes.circle(x, y, radius)
# Line
shapes.line(x1, y1, x2, y2)
# Arc (portion of circle outline)
shapes.arc(x, y, radius, start_angle, end_angle)
# Pie (filled circle segment)
shapes.pie(x, y, radius, start_angle, end_angle)
# Rounded rectangle
shapes.rounded_rectangle(x, y, width, height, radius)
# Regular polygon (pentagon, hexagon, etc.)
shapes.regular_polygon(x, y, sides, radius)
# Squircle (smooth rectangle-circle hybrid)
shapes.squircle(x, y, width, height)
```
**Transformations**:
```python
from badgeware import Matrix
# Create shape
rect = shapes.rectangle(-1, -1, 2, 2)
# Apply transformation
rect.transform = Matrix() \
.translate(80, 60) \ # Move to center
.scale(20, 20) \ # Scale up
.rotate(io.ticks / 100) # Animated rotation
screen.draw(rect)
```
### Brushes Module
Full documentation: https://github.com/badger/home/blob/main/badgerware/brushes.md
**Solid Colors**:
```python
from badgeware import brushes
# RGB color (0-255 per channel)
screen.brush = brushes.color(r, g, b)
# Examples
screen.brush = brushes.color(255, 0, 0) # Red
screen.brush = brushes.color(0, 255, 0) # Green
screen.brush = brushes.color(0, 0, 255) # Blue
screen.brush = brushes.color(255, 255, 255) # White
screen.brush = brushes.color(0, 0, 0) # Black
```
### Fonts
Full documentation: https://github.com/badger/home/blob/main/PixelFont.md
**30 Licensed Pixel Fonts Included**:
```python
from badgeware import PixelFont
# Load font
screen.font = PixelFont.load("nope.ppf")
# Draw text with loaded font
screen.text("Styled text", x, y)
# Measure text width
width = screen.font.measure("text to measure")
# Reset to default font
screen.font = None
```
### Images & Sprites
Full documentation: https://github.com/badger/home/blob/main/badgerware/Image.md
**Loading Images**:
```python
from badgeware import Image
# Load PNG image
img = Image.load("sprite.png")
# Blit to screen
screen.blit(img, x, y)
# Scaled blit
screen.scale_blit(img, x, y, width, height)
```
**Sprite Sheets**:
```python
# Using SpriteSheet helper (from examples)
from lib import SpriteSheet
# Load sprite sheet (7 columns, 1 row)
sprites = SpriteSheet("assets/mona-sprites.png", 7, 1)
# Blit specific sprite (column 0, row 0)
screen.blit(sprites.sprite(0, 0), x, y)
# Scaled sprite
screen.scale_blit(sprites.sprite(3, 0), x, y, 30, 30)
```
## Button Handling (io module)
Full documentation: https://github.com/badger/home/blob/main/badgerware/io.md
### Button Constants
```python
from badgeware import io
# Available buttons
io.BUTTON_A # Left button
io.BUTTON_B # Middle button
io.BUTTON_C # Right button
io.BUTTON_UP # Up button
io.BUTTON_DOWN # Down button
io.BUTTON_HOME # HOME button (exits to MonaOS)
```
### Button States
Check button states within your `update()` function:
```python
def update():
# Button just pressed this frame
if io.BUTTON_A in io.pressed:
print("A was just pressed")
# Button just released this frame
if io.BUTTON_B in io.released:
print("B was just released")
# Button currently held down
if io.BUTTON_C in io.held:
print("C is being held")
# Button state changed this frame (pressed or released)
if io.BUTTON_UP in io.changed:
print("UP state changed")
```
**No Debouncing Needed**: The io module handles button debouncing automatically.
### Menu Navigation Example
```python
menu_items = ["Option 1", "Option 2", "Option 3", "Option 4"]
selected = 0
def update():
global selected
# Clear screen
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# Draw title
screen.brush = brushes.color(255, 255, 255)
screen.text("Menu", 10, 5)
# Draw menu items
y = 30
for i, item in enumerate(menu_items):
if i == selected:
# Highlight selected item
screen.brush = brushes.color(255, 255, 0)
screen.text("> " + item, 10, y)
else:
screen.brush = brushes.color(200, 200, 200)
screen.text(" " + item, 10, y)
y += 20
# Handle navigation
if io.BUTTON_UP in io.pressed:
selected = (selected - 1) % len(menu_items)
if io.BUTTON_DOWN in io.pressed:
selected = (selected + 1) % len(menu_items)
if io.BUTTON_A in io.pressed:
print(f"Selected: {menu_items[selected]}")
```
## Animation & Timing
### Using io.ticks
```python
from badgeware import io
import math
def upRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.