Apply Venus OS best practices across all services

- Standardize multilog to s25000 n4 (~100KB cap) to prevent filling /data
- Use VeDbusService register=False + deferred register() in dbus-generator-ramp and dbus-vrm-history
- Add @exit_on_error decorator to template GLib callback
- Document best practices in README and template README

Made-with: Cursor
This commit is contained in:
2026-03-16 18:40:54 +00:00
parent 9756538f16
commit dd566f6975
15 changed files with 45 additions and 12 deletions

View File

@@ -74,6 +74,22 @@ Some projects require API keys or credentials. These are excluded from version c
- `dbus-windy-station/station_config.example.json` --> `station_config.json`
- `watermaker/ui/.env.example` --> `.env`
## Venus OS Best Practices
These projects follow guidelines from the [Venus OS wiki](https://github.com/victronenergy/venus/wiki):
**D-Bus Service Registration** -- Create `VeDbusService` with `register=False`, add all paths and populate initial values, then call `register()`. This prevents consumers from querying the service before it is fully initialized ([details](https://github.com/victronenergy/venus/wiki/dbus-api)).
**Mandatory D-Bus Paths** -- Every service must publish: `/Mgmt/ProcessName`, `/Mgmt/ProcessVersion`, `/Mgmt/Connection`, `/DeviceInstance`, `/ProductId`, `/ProductName`, `/FirmwareVersion`, `/Connected`.
**Stability and Exception Handling** -- Don't code too defensively. If something goes wrong, let the process exit cleanly and let daemontools restart it. For `GLib.timeout_add` callbacks in Python, an unhandled exception silently drops the callback without exiting; use `exit_on_error()` from `ve_utils.py` to ensure crashes propagate. Background threads should always set `daemon=True` ([details](https://github.com/victronenergy/venus/wiki/howto-add-a-driver-to-Venus#4-stability---exceptions-handling)).
**Logging** -- Use `multilog t s25000 n4` in `service/log/run` to cap log storage at ~100KB per service (4 files x 25KB). This matches the Victron v2.23 standard and prevents filling the `/data` partition on space-constrained devices.
**Service Directory** -- Symlink services to `/opt/victronenergy/service` (not `/service`, which is a tmpfs overlay). The `install.sh` scripts handle this with fallback logic.
**Persistence** -- Install code under `/data/` so it survives firmware updates. Add an entry in `/data/rc.local` to restore the service symlink after updates.
## New Project Template
See [`_template/`](_template/) for a skeleton D-Bus service with all the boilerplate: main script, config, daemontools service, install/uninstall scripts, and build packaging.

View File

@@ -34,6 +34,15 @@ Update these for your service:
| `/Connected` | int | Connection status (0/1) |
| `/Settings/Template/Enabled` | int | Enable/disable via settings |
## Venus OS Best Practices
The template follows these [Venus OS wiki](https://github.com/victronenergy/venus/wiki) guidelines:
- **`register=False`** -- `VeDbusService` is created with `register=False`, paths are added, then `register()` is called to prevent race conditions with consumers.
- **`@exit_on_error`** -- Applied to the `GLib.timeout_add` callback so unhandled exceptions crash the process instead of silently dropping the callback.
- **`daemon=True`** -- Set on background threads so they don't prevent process exit.
- **multilog** -- Log size capped at `s25000 n4` (~100KB) to avoid filling the `/data` partition.
## Checklist
- [ ] Unique service name in `config.py` (`com.victronenergy.yourservice`)
@@ -41,4 +50,6 @@ Update these for your service:
- [ ] All file references updated in `service/run`, `install.sh`, `build-package.sh`
- [ ] Custom D-Bus paths added
- [ ] Settings paths updated
- [ ] `@exit_on_error` on all `GLib.timeout_add` callbacks
- [ ] Background threads use `daemon=True`
- [ ] README replaced with your own documentation

View File

@@ -20,6 +20,7 @@ import time
sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'ext', 'velib_python'))
from vedbus import VeDbusService # noqa: E402
from ve_utils import exit_on_error # noqa: E402
from settingsdevice import SettingsDevice # noqa: E402
import dbus # noqa: E402
from gi.repository import GLib # noqa: E402
@@ -84,6 +85,7 @@ class TemplateService:
self._running = True
GLib.timeout_add(MAIN_LOOP_INTERVAL_MS, self._update)
@exit_on_error
def _update(self):
"""Called every MAIN_LOOP_INTERVAL_MS. Return True to keep running."""
if not self._running:

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-template
exec multilog t s25000 n4 /var/log/dbus-template

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-raymarine-publisher
exec multilog t s25000 n4 /var/log/dbus-raymarine-publisher

View File

@@ -157,7 +157,7 @@ class GeneratorRampController:
for attempt in range(max_retries):
try:
self.dbus_service = VeDbusService(SERVICE_NAME, self.bus)
self.dbus_service = VeDbusService(SERVICE_NAME, self.bus, register=False)
break # Success
except dbus.exceptions.NameExistsException:
if attempt < max_retries - 1:
@@ -317,6 +317,7 @@ class GeneratorRampController:
onchangecallback=self._on_setting_changed,
gettextcallback=lambda p, v: f"{v} min")
self.dbus_service.register()
self.logger.info("D-Bus service created")
def _generator_state_text(self, path, value):

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-generator-ramp-webui
exec multilog t s25000 n4 /var/log/dbus-generator-ramp-webui

View File

@@ -5,4 +5,4 @@
# Logs service output using multilog
#
exec multilog t s99999 n8 /var/log/dbus-generator-ramp
exec multilog t s25000 n4 /var/log/dbus-generator-ramp

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-lightning
exec multilog t s25000 n4 /var/log/dbus-lightning

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-meteoblue-forecast
exec multilog t s25000 n4 /var/log/dbus-meteoblue-forecast

View File

@@ -5,4 +5,4 @@
# Logs service output using multilog
#
exec multilog t s99999 n8 /var/log/dbus-no-foreign-land
exec multilog t s25000 n4 /var/log/dbus-no-foreign-land

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-tides
exec multilog t s25000 n4 /var/log/dbus-tides

View File

@@ -93,7 +93,7 @@ class VrmHistoryService:
for attempt in range(max_retries):
try:
self.dbus_service = VeDbusService(SERVICE_NAME, self.bus)
self.dbus_service = VeDbusService(SERVICE_NAME, self.bus, register=False)
break
except dbus.exceptions.NameExistsException:
if attempt < max_retries - 1:
@@ -135,6 +135,9 @@ class VrmHistoryService:
svc.add_path('/History/GeneratorRuntime', '')
svc.add_path('/History/BatteryCycles', '')
# Register service after all paths added (Venus OS best practice)
svc.register()
def _setup_settings(self):
try:
base = DBUS_CONFIG['settings_path']

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-vrm-history
exec multilog t s25000 n4 /var/log/dbus-vrm-history

View File

@@ -1,2 +1,2 @@
#!/bin/sh
exec multilog t s99999 n8 /var/log/dbus-windy-station
exec multilog t s25000 n4 /var/log/dbus-windy-station