Pixoo Page Engine - Build Beautiful Dashboards Without Code
Hey everyone! 
I’m excited to share a major new feature for the Pixoo Home Assistant integration: The Page Engine (BETA, like the rest of the integration). This is heavily inspired by the amazing work in pixoo-homeassistant, but completely rewritten from scratch to integrate seamlessly with the modern async architecture.
What Is It?
The Page Engine is a declarative component system that lets you design custom display pages using simple YAML configuration – no pixel-by-pixel positioning or complex automations needed. Think of it as a mini design language specifically for your Pixoo display.
You can:
- Create multi-element layouts with text, images, icons, shapes, and graphs
- Use Home Assistant templates (
{{ states('sensor.temperature') }}) anywhere
- Set up automatic page rotation (like a dashboard carousel)
- Show temporary override messages (doorbell, alerts) that auto-resume rotation
- Build reusable templates with variables
Why Is This Cool?
Instead of writing complex automations with multiple service calls to draw each element, you define everything in one place:
Old way (without Page Engine):
- service: pixoo.clear_display
- service: pixoo.display_text
data:
text: "Temperature"
x: 2
y: 2
- service: pixoo.display_text
data:
text: "{{ states('sensor.temperature') }}°C"
x: 2
y: 12
- service: pixoo.display_image
data:
url: "{{ state_attr('weather.home', 'entity_picture') }}"
New way (with Page Engine):
service: pixoo.render_page
target:
entity_id: light.pixoo64_display
data:
page:
page_type: components
background: "#001122"
components:
- type: text
x: 2
y: 2
text: "Temperature"
color: "#FFFFFF"
- type: text
x: 2
y: 12
text: "{{ states('sensor.temperature') }}°C"
color: "#00FF00"
- type: image
x: 40
y: 0
source:
url: "{{ state_attr('weather.home', 'entity_picture') }}"
Much cleaner, right? And everything renders in a single transaction!
Component Types
The Page Engine supports 8+ component types:
1. Text - Static or scrolling text
- type: text
x: 0
y: 0
text: "{{ states('sensor.title') }}"
color: "#FFFFFF"
align: center
scroll: true # Auto-scroll long text
scroll_speed: 50 # 0-100
2. Rectangle - Filled or outlined boxes
- type: rectangle
x: 0
y: 16
width: 64
height: 20
color: "#FF0000"
filled: true
3. Image - From URL, file path, or base64
- type: image
x: 0
y: 0
source:
url: "{{ state_attr('media_player.spotify', 'entity_picture') }}"
resize_mode: fit # fit, fill, none
4. Icon - MDI icons (Material Design Icons)
- type: icon
icon: "mdi:battery-{{ (battery | int / 10) | round * 10 }}"
x: 48
y: 48
size: 16
color: "#00FF00"
5. Line - Lines with thickness and color thresholds
- type: line
start: [0, 32]
end: [64, 32]
color: "#FFFFFF"
thickness: 2
value: "{{ states('sensor.temperature') }}"
color_thresholds:
- value: 20
color: "#0088FF" # Blue when cold
- value: 30
color: "#FF0000" # Red when hot
6. Circle - Dots, markers, or gauge segments
- type: circle
center: [32, 32]
radius: 10
color: "#00FF00"
filled: true
7. Arc - Circular progress rings and gauges
- type: arc
center: [32, 32]
radius: 20
start_angle: 0
end_angle: "{{ (battery | float) * 3.6 }}" # 0-100% -> 0-360°
color: "#00FF00"
thickness: 4
filled: false
8. Arrow - Directional arrows for compasses, wind direction
- type: arrow
center: [32, 32]
length: 20
angle: "{{ state_attr('weather.home', 'wind_bearing') }}"
color: "#FFFFFF"
thickness: 2
head_size: 4
Real-World Examples
Now Playing (Spotify/Apple Music)
service: pixoo.render_page
target:
entity_id: light.pixoo64_display
data:
page:
page_type: components
background: "#000000"
components:
# Album cover (full screen)
- type: image
x: 0
y: 0
source:
url: "{{ state_attr('media_player.spotify', 'entity_picture') }}"
# Dark overlay bar at bottom
- type: rectangle
x: 0
y: 46
width: 64
height: 18
filled: true
color: "#000000"
# Track title
- type: text
x: 2
y: 48
text: "{{ state_attr('media_player.spotify', 'media_title') }}"
color: "#FFFFFF"
scroll: true
# Artist
- type: text
x: 2
y: 56
text: "{{ state_attr('media_player.spotify', 'media_artist') }}"
color: "#888888"
Battery Gauge (Circular Progress)
service: pixoo.render_page
target:
entity_id: light.pixoo64_display
data:
page:
page_type: components
background: "#000000"
components:
# Background ring
- type: arc
center: [32, 32]
radius: 24
start_angle: 0
end_angle: 360
color: "#222222"
thickness: 6
# Battery progress arc
- type: arc
center: [32, 32]
radius: 24
start_angle: 0
end_angle: "{{ (states('sensor.battery') | float) * 3.6 }}"
color: "#00FF00"
thickness: 6
value: "{{ states('sensor.battery') | float }}"
color_thresholds:
- value: 20
color: "#FF0000" # Red when low
- value: 50
color: "#FFAA00" # Orange when medium
- value: 100
color: "#00FF00" # Green when full
# Battery icon
- type: icon
icon: "mdi:battery-{{ (states('sensor.battery') | int / 10) | round * 10 }}"
x: 24
y: 24
size: 16
color: "#FFFFFF"
# Percentage text
- type: text
x: 32
y: 48
align: center
text: "{{ states('sensor.battery') }}%"
color: "#FFFFFF"
Weather Dashboard
service: pixoo.render_page
target:
entity_id: light.pixoo64_display
data:
page:
page_type: components
background: "#001122"
components:
# Temperature ring (outer)
- type: arc
center: [32, 36]
radius: 20
start_angle: 0
end_angle: "{{ ((state_attr('weather.home', 'temperature') | float + 20) / 60 * 360) | int }}"
color: "#00FFFF"
thickness: 4
value: "{{ state_attr('weather.home', 'temperature') }}"
color_thresholds:
- value: 0
color: "#0088FF" # Blue (cold)
- value: 20
color: "#00FFFF" # Cyan (mild)
- value: 30
color: "#FF8800" # Orange (hot)
# Weather icon (center)
- type: icon
icon: "mdi:weather-{{ states('weather.home') }}"
x: 24
y: 28
size: 16
color: "#FFFFFF"
# Wind direction arrow
- type: arrow
center: [52, 52]
length: 10
angle: "{{ state_attr('weather.home', 'wind_bearing') }}"
color: "#FFFFFF"
thickness: 2
head_size: 3
# Temperature text
- type: text
x: 32
y: 48
align: center
text: "{{ state_attr('weather.home', 'temperature') }}°C"
color: "#FFFFFF"
Progress Bar
service: pixoo.render_page
target:
entity_id: light.pixoo64_display
data:
page:
page_type: components
background: "#000000"
components:
# Title
- type: text
x: 32
y: 6
align: center
text: "Download Progress"
color: "#FFFFFF"
# Percentage
- type: text
x: 32
y: 18
align: center
text: "{{ (states('sensor.download_progress') | float) | round(0) }}%"
color: "#FFFFFF"
# Progress bar border
- type: rectangle
x: 1
y: 28
width: 62
height: 8
filled: false
color: "#FFFFFF"
# Progress bar fill (60px max width)
- type: rectangle
x: 2
y: 29
width: "{{ (states('sensor.download_progress') | float / 100 * 60) | round(0, 'floor') }}"
height: 6
filled: true
color: "#00FF00"
Automatic Page Rotation
Want a rotating dashboard that cycles through multiple pages? Just set up rotation in your integration options!
1. Create a pages file (/config/pixoo_pages.yaml):
pages:
- name: weather
page_type: components
duration: 15
enabled: true
background: "#001122"
components:
- type: text
x: 32
y: 8
align: center
text: "{{ state_attr('weather.home', 'temperature') }}°C"
color: "#FFFFFF"
# ... more components
- name: now_playing
page_type: components
duration: 10
enabled: "{{ is_state('media_player.spotify', 'playing') }}" # Only show when playing
background: "#000000"
components:
- type: image
x: 0
y: 0
source:
url: "{{ state_attr('media_player.spotify', 'entity_picture') }}"
# ... more components
- name: battery_status
page_type: components
duration: 8
enabled: true
# ... battery gauge components
2. Configure rotation in Settings → Devices & Services → Pixoo:
- Set
pages_yaml_path to pixoo_pages.yaml
- Enable rotation
- Set default duration
3. Reload pages after editing:
service: pixoo.rotation_reload_pages
target:
entity_id: light.pixoo64_display
Pages will automatically cycle based on their duration and enabled conditions!
Temporary Override Messages
Show urgent notifications that auto-resume rotation:
service: pixoo.show_message
target:
entity_id: light.pixoo64_display
data:
duration: 10 # Show for 10 seconds, then resume rotation
page:
page_type: components
background: "#FF0000"
components:
- type: icon
icon: "mdi:doorbell"
x: 24
y: 16
size: 16
color: "#FFFFFF"
- type: text
x: 32
y: 40
align: center
text: "Doorbell!"
color: "#FFFFFF"
Perfect for:
Doorbell alerts
Door/window sensors
Timer notifications
Package delivery alerts
Garage door status
Available Services
pixoo.render_page
Render a single page immediately (no rotation).
pixoo.render_page_by_name
Render a specific page from your pixoo_pages.yaml file.
pixoo.show_message
Show temporary message with auto-resume.
pixoo.rotation_start / pixoo.rotation_stop
Control page rotation.
pixoo.rotation_reload_pages
Reload pages from YAML file.
pixoo.rotation_next_page / pixoo.rotation_previous_page
Manual page navigation.
Color Thresholds (Dynamic Coloring)
Many components support color_thresholds for dynamic coloring based on sensor values:
- type: arc
# ... position, size, etc.
value: "{{ states('sensor.battery') | float }}"
color_thresholds:
- value: 0
color: "#FF0000" # Red: 0-20%
- value: 20
color: "#FFAA00" # Orange: 20-50%
- value: 50
color: "#00FF00" # Green: 50-100%
The component automatically picks the right color based on the current value!
Conditional Pages
Use enabled to show pages only when relevant:
- name: now_playing
enabled: "{{ is_state('media_player.spotify', 'playing') }}"
# Only shown when Spotify is playing
- name: washing_machine_done
enabled: "{{ is_state('binary_sensor.washing_machine', 'off') and is_state('sensor.washer_run_state', 'stopped') }}"
duration: 30
# Only shown when wash cycle just finished
Pre-Built Templates
The integration ships with ready-to-use templates:
Now Playing - Media player with album art
Progress Bar - Generic progress indicator
Battery Gauge - Circular battery display
Weather Dashboard - Weather with temperature ring
Compass Navigation - Wind/GPS direction
Network Activity - Bar chart visualization
Weather Radar - Concentric circles with indicators
Stock Dashboard - 4-stock grid with S&P 500
Check out the examples folder for complete YAML configs!
Installation
The Page Engine is included in the latest version of the integration:
HACS (recommended):
- Add custom repository:
https://github.com/kmplngj/pixoo-ha
- Install “Pixoo” integration
- Restart Home Assistant
- Add integration via Settings → Devices & Services
Manual:
- Copy
custom_components/pixoo/ to your HA config
- Restart Home Assistant
Technical Details
For those interested in the implementation:
- Built on Pydantic v2 for type-safe models
- Full Jinja2 template support in all fields
- Async rendering pipeline with best-effort component handling
- Variable dependency resolution (variables can reference other variables)
- Pillow-based image processing with buffering
- Rotation state machine with message override support
- Support for Pixoo 16, Pixoo 64, and Pixoo Max (auto-detected)
What’s Next?
I’m planning to add:
Graph component - Historical sensor data graphs
Color gradients - Smooth color transitions
Animation support - Frame-based animations
More templates - Community contributions welcome!
Credits
Huge thanks to @gickowtf for pixoo-homeassistant – this work served as the inspiration and reference for the Page Engine architecture. The component DSL concept and rotation logic are heavily based on that brilliant project, adapted to work with the modern async pixooasync library.