multiple updates
- colour theme implemented. ThemeService based global switching for future light detection triggers - test flipflop service for various fun - navigator widget class with state switching
This commit is contained in:
@@ -56,11 +56,22 @@ def set_cross_compile_env():
|
||||
return env_vars
|
||||
|
||||
|
||||
def generate_theme() -> bool:
|
||||
"""Generate theme colors before build."""
|
||||
import generate_theme as theme_gen
|
||||
return theme_gen.main()
|
||||
|
||||
|
||||
def build(clean: bool = False) -> bool:
|
||||
"""Run the build process. Returns True on success."""
|
||||
print("=== Smart Serow Build ===")
|
||||
print(f"Project: {UI_DIR}")
|
||||
|
||||
# Generate theme colors first
|
||||
if not generate_theme():
|
||||
print("ERROR: Theme generation failed")
|
||||
return False
|
||||
|
||||
# Check flutter-elinux
|
||||
flutter_path = check_flutter_elinux()
|
||||
print(f"Using: {flutter_path}")
|
||||
|
||||
128
scripts/generate_theme.py
Normal file
128
scripts/generate_theme.py
Normal file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Theme generator for Smart Serow Flutter UI.
|
||||
|
||||
Reads navigator from config, loads corresponding theme JSON,
|
||||
and generates app_colors.dart with the colour palette.
|
||||
|
||||
Fallback chain: {navigator}.json -> default.json -> hardcoded defaults
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SCRIPT_DIR = Path(__file__).parent.resolve()
|
||||
PROJECT_ROOT = SCRIPT_DIR.parent
|
||||
UI_DIR = PROJECT_ROOT / "pi" / "ui"
|
||||
THEMES_DIR = PROJECT_ROOT / "extra" / "themes"
|
||||
CONFIG_FILE = UI_DIR / "config.json"
|
||||
OUTPUT_FILE = UI_DIR / "lib" / "theme" / "app_colors.dart"
|
||||
|
||||
# Hardcoded fallback if no theme files exist at all
|
||||
HARDCODED_THEME = {
|
||||
"dark": {
|
||||
"background": "#000000",
|
||||
"foreground": "#FFFFFF",
|
||||
"highlight": "#FF5555",
|
||||
"subdued": "#808080",
|
||||
},
|
||||
"bright": {
|
||||
"background": "#F0F0F0",
|
||||
"foreground": "#1A1A1A",
|
||||
"highlight": "#CC0000",
|
||||
"subdued": "#606060",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def hex_to_flutter(hex_color: str) -> str:
|
||||
"""Convert #RRGGBB to 0xFFRRGGBB format."""
|
||||
hex_clean = hex_color.lstrip("#").upper()
|
||||
return f"0xFF{hex_clean}"
|
||||
|
||||
|
||||
def load_config() -> dict:
|
||||
"""Load UI config, return empty dict if missing."""
|
||||
if CONFIG_FILE.exists():
|
||||
with open(CONFIG_FILE) as f:
|
||||
return json.load(f)
|
||||
return {}
|
||||
|
||||
|
||||
def load_theme(navigator: str) -> dict:
|
||||
"""Load theme for navigator with fallback chain."""
|
||||
# Try navigator-specific theme
|
||||
nav_theme = THEMES_DIR / f"{navigator}.json"
|
||||
if nav_theme.exists():
|
||||
print(f" Using theme: {nav_theme.name}")
|
||||
with open(nav_theme) as f:
|
||||
return json.load(f)
|
||||
|
||||
# Try default theme
|
||||
default_theme = THEMES_DIR / "default.json"
|
||||
if default_theme.exists():
|
||||
print(f" Fallback to: default.json")
|
||||
with open(default_theme) as f:
|
||||
return json.load(f)
|
||||
|
||||
# Last resort: hardcoded
|
||||
print(f" Using hardcoded defaults")
|
||||
return HARDCODED_THEME
|
||||
|
||||
|
||||
def generate_dart(theme: dict) -> str:
|
||||
"""Generate Dart source from theme dict."""
|
||||
dark = theme["dark"]
|
||||
bright = theme["bright"]
|
||||
|
||||
return f'''import 'package:flutter/material.dart';
|
||||
|
||||
/// Auto-generated from theme config. Do not edit manually.
|
||||
/// Run scripts/generate_theme.py to regenerate.
|
||||
class AppColors {{
|
||||
AppColors._();
|
||||
|
||||
// Dark theme (low ambient light)
|
||||
static const darkBackground = Color({hex_to_flutter(dark["background"])});
|
||||
static const darkForeground = Color({hex_to_flutter(dark["foreground"])});
|
||||
static const darkHighlight = Color({hex_to_flutter(dark["highlight"])});
|
||||
static const darkSubdued = Color({hex_to_flutter(dark["subdued"])});
|
||||
|
||||
// Bright theme (high ambient light)
|
||||
static const brightBackground = Color({hex_to_flutter(bright["background"])});
|
||||
static const brightForeground = Color({hex_to_flutter(bright["foreground"])});
|
||||
static const brightHighlight = Color({hex_to_flutter(bright["highlight"])});
|
||||
static const brightSubdued = Color({hex_to_flutter(bright["subdued"])});
|
||||
}}
|
||||
'''
|
||||
|
||||
|
||||
def main():
|
||||
print("=== Theme Generation ===")
|
||||
|
||||
# Get navigator from config
|
||||
config = load_config()
|
||||
navigator = config.get("navigator", "default")
|
||||
print(f" Navigator: {navigator}")
|
||||
|
||||
# Load theme
|
||||
theme = load_theme(navigator)
|
||||
|
||||
# Generate Dart
|
||||
dart_source = generate_dart(theme)
|
||||
|
||||
# Ensure output directory exists
|
||||
OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write output
|
||||
with open(OUTPUT_FILE, "w") as f:
|
||||
f.write(dart_source)
|
||||
|
||||
print(f" Generated: {OUTPUT_FILE.relative_to(PROJECT_ROOT)}")
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
@@ -27,6 +27,12 @@ echo "Creating app directory: $APP_DIR"
|
||||
sudo mkdir -p "$APP_DIR/bundle"
|
||||
sudo chown -R "$PI_USER:$PI_USER" "$APP_DIR"
|
||||
|
||||
# Create assets directory in user home (for navigator images, etc.)
|
||||
ASSETS_DIR="/home/$PI_USER/smartserow-ui/assets"
|
||||
echo "Creating assets directory: $ASSETS_DIR"
|
||||
mkdir -p "$ASSETS_DIR/navigator"
|
||||
echo " (deploy.py will sync extra/images here)"
|
||||
|
||||
# Install runtime dependencies for flutter-elinux
|
||||
echo "Installing runtime dependencies..."
|
||||
sudo apt-get update
|
||||
|
||||
Reference in New Issue
Block a user