| Both sides previous revision Previous revision Next revision | Previous revision |
| launch_a_script [2024/02/27 16:16] – [Table] Add hook for RetroAchievements lbrpdx | launch_a_script [2025/11/05 22:37] (current) – [Table] beter formatting lbrpdx |
|---|
| |
| ==== custom: Last during startup, first after shutdown ==== | ==== custom: Last during startup, first after shutdown ==== |
| | |
| | <WRAP center round important> |
| | While [[#custom.sh (deprecated)|custom.sh]] still works in **v40**, it's deprecated since **v38** and and may be removed in a later version. (See [[https://batocera.org/changelog|changelog]].) |
| | The recommended way to do custom scripting in Batocera now is the [[#services|services]] method detailed below. |
| | </WRAP> |
| | |
| | === custom.sh (deprecated) === |
| |
| Sometimes you just want to fire up one script after successfully booting, for example when you want [[:vpn_client|to start up a VPN]] or other after-boot tasks. In order to do this, create a script text file at ''/userdata/system/custom.sh''. Be aware that the script will be executed independently of the executable (''x'') attribute being set to the script file or not! | Sometimes you just want to fire up one script after successfully booting, for example when you want [[:vpn_client|to start up a VPN]] or other after-boot tasks. In order to do this, create a script text file at ''/userdata/system/custom.sh''. Be aware that the script will be executed independently of the executable (''x'') attribute being set to the script file or not! |
| </WRAP> | </WRAP> |
| |
| === A simple custom script as an example === | == A simple custom script as an example == |
| |
| <code bash| custom.sh> | <code bash| custom.sh> |
| |
| exit $? | exit $? |
| | </code> |
| | |
| | === services === |
| | |
| | You should replace ''/userdata/system/custom.sh'' with ''/userdata/system/services/myservice''. |
| | |
| | Important notes: |
| | * Make sure that the filename is without the ''.sh'' extension. |
| | * To debug your services, use: ''bash -x batocera-services list'' |
| | |
| | Additional benefits of this system over ''custom.sh'': |
| | * You can now have more than one service running. |
| | * You can enable and disable them individually, from the command line (''batocera-services'') or through the UI in EmulationStation (System settings -> Services). |
| | |
| | **Disabling** services (with ''batocera-services disable'') doesn't **stop** them. That needs to be done manually with ''batocera-services stop''. Disabling them only means that they wil not be automatically started after the next system startup. |
| | |
| | **Enabling** them (with ''batocera-services enable'') works the same. To actually **start** a service, enable it first, then restart Batocera or use ''batocera-services start''. |
| | |
| | Enabling or disabling the service in the EmulationStation user interface will also start or stop the service immediately. |
| | |
| | == Sample service == |
| | |
| | <code bash| myservice> |
| | #!/bin/bash |
| | |
| | case "$1" in |
| | start) |
| | echo "I've started." |
| | ;; |
| | stop) |
| | echo "I've stopped." |
| | ;; |
| | status) |
| | echo "This is my status." |
| | ;; |
| | esac |
| </code> | </code> |
| |
| </WRAP> | </WRAP> |
| |
| ^ Event name ^ Arguments ^ Notes ^ | ^ Event name ^ Arguments ^ Notes ^ |
| | ''game-start'' | ROM name ''rom'', ROM path ''basename'' | When a game starts. Nearly identical to Batocera's ''gameStart'', however this doesn't include as much information and only triggers when it's ES itself that starts it. | | | ''game-start'' | ROM name ''rom'', ROM path ''basename'' (arguments described below) | When a game starts. Nearly identical to Batocera's ''gameStart'', however this doesn't include as much information and only triggers when it's ES itself that starts it. | |
| | ''game-end'' | N/A | When a game ends. Nearly identical to Batocera's ''gameStop'', however this only triggers when it's ES itself that ends it. | | | ''game-end'' | N/A | When a game ends. Nearly identical to Batocera's ''gameStop'', however this only triggers when it's ES itself that ends it. | |
| | ''game-selected'' | ''getSourceFileData()->getSystem()->getName()'', ''getPath()'', ''getName()'' | New to Batocera **v33**. Whichever game is currently being hovered over. Includes games shown during the screensaver. | | | ''game-selected'' | ''getSourceFileData()->getSystem()->getName()'', ''getPath()'', ''getName()'' | New to Batocera **v33**. Whichever game is currently being hovered over. Includes games shown during the screensaver. | |
| | ''system-selected'' | System ''getSelected()->getName()'' | New to Batocera **v33**. Whichever system is currently being hovered over in the system list. | | | ''system-selected'' | System ''getSelected()->getName()'' | New to Batocera **v33**. Whichever system is currently being hovered over in the system list. | |
| | ''theme-changed'' | Theme being switched to ''theme_set->getSelected()'', Previous theme ''oldTheme'' | When a different theme is selected. Mostly used for theme/element reloading. | | | ''theme-changed'' | Theme being switched to ''theme_set->getSelected()'', Previous theme ''oldTheme'' | When a different theme is selected. Mostly used for theme/element reloading. | |
| | ''settings-changed'' | N/A | When a system setting is saved. | | | ''settings-changed'' | N/A | When a system setting is saved. | |
| | ''controls-changed'' | N/A | When a controller mapping is saved from the **CONTROLLER MAPPING** menu. | | | ''controls-changed'' | N/A | When a controller mapping is saved from the **CONTROLLER MAPPING** menu. | |
| | ''config-changed'' | N/A | Whenever any configuration, be it system settings or controller mappings, is changed. | | | ''config-changed'' | N/A | Whenever any configuration, be it system settings or controller mappings, is changed. | |
| | ''quit'' | N/A | When the system is told to do a regular shutdown. | | | ''quit'' | N/A | When the system is told to do a regular shutdown. | |
| | ''reboot'' | N/A | When the system is told to do a reboot. | | | ''reboot'' | N/A | When the system is told to do a reboot. | |
| | ''shutdown'' | N/A | When the system is told to do a fast shutdown. | | | ''shutdown'' | N/A | When the system is told to do a fast shutdown. | |
| | ''sleep'' | N/A | When the system is told to sleep. | | | ''sleep'' | N/A | When the system is told to sleep. | |
| | ''wake'' | N/A | When the system is told to wake from sleep. | | | ''wake'' | N/A | When the system is told to wake from sleep. | |
| | ''achievements'' | N/A | When a RetroAchievement is unlocked | | | ''achievements'' | with arguments described below | When a RetroAchievement is unlocked - new to Batocera **v38** | |
| | | ''screensaver-start'' | N/A | When the screensaver starts | |
| | | ''screensaver-stop'' | N/A | When the screensaver stops (waken up by user) | |
| | | ''suspend'' | N/A | When the system is told to sleep (but triggered by ''/etc/pm/sleep.d/99es_scripts_runner'' instead of ''sleep'') | |
| | | ''resume'' | N/A | When waken up by keyboard action ( triggered by ''/etc/pm/sleep.d/99es_scripts_runner'') | |
| | | ''controller-connected'' | device being added to the Batocera machine | New to Batocera **v43**, triggered when a device ''/dev/input/js*'' is connected | |
| | | ''controller-disconnected'' | device being disconnected | New to Batocera **v43**, triggered when a device ''/dev/input/js*'' is disconnected | |
| |
| EmulationStation sends different arguments to these scripts based on the event type. For game-related events: | EmulationStation sends different arguments to these scripts based on the event type. For game-related events: |
| ifconfig eth1 down | ifconfig eth1 down |
| </file> | </file> |
| | |
| | ==== Shut down Batocera after 1 hour of idleness ==== |
| | |
| | In that example, we want the Batocera system to shutdown one hour after the screensaver is launched (so that the system shuts down itself automatically after being idle). |
| | |
| | To do so, you will need to add two scripts: |
| | * ''/userdata/system/configs/emulationstation/scripts/screensaver-start/shutdown-trigger.sh'' that is launched when the screensaver starts, and will start a 1 hour timer (see in the script the 3rd line where you can tune that with options possible through the ''date'' command). |
| | * ''/userdata/system/configs/emulationstation/scripts/screensaver-stop/shutdown-cancel.sh'' that cancels the automatic shutdown, triggered when you touch the controller. |
| | |
| | The script to put in the directory ''/userdata/system/configs/emulationstation/scripts/screensaver-start/'' is: |
| | <file bash shutdown-trigger.sh> |
| | #!/bin/sh |
| | TRG=/tmp/shutdown.screensaver |
| | date -d "+1 hour" +"%s" > "$TRG" |
| | while true; do |
| | now=$(date +"%s") |
| | [ -f "$TRG" ] || break |
| | trg=$(cat "$TRG") |
| | if [ "$now" -ge "$trg" ]; then |
| | shutdown -h now |
| | rm "$TRG" |
| | fi |
| | sleep 5 |
| | done |
| | </file> |
| | |
| | And the script to put in the directory ''/userdata/system/configs/emulationstation/scripts/screensaver-stop/'' is: |
| | <file bash shutdown-cancel.sh> |
| | #!/bin/sh |
| | TRG=/tmp/shutdown.screensaver |
| | rm "$TRG" |
| | </file> |
| | |
| | |
| |
| ==== Batocera event watcher scripts ==== | ==== Batocera event watcher scripts ==== |
| # Toggle the audio mute. | # Toggle the audio mute. |
| batocera-audio setSystemVolume mute-toggle | batocera-audio setSystemVolume mute-toggle |
| | </file> |
| | |
| | * Watch for controller inactivity and do X when inactive |
| | <file bash controller-inactivity-checker.sh> |
| | #!/bin/bash |
| | |
| | LOCK="/var/run/controller-inactivity-checker.lock" |
| | if [ -f "$LOCK" ]; then |
| | exit 1 |
| | fi |
| | |
| | trap 'rm -f "$LOCK"; exit 0' SIGTERM EXIT |
| | touch "$LOCK" |
| | |
| | STATE="active" |
| | JS_DEVICES=() |
| | |
| | TIMER="$(/usr/bin/batocera-settings-get system.controllerinactivitytimer)" |
| | if [[ -z "$TIMER" || ! "$TIMER" =~ ^[0-9]+$ || "$TIMER" -le 60 ]]; then |
| | TIMER="900" # default in seconds |
| | /usr/bin/batocera-settings-set system.controllerinactivitytimer "$TIMER" |
| | fi |
| | |
| | js_update() { |
| | JS_DEVICES=() |
| | for js_device in /dev/input/js*; do |
| | if [ -e "$js_device" ]; then |
| | JS_DEVICES+=("$js_device") |
| | fi |
| | done |
| | } |
| | |
| | do_inactivity() { |
| | STATE="inactive" |
| | # your code here |
| | } |
| | |
| | do_activity() { |
| | STATE="active" |
| | # your code here |
| | } |
| | |
| | monitor_controllers() { |
| | local JS_REFRESH_INTERVAL=10 # Number of loops before controller refresh (by default 1 loop per second) |
| | LOOP_COUNT=0 # This should always be 0 |
| | |
| | while true; do |
| | if (( LOOP_COUNT % JS_REFRESH_INTERVAL == 0 )); then |
| | js_update |
| | fi |
| | |
| | for i in "${!JS_DEVICES[@]}"; do |
| | js="${JS_DEVICES[$i]}" |
| | |
| | if [ -e "$js" ]; then |
| | if timeout 1 jstest --event "$js" | tail -n +25 | grep -q "Event"; then |
| | LOOP_COUNT=0 |
| | JS_DEVICES=("$js" "${JS_DEVICES[@]:0:$i}" "${JS_DEVICES[@]:$((i + 1))}") |
| | if [ "$STATE" = "inactive" ]; then |
| | do_activity |
| | fi |
| | break |
| | fi |
| | fi |
| | done |
| | |
| | ((LOOP_COUNT++)) |
| | if (( LOOP_COUNT >= TIMER )); then |
| | if [ "$STATE" = "active" ]; then |
| | do_inactivity |
| | fi |
| | fi |
| | done |
| | } |
| | |
| | js_update |
| | monitor_controllers |
| | |
| | exit 0 |
| | |
| </file> | </file> |
| |