Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| homeautomation [2023/01/03 13:29] – Improvement: Adjust command to prevent terminal timeouts. grandmabetty | homeautomation [2026/02/20 15:59] (current) – Advanced MQTT example lbrpdx | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== Home automation ====== | ====== Home automation ====== | ||
| - | Welcome to the awesome world of home automation! | + | Since Batocera is [[wake_on_lan|Wake on LAN (WoL)]] enabled by default |
| + | |||
| + | ===== Integrate Batocera with MQTT ===== | ||
| + | MQTT is a simple messaging protocol used in home automation to allow devices to talk to each other. It helps smart home gadgets, like lights and sensors, send and receive messages quickly and efficiently. This means you can control and monitor your home devices in real-time, as well as trigger automations that respond to changes in those devices. | ||
| + | |||
| + | Batocera provides an MQTT client, which can publish all of Batocera' | ||
| + | |||
| + | ==== Requirements ==== | ||
| + | |||
| + | * A separate MQTT broker (server) to publish events to. Batocera only contains tools to read & write to an MQTT broker, but not the broker itself. Solutions like Home Assistant have fairly easy packages MQTT brokers available. | ||
| + | |||
| + | ==== Use a Custom User Service to Forward Events ==== | ||
| + | |||
| + | The following user service listens for the [[wiki: | ||
| + | |||
| + | When the service is enabled, it will install several small script files that take advantage Batocera' | ||
| + | |||
| + | - Copy this script to ''/ | ||
| + | - Modify the configuration variables at the top of the file with the connection info for your MQTT broker | ||
| + | - In EmulationStation, | ||
| + | - Reboot to complete install, or via SSH, run ''/ | ||
| + | |||
| + | The service is designed to capture events and publish them to the following topics: | ||
| + | |||
| + | < | ||
| + | / | ||
| + | / | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | The messages published to those topics are a JSON dictionary containing 2 fields: | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | <code bash mqtt_events> | ||
| + | MQTT_HOST=your-mqtt-server.lan | ||
| + | MQTT_PORT=1883 | ||
| + | MQTT_USER=username | ||
| + | MQTT_PASSWORD=password | ||
| + | MQTT_BASE_TOPIC=batocera/ | ||
| + | |||
| + | SERVICE_NAME=$(basename " | ||
| + | ES_EVENTS=(game-start game-end game-selected system-selected theme-changed settings-changed controls-changed config-changed quit reboot shutdown sleep wake achievements screensaver-start screensaver-stop) | ||
| + | |||
| + | |||
| + | ############################################################################### | ||
| + | # Publish an event message to the MQTT broker as JSON dictionary. | ||
| + | # | ||
| + | # The first argument is the subtopic to publish to (source/ | ||
| + | # The remaining arguments are converted to a list of strings, which | ||
| + | # is stored in the ' | ||
| + | # An ISO-formatted ' | ||
| + | # | ||
| + | # Only executes if this service is enabled | ||
| + | ############################################################################### | ||
| + | publish() { | ||
| + | if [[ " $(/ | ||
| + | subtopic=" | ||
| + | shift | ||
| + | # Initialize an empty JSON array | ||
| + | data=' | ||
| + | # Iterate over the remaining arguments | ||
| + | for arg in " | ||
| + | # Convert each argument to a JSON string and append it to the array | ||
| + | data+=" | ||
| + | done | ||
| + | # Remove the trailing comma and close the JSON array | ||
| + | data=" | ||
| + | mosquitto_pub -h " | ||
| + | fi | ||
| + | } | ||
| + | |||
| + | ############################################################################### | ||
| + | # Start the MQTT service. | ||
| + | # - Installs any missing support files | ||
| + | # - Publishes a system startup event | ||
| + | ############################################################################### | ||
| + | start() { | ||
| + | install | ||
| + | publish " | ||
| + | } | ||
| + | |||
| + | ############################################################################### | ||
| + | # Stop the MQTT service. | ||
| + | # - Publishes a system shutdown event | ||
| + | ############################################################################### | ||
| + | stop() { | ||
| + | publish " | ||
| + | } | ||
| + | |||
| + | ############################################################################### | ||
| + | # Create scripts that capture events and publish them to the MQTT broker. | ||
| + | ############################################################################### | ||
| + | install() { | ||
| + | # EmulationStation events | ||
| + | for event in ${ES_EVENTS[@]}; | ||
| + | script_dir="/ | ||
| + | mkdir -p " | ||
| + | script=" | ||
| + | if [[ -f " | ||
| + | echo " | ||
| + | continue | ||
| + | fi | ||
| + | echo " | ||
| + | cat << | ||
| + | # | ||
| + | / | ||
| + | EOF | ||
| + | chmod +x " | ||
| + | done | ||
| + | |||
| + | # Game events | ||
| + | script="/ | ||
| + | echo " | ||
| + | cat << | ||
| + | # | ||
| + | / | ||
| + | EOF | ||
| + | } | ||
| + | |||
| + | ############################################################################### | ||
| + | # Delete scripts that capture events and publish them to the MQTT broker. | ||
| + | ############################################################################### | ||
| + | uninstall() { | ||
| + | # Delete EmulationStation event scripts, and remove event directories if empty | ||
| + | for event in ${ES_EVENTS[@]}; | ||
| + | script_dir="/ | ||
| + | script=" | ||
| + | echo " | ||
| + | rm " | ||
| + | if [ -z "$(ls -A " | ||
| + | echo " | ||
| + | rm -rf " | ||
| + | fi | ||
| + | done | ||
| + | |||
| + | # Delete Game event script | ||
| + | script="/ | ||
| + | echo " | ||
| + | rm " | ||
| + | } | ||
| + | |||
| + | ############################################################################### | ||
| + | # Script entrypoint | ||
| + | ############################################################################### | ||
| + | if [ $# -eq 0 ]; then | ||
| + | echo " | ||
| + | echo " | ||
| + | exit 1 | ||
| + | fi | ||
| + | if [[ $(type -t " | ||
| + | FUNCTION=" | ||
| + | shift | ||
| + | $FUNCTION " | ||
| + | else | ||
| + | echo " | ||
| + | echo " | ||
| + | exit 1 | ||
| + | fi | ||
| + | </ | ||
| + | |||
| + | ==== More Advanced Script ==== | ||
| + | |||
| + | The following script is an extension of the previous script, that provides more data. Once enabled and integrated with a Home Assistant MQTT broker, you can transparently get cards like this: | ||
| + | {{ :: | ||
| + | |||
| + | To install this one: | ||
| + | - copy this script into ''/ | ||
| + | - edit configuration variables at the top of the file with the credentials your MQTT broker | ||
| + | - make it executable with '' | ||
| + | - run ''/ | ||
| + | - then, either go to EmulationStation and enable the service, or do it from the command line with '' | ||
| + | |||
| + | <code bash mqtt_events> | ||
| + | # | ||
| + | # Batocera MQTT bridge (boxart + marquee + CPU-only telemetry pinned to HWMON: | ||
| + | # Initially from kit on Batocera Discord | ||
| + | # - CPU temp: | ||
| + | # * Median-of-samples + sticky + last-good + hard range guards. | ||
| + | # * Cached temp*_input path to avoid flicks and rescan churn. | ||
| + | # - Home Assistant MQTT discovery: | ||
| + | # * Image: Box Art + Marquee (no YAML). | ||
| + | # * Sensors: cpu_temp (+source/ | ||
| + | # - Debug topic: batocera/< | ||
| + | |||
| + | ###################################### | ||
| + | # Broker / topics | ||
| + | ###################################### | ||
| + | MQTT_HOST="" | ||
| + | MQTT_PORT="" | ||
| + | MQTT_USER="" | ||
| + | MQTT_PASSWORD="" | ||
| + | |||
| + | DISCOVERY_PREFIX=" | ||
| + | HOSTNAME=" | ||
| + | MQTT_BASE_TOPIC=" | ||
| + | |||
| + | # Telemetry | ||
| + | TELEMETRY_ENABLE=1 | ||
| + | TELEMETRY_INTERVAL=5 | ||
| + | |||
| + | # Media behavior | ||
| + | RETAIN_MEDIA_BYTES=1 | ||
| + | PUBLISH_IMAGE_B64=1 | ||
| + | |||
| + | # Paths / cache | ||
| + | PIDFILE="/ | ||
| + | TMP_DIR="/ | ||
| + | mkdir -p " | ||
| + | CORETEMP_PATH_CACHE="/ | ||
| + | CPU_TEMP_STATE_FILE="/ | ||
| + | |||
| + | # jq detection (optional) | ||
| + | HAS_JQ=0 | ||
| + | command -v jq >/ | ||
| + | |||
| + | ###################################### | ||
| + | # ES service | ||
| + | ###################################### | ||
| + | SERVICE_NAME=$(basename " | ||
| + | ES_EVENTS=(game-start game-end game-selected system-selected theme-changed settings-changed controls-changed config-changed quit reboot shutdown sleep wake achievements screensaver-start screensaver-stop) | ||
| + | |||
| + | ###################################### | ||
| + | # Helpers | ||
| + | ###################################### | ||
| + | _ts() { date -u +" | ||
| + | _local_ts() { date +" | ||
| + | |||
| + | _json_array_from_args() { | ||
| + | if [ " | ||
| + | local arr=" | ||
| + | for arg in " | ||
| + | arr+=$(jq -Rn --arg a " | ||
| + | arr+="," | ||
| + | done | ||
| + | echo " | ||
| + | else | ||
| + | local arr=" | ||
| + | for arg in " | ||
| + | echo " | ||
| + | fi | ||
| + | } | ||
| + | |||
| + | # Retained publishers | ||
| + | _pub() { mosquitto_pub -h " | ||
| + | _pub_str() { _pub " | ||
| + | |||
| + | # Non-retained | ||
| + | _pub_event() { mosquitto_pub -h " | ||
| + | |||
| + | # File publisher (bytes) | ||
| + | _pub_file() { | ||
| + | [ -f " | ||
| + | local args=(-h " | ||
| + | [ " | ||
| + | mosquitto_pub " | ||
| + | } | ||
| + | |||
| + | _dbg() { _pub_event " | ||
| + | |||
| + | ###################################### | ||
| + | # Startup cleanup: purge any old GPU retained topics/ | ||
| + | # Unused, kept as an example for clearing out entities | ||
| + | ###################################### | ||
| + | _clear_legacy_gpu_topics() { | ||
| + | local ID=" | ||
| + | # HA discovery (GPU) | ||
| + | for t in \ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | mosquitto_pub -h " | ||
| + | done | ||
| + | # State topics (GPU) | ||
| + | for t in gpu_temp gpu_temp_source gpu_temp_status; | ||
| + | mosquitto_pub -h " | ||
| + | done | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Converters: dwebp | magick | convert | ffmpeg | ||
| + | ###################################### | ||
| + | _convert_webp_to_png() { | ||
| + | local in=" | ||
| + | out=" | ||
| + | if command -v dwebp >/ | ||
| + | echo " | ||
| + | return 0 | ||
| + | }; fi | ||
| + | if command -v magick >/ | ||
| + | echo " | ||
| + | return 0 | ||
| + | }; fi | ||
| + | if command -v convert >/ | ||
| + | echo " | ||
| + | return 0 | ||
| + | }; fi | ||
| + | if command -v ffmpeg >/ | ||
| + | echo " | ||
| + | return 0 | ||
| + | }; fi | ||
| + | return 1 | ||
| + | } | ||
| + | _maybe_convert_to_png() { | ||
| + | local in=" | ||
| + | case " | ||
| + | webp) | ||
| + | local out | ||
| + | out=" | ||
| + | _dbg " | ||
| + | echo " | ||
| + | return 0 | ||
| + | } | ||
| + | _dbg " | ||
| + | echo " | ||
| + | ;; | ||
| + | *) echo " | ||
| + | esac | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Media discovery (box-art + marquee) | ||
| + | ###################################### | ||
| + | _find_game_image() { | ||
| + | local sys=" | ||
| + | base=" | ||
| + | stem=" | ||
| + | name=" | ||
| + | # 1) roms/< | ||
| + | for ext in webp png jpg jpeg JPG JPEG PNG; do | ||
| + | for candidate in \ | ||
| + | "/ | ||
| + | "/ | ||
| + | "/ | ||
| + | "/ | ||
| + | done | ||
| + | # 2) downloaded_images/< | ||
| + | for root in "/ | ||
| + | [ -d " | ||
| + | for ext in webp png jpg jpeg JPG JPEG PNG; do | ||
| + | for candidate in \ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | done | ||
| + | done | ||
| + | # 3) < | ||
| + | local gl="/ | ||
| + | if [ -f " | ||
| + | p=" | ||
| + | if [ -n " | ||
| + | line=" | ||
| + | [ -n " | ||
| + | case " | ||
| + | [ -f " | ||
| + | fi | ||
| + | fi | ||
| + | return 1 | ||
| + | } | ||
| + | _find_marquee_image() { | ||
| + | local sys=" | ||
| + | base=" | ||
| + | stem=" | ||
| + | name=" | ||
| + | # 1) roms/< | ||
| + | for ext in webp png jpg jpeg JPG JPEG PNG; do | ||
| + | for candidate in \ | ||
| + | "/ | ||
| + | "/ | ||
| + | "/ | ||
| + | "/ | ||
| + | "/ | ||
| + | "/ | ||
| + | done | ||
| + | # 2) downloaded_images/< | ||
| + | for root in "/ | ||
| + | [ -d " | ||
| + | for ext in webp png jpg jpeg JPG JPEG PNG; do | ||
| + | for candidate in \ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | done | ||
| + | done | ||
| + | # 3) < | ||
| + | local gl="/ | ||
| + | if [ -f " | ||
| + | p=" | ||
| + | if [ -n " | ||
| + | for tag in marquee thumbnail; do | ||
| + | line=" | ||
| + | [ -n " | ||
| + | case " | ||
| + | [ -f " | ||
| + | done | ||
| + | fi | ||
| + | fi | ||
| + | return 1 | ||
| + | } | ||
| + | |||
| + | _publish_file_variants() { | ||
| + | local kind=" | ||
| + | [ -f " | ||
| + | _dbg " | ||
| + | return 1 | ||
| + | } | ||
| + | ext=" | ||
| + | if [ " | ||
| + | local conv | ||
| + | conv=" | ||
| + | fp=" | ||
| + | fi | ||
| + | _pub_event " | ||
| + | _pub_file " | ||
| + | if [ " | ||
| + | base64 " | ||
| + | -t " | ||
| + | fi | ||
| + | } | ||
| + | |||
| + | _publish_media_for_selection() { | ||
| + | local sys=" | ||
| + | fp=" | ||
| + | fp=" | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Core publish | ||
| + | ###################################### | ||
| + | publish() { | ||
| + | local subtopic=" | ||
| + | shift | ||
| + | local data ts | ||
| + | data=$(_json_array_from_args " | ||
| + | ts=$(_ts) | ||
| + | _pub " | ||
| + | case " | ||
| + | emulationstation/ | ||
| + | local rom_path rom_name | ||
| + | if [ " | ||
| + | rom_path=$(jq -r '.[0] // empty' <<<" | ||
| + | rom_name=$(jq -r '.[1] // .[0]' <<<" | ||
| + | else | ||
| + | rom_path=" | ||
| + | rom_name=" | ||
| + | fi | ||
| + | [ -n " | ||
| + | _pub_str " | ||
| + | ;; | ||
| + | emulationstation/ | ||
| + | _pub_str " | ||
| + | _pub_str " | ||
| + | ;; | ||
| + | emulationstation/ | ||
| + | local sys | ||
| + | if [ " | ||
| + | [ -n " | ||
| + | ;; | ||
| + | emulationstation/ | ||
| + | local sys romp rom_name ts_local | ||
| + | ts_local=" | ||
| + | if [ " | ||
| + | sys=$(jq -r '.[0] // empty' <<<" | ||
| + | romp=$(jq -r '.[1] // empty' <<<" | ||
| + | rom_name=$(jq -r '.[2] // .[1] // .[0]' <<<" | ||
| + | else | ||
| + | sys=" | ||
| + | romp=" | ||
| + | rom_name=" | ||
| + | fi | ||
| + | [ -n " | ||
| + | [ -n " | ||
| + | if [ " | ||
| + | _pub_event " | ||
| + | else | ||
| + | _pub_event " | ||
| + | fi | ||
| + | _pub_event " | ||
| + | _pub_event " | ||
| + | _publish_media_for_selection " | ||
| + | ;; | ||
| + | game/ | ||
| + | local sys | ||
| + | if [ " | ||
| + | [ -n " | ||
| + | _pub_str " | ||
| + | ;; | ||
| + | esac | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # HA MQTT Discovery (images + CPU telemetry only) | ||
| + | ###################################### | ||
| + | ha_discover() { | ||
| + | local ID=" | ||
| + | |||
| + | # CPU/system sensors | ||
| + | declare -A SENS=([cpu_temp]=" | ||
| + | for key in cpu_temp cpu_load ram_used disk_used uptime; do | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | done | ||
| + | |||
| + | # Diagnostics for CPU temp | ||
| + | for diag in cpu_temp_source cpu_temp_status; | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | done | ||
| + | |||
| + | # Now Playing / System / Playing | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | |||
| + | # Buttons | ||
| + | for cmd in reboot shutdown; do | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | done | ||
| + | |||
| + | # Images | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | _pub " | ||
| + | "{ | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | \" | ||
| + | }" | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # CPU temperature | ||
| + | ###################################### | ||
| + | CPU_TEMP_SAMPLE_COUNT=3 | ||
| + | CPU_TEMP_SAMPLE_DELAY_MS=150 | ||
| + | CPU_TEMP_STICKY_SECONDS=600 | ||
| + | CPU_TEMP_MIN_VALID=-40 | ||
| + | CPU_TEMP_MAX_VALID=125 | ||
| + | |||
| + | _norm_celsius() { | ||
| + | local v=" | ||
| + | [ -z " | ||
| + | v=" | ||
| + | case " | ||
| + | if [ " | ||
| + | } | ||
| + | _is_valid_temp() { | ||
| + | local c=" | ||
| + | [ -z " | ||
| + | [ " | ||
| + | [ " | ||
| + | return 0 | ||
| + | } | ||
| + | _sleep_ms() { usleep "$(($1 * 1000))" | ||
| + | |||
| + | _resolve_coretemp_path() { | ||
| + | # Use cached path if still valid | ||
| + | if [ -f " | ||
| + | local p | ||
| + | p=" | ||
| + | [ -n " | ||
| + | fi | ||
| + | # Scan for ' | ||
| + | for h in / | ||
| + | [ -f " | ||
| + | local name | ||
| + | name=" | ||
| + | case " | ||
| + | local first="" | ||
| + | for n in " | ||
| + | [ -f " | ||
| + | [ -z " | ||
| + | local lblfile=" | ||
| + | [ -f " | ||
| + | if echo " | ||
| + | echo " | ||
| + | return 0 | ||
| + | fi | ||
| + | done | ||
| + | if [ -n " | ||
| + | echo " | ||
| + | return 0 | ||
| + | fi | ||
| + | ;; | ||
| + | esac | ||
| + | done | ||
| + | return 1 | ||
| + | } | ||
| + | |||
| + | _read_coretemp_once() { | ||
| + | local p | ||
| + | p=" | ||
| + | local raw | ||
| + | raw=" | ||
| + | local c | ||
| + | c=" | ||
| + | echo " | ||
| + | return 0 | ||
| + | } | ||
| + | |||
| + | _sample_coretemp_median() { | ||
| + | local n=" | ||
| + | local vals=() i=0 v | ||
| + | while [ $i -lt " | ||
| + | v=" | ||
| + | _sleep_ms " | ||
| + | i=$((i + 1)) | ||
| + | continue | ||
| + | } | ||
| + | vals+=(" | ||
| + | i=$((i + 1)) | ||
| + | [ $i -lt " | ||
| + | done | ||
| + | [ " | ||
| + | IFS=$' | ||
| + | unset IFS | ||
| + | local mid=$(((${# | ||
| + | echo " | ||
| + | return 0 | ||
| + | } | ||
| + | |||
| + | _publish_cpu_temp() { | ||
| + | local now c last_c last_src last_ts sticky_src sticky_until | ||
| + | now=" | ||
| + | [ -f " | ||
| + | |||
| + | c=" | ||
| + | |||
| + | if _is_valid_temp " | ||
| + | _pub_str " | ||
| + | _pub_str " | ||
| + | _pub_str " | ||
| + | local until=$((now + CPU_TEMP_STICKY_SECONDS)) | ||
| + | echo " | ||
| + | else | ||
| + | if _is_valid_temp " | ||
| + | _pub_str " | ||
| + | _pub_str " | ||
| + | _pub_str " | ||
| + | else | ||
| + | _dbg " | ||
| + | fi | ||
| + | fi | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Command listener | ||
| + | ###################################### | ||
| + | listen_commands() { | ||
| + | mosquitto_sub -h " | ||
| + | -t " | ||
| + | case " | ||
| + | reboot) reboot ;; | ||
| + | shutdown) shutdown -h now ;; | ||
| + | esac | ||
| + | done & | ||
| + | CMD_PID=$! | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Telemetry loop | ||
| + | ###################################### | ||
| + | telemetry_loop() { | ||
| + | [ " | ||
| + | while true; do | ||
| + | _publish_cpu_temp | ||
| + | |||
| + | # CPU load (1min) | ||
| + | [ -r / | ||
| + | |||
| + | # RAM used (MB) | ||
| + | if [ -r / | ||
| + | local mt ma | ||
| + | mt=" | ||
| + | ma=" | ||
| + | [ -n " | ||
| + | fi | ||
| + | |||
| + | # Disk used (% of / | ||
| + | if df -P /userdata >/ | ||
| + | local du | ||
| + | du=" | ||
| + | [ -n " | ||
| + | fi | ||
| + | |||
| + | # Uptime (s) | ||
| + | [ -r / | ||
| + | |||
| + | sleep " | ||
| + | done & | ||
| + | TEL_PID=$! | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Install / Uninstall ES hooks | ||
| + | ###################################### | ||
| + | install() { | ||
| + | mkdir -p / | ||
| + | for event in " | ||
| + | local script_dir="/ | ||
| + | mkdir -p " | ||
| + | local script=" | ||
| + | if [ ! -f " | ||
| + | cat >" | ||
| + | # | ||
| + | event_name=" | ||
| + | / | ||
| + | EOF | ||
| + | sed -i " | ||
| + | chmod +x " | ||
| + | fi | ||
| + | done | ||
| + | |||
| + | mkdir -p / | ||
| + | local game_script="/ | ||
| + | if [ ! -f " | ||
| + | cat >" | ||
| + | # | ||
| + | # Args: gameStart|gameStop system emulator core rompath | ||
| + | / | ||
| + | EOF | ||
| + | sed -i " | ||
| + | chmod +x " | ||
| + | fi | ||
| + | } | ||
| + | uninstall() { | ||
| + | for event in " | ||
| + | local script_dir="/ | ||
| + | local script=" | ||
| + | [ -f " | ||
| + | [ -d " | ||
| + | done | ||
| + | rm -f "/ | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Process Control | ||
| + | ###################################### | ||
| + | getMPID() { | ||
| + | X=$(cat " | ||
| + | test -z " | ||
| + | if test -e "/ | ||
| + | echo " | ||
| + | return 0 | ||
| + | fi | ||
| + | echo "" | ||
| + | return 1 | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Start / Stop | ||
| + | ###################################### | ||
| + | start() { | ||
| + | P=$(getMPID) | ||
| + | if test -n " | ||
| + | kill " | ||
| + | fi | ||
| + | echo $$ >" | ||
| + | install | ||
| + | _pub_str " | ||
| + | publish " | ||
| + | ha_discover | ||
| + | listen_commands | ||
| + | telemetry_loop | ||
| + | } | ||
| + | stop() { | ||
| + | P=$(getMPID) | ||
| + | if test -n " | ||
| + | kill " | ||
| + | fi | ||
| + | /bin/rm " | ||
| + | publish " | ||
| + | [ -n " | ||
| + | [ -n " | ||
| + | _pub_str " | ||
| + | } | ||
| + | |||
| + | ###################################### | ||
| + | # Entrypoint | ||
| + | ###################################### | ||
| + | if [ $# -eq 0 ]; then | ||
| + | echo " | ||
| + | exit 1 | ||
| + | fi | ||
| + | case " | ||
| + | publish) | ||
| + | shift | ||
| + | publish " | ||
| + | ;; | ||
| + | start) start ;; | ||
| + | stop) stop ;; | ||
| + | install) install ;; | ||
| + | uninstall) uninstall ;; | ||
| + | *) | ||
| + | echo " | ||
| + | exit 1 | ||
| + | ;; | ||
| + | esac | ||
| + | </ | ||
| ===== Control Batocera with a Logitech Harmony remote via Home Assistant (HA) ===== | ===== Control Batocera with a Logitech Harmony remote via Home Assistant (HA) ===== | ||
| Line 181: | Line 1041: | ||
| That's it! Oh boy, how cool is that? You are now able to power on/off your Batocera system with just one click from your Logitech Harmony remote control. Feel free to integrate the Roku 4 device into your Logitech Harmony activities so you can power on/off all of your devices (e.g. TV, AV receiver, amplifier, HTPC, ...) with just one click with a single activity. | That's it! Oh boy, how cool is that? You are now able to power on/off your Batocera system with just one click from your Logitech Harmony remote control. Feel free to integrate the Roku 4 device into your Logitech Harmony activities so you can power on/off all of your devices (e.g. TV, AV receiver, amplifier, HTPC, ...) with just one click with a single activity. | ||
| + | |||
| + | === Advanced: Boot automatically into OSes on multiboot systems === | ||
| + | |||
| + | In case you got Batocera set up as a [[dual_boot_ubuntu_batocera.linux|dual- or multiboot]] system, you may be interested on how to set up PXE-boot (network boot) on your Batocera PC so you can boot automatically into one of the according installed OSes on your PC, based on Home Assistant automations. | ||
| + | To accomplish this, please refer to [[https:// | ||
- homeautomation.1672752551.txt.gz
- Last modified: 3 years ago
- by grandmabetty