ErisPulse-Dashboard
ErisPulse-Dashboard is a web management panel module directly maintained by ErisDev, providing a visual runtime management interface for ErisPulse: module start/stop, configuration editing, log viewing, event stream monitoring, and more.
Important
Dashboard is not a built-in feature of the ErisPulse framework and must be installed separately:
epsdk install Dashboard
The Dashboard also supports other ErisPulse modules registering custom management pages to the sidebar. After registration, users can directly switch to the dedicated view page of that module within the Dashboard, without needing to develop an additional standalone frontend interface.
Note
View registration is an optional feature.
- If the Dashboard module is not installed or not loaded, calling
sdk.Dashboard.register_view()will raise an exception - Be sure to wrap registration code with
try/exceptto ensure other features of the module are not affected - It is recommended to check if Dashboard is available before registering:
hasattr(sdk, 'Dashboard') and sdk.Dashboard
How It Works
Module on_load()
→ Calls sdk.Dashboard.register_view(...)
→ Dashboard backend stores view information
→ WebSocket notifies frontend
→ Frontend dynamically creates sidebar navigation item + page container
→ User clicks to view module view
Register API
sdk.Dashboard.register_view(
id="MyModule", # Required, unique identifier
title="我的模块", # Chinese name
title_en="My Module", # English name
icon_svg='<svg>...</svg>', # SVG icon for sidebar
html_content='<div>...</div>', # HTML content for the page
js_content='function xxx() {}', # JavaScript logic for the page
css_content='.my-style {}', # Optional custom CSS
iframe_url='', # URL for iframe mode (either this or html_content)
loader="loadMyModuleView", # JS function name called when switching to this page
group="group_extensions", # Sidebar group
group_title="", # Custom group Chinese title
group_title_en="", # Custom group English title
)
Parameter Description
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
str |
Yes | Unique identifier for the view, recommended to use the module name |
title |
str |
No | Chinese display name, defaults to id |
title_en |
str |
No | English display name, defaults to title |
icon_svg |
str |
No | Full SVG string for the sidebar icon |
html_content |
str |
No* | HTML content for the injected mode page |
js_content |
str |
No | JavaScript code for the page |
css_content |
str |
No | Custom CSS styles for the page |
iframe_url |
str |
No* | URL for iframe mode, if set, html_content is ignored |
loader |
str |
No | Name of the JS function automatically called when the page is activated |
group |
str |
No | Sidebar group identifier, default is group_extensions |
group_title |
str |
No | Custom Chinese title for the group |
group_title_en |
str |
No | Custom English title for the group |
*Either
html_contentoriframe_urlmust be provided; otherwise, the page will be blank.
Two Injection Modes
Mode 1: HTML/JS Injection (Recommended)
Directly provide HTML, JS, and CSS strings. The Dashboard will inject the content into the page. This mode is fully consistent with the Dashboard's styling, and it is recommended to use the CSS class names provided by the Dashboard.
sdk.Dashboard.register_view(
id="HelloPage",
title="你好页面", title_en="Hello",
icon_svg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/></svg>',
html_content='<h1 class="page-title">Hello World</h1><div class="card"><div class="card-body">这是一个示例页面</div></div>',
group="group_tools",
)
For a complete weather module example (including API routes, JS interactions, etc.), see the Complete Module Example below.
Mode 2: iframe Embedding
The module provides its own HTML page URL (which needs to be registered separately), and the Dashboard embeds it using an iframe. This mode is suitable for scenarios requiring a completely independent UI or complex interactions.
sdk.Dashboard.register_view(
id="MyVisualizer",
title="数据可视化", title_en="Data Visualizer",
iframe_url="/MyVisualizer/view",
group="group_tools",
)
In iframe mode, a
tokenparameter is automatically appended to the URL for authentication purposes.
Sidebar Grouping
Modules can specify which sidebar group the window belongs to. Dashboard provides the following built-in groups:
| Group Identifier | Chinese Name | Position |
|---|---|---|
group_overview |
Overview | Group 1 |
group_events |
Events | Group 2 |
group_extensions |
Extensions | Group 3 (Default) |
group_system |
System | Group 4 |
group_tools |
Tools | Group 5 |
Specify a built-in group name, and the module window will be appended to the end of that group:
group="group_tools" # Appends to the "Tools" group
You can also use a custom group name (not starting with group_), and Dashboard will automatically create a new group:
group="my_group",
group_title="我的分组",
group_title_en="My Group",
Common CSS Class Names
When using the HTML injection mode in module windows, you can directly use the existing CSS class names from Dashboard to maintain visual consistency:
| Class Name | Purpose |
|---|---|
page-title |
Page title, e.g., <h1 class="page-title">Title</h1> |
card |
Card container |
card-header |
Card header bar |
card-body |
Card content area |
grid-2 |
Two-column grid layout |
grid-3 |
Three-column grid layout |
btn |
Basic button |
btn-primary |
Primary button (blue) |
btn-secondary |
Secondary button |
btn-icon |
Icon button |
btn-danger |
Button for dangerous operations |
Dashboard uses CSS variables to control theme colors. You can directly reference these variables in module windows:
| CSS Variable | Purpose |
|---|---|
var(--bg-p) |
Primary background color |
var(--bg-s) |
Secondary background color |
var(--bg-t) |
Tertiary background color (for cards, etc.) |
var(--tx-p) |
Primary text color |
var(--tx-s) |
Secondary text color |
var(--tx-t) |
Auxiliary text color |
var(--bd) |
Border color |
var(--accent) |
Accent color |
var(--ok-c) |
Success color |
var(--er-c) |
Error color |
These variables automatically switch based on Dashboard's light/dark theme, so modules do not need additional handling.
Authentication and API Calls
When calling the module's own API from the JavaScript in the module window, you need to include the Dashboard's Token for authentication:
var token = localStorage.getItem('__ep_tk__');
var resp = await fetch('/YourModule/api/data', {
headers: { 'Authorization': 'Bearer ' + token }
});
var data = await resp.json();
The module's API endpoint can decide whether to validate the Token. If validation is required, you can extract it from the request headers:
async def _api_data(self, request):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if not token:
return {"error": "Unauthorized"}, 401
return {"data": "hello"}
Complete Module Example
The following is a complete weather module example, demonstrating how to register a window, provide API data, and clean up resources upon unloading:
from ErisPulse import sdk
from ErisPulse.Core.Bases import BaseModule
from ErisPulse.Core.Event import command
class Main(BaseModule):
def __init__(self):
self.sdk = sdk
self.logger = sdk.logger.get_child("Weather")
self.config = self._load_config()
@staticmethod
def get_load_strategy():
from ErisPulse.loaders import ModuleLoadStrategy
return ModuleLoadStrategy(lazy_load=False, priority=50)
async def on_load(self, event):
self._register_routes()
self._register_dashboard_view()
self.logger.info("Weather module loaded")
async def on_unload(self, event):
self._unregister_routes()
if hasattr(self.sdk, 'Dashboard') and self.sdk.Dashboard:
self.sdk.Dashboard.unregister_view("Weather")
self.logger.info("Weather module unloaded")
def _load_config(self):
config = self.sdk.config.getConfig("Weather")
if not config:
default = {"city": "Beijing", "api_key": ""}
self.sdk.config.setConfig("Weather", default)
return default
return config
def _register_routes(self):
r = self.sdk.router
r.register_http_route("Weather", "/api/current",
handler=self._api_current, methods=["GET"])
def _unregister_routes(self):
r = self.sdk.router
try:
r.unregister_http_route("Weather", "/api/current")
except Exception:
pass
async def _api_current(self, request):
return {
"city": self.config.get("city", "Beijing"),
"temp": 25,
"humidity": 60,
}
def _register_dashboard_view(self):
try:
dashboard = self.sdk.Dashboard
dashboard.register_view(
id="Weather",
title="Weather", title_en="Weather",
icon_svg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>',
html_content='''
<h1 class="page-title">Weather Query</h1>
<p style="color:var(--tx-s);margin-bottom:16px">View current weather information</p>
<div class="grid-2">
<div class="card">
<div class="card-header">Current Weather</div>
<div class="card-body">
<div id="weather-info" style="font-size:14px;color:var(--tx-s)">Click to refresh and load</div>
</div>
</div>
<div class="card">
<div class="card-header">Actions</div>
<div class="card-body">
<button class="btn btn-primary" onclick="refreshWeather()">Refresh</button>
</div>
</div>
</div>
''',
js_content='''
async function loadWeatherView() { await refreshWeather(); }
async function refreshWeather() {
var el = document.getElementById('weather-info');
if (!el) return;
el.textContent = 'Loading...';
try {
var resp = await fetch('/Weather/api/current', {
headers: { 'Authorization': 'Bearer ' + localStorage.getItem('__ep_tk__') }
});
var data = await resp.json();
el.innerHTML = '<p>City: ' + (data.city || '--') + '</p>' +
'<p>Temperature: ' + (data.temp || '--') + '°C</p>' +
'<p>Humidity: ' + (data.humidity || '--') + '%</p>';
} catch (e) {
el.textContent = 'Failed to load: ' + e.message;
}
}
''',
loader="loadWeatherView",
group="group_tools",
)
except Exception as e:
self.logger.warning(f"Failed to register Dashboard view: {e}")
Unregistering Views
When the module is unloaded, unregister_view() should be called to clean up registered views:
async def on_unload(self, event):
if hasattr(self.sdk, 'Dashboard') and self.sdk.Dashboard:
self.sdk.Dashboard.unregister_view("Weather")
After unregistration, the Dashboard frontend will remove the sidebar navigation item and page content in real-time via WebSocket, without requiring a user refresh.
Notes
- Load Order — The Dashboard's load priority is
99999(high priority). Your module's priority should be lower than this value (e.g.,50), ensuring the Dashboard loads first. - Defensive Programming — Use
try/exceptto wrap window registration, as the Dashboard module may not be installed or loaded. - Resource Cleanup — Call
unregister_view()inon_unloadto remove registered windows. - ID Uniqueness — The
idparameter must be unique throughout the Dashboard. It is recommended to use the module name directly. - SVG Icon —
icon_svgshould be a complete<svg>tag. It is recommended to useviewBox="0 0 24 24"andstroke="currentColor"to inherit the Dashboard's main theme color. - JS Function Naming — The function names in
js_contentshould be unique (e.g.,loadWeatherView), to avoid conflicts with other modules. - Dynamic Updates — After registering or unregistering windows, the Dashboard frontend will update the sidebar in real time via WebSocket, without requiring a page refresh.