Last active
March 9, 2025 00:04
-
-
Save ristomatti/bc15496ee252f243c69aee5783edf077 to your computer and use it in GitHub Desktop.
Rofi / terminal scripts for Home Assistant
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# Rofi / terminal interface to interact with Home Assistant LLM conversation API. | |
# | |
# Requirements | |
# - xh: https://github.com/ducaale/xh | |
# - jq: https://stedolan.github.io/jq/ | |
# - rofi: https://github.com/davatorium/rofi | |
# | |
# Resources | |
# - REST API: https://developers.home-assistant.io/docs/api/rest/ | |
# | |
# Copyright (c) 2025 Ristomatti Airo <[email protected]> | |
# | |
# This work is licensed under the terms of the MIT license. | |
# For a copy, see <https://opensource.org/licenses/MIT>. | |
set -Eeuo pipefail | |
trap cleanup SIGINT SIGTERM ERR EXIT | |
# Log file (optional) | |
LOG_FILE="/tmp/hass-assist.log" | |
function cleanup() { | |
trap - SIGINT SIGTERM ERR EXIT | |
} | |
cd "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" | |
source hass-utils.sh | |
utils::init_environment | |
messages= | |
function rofi_hass_assist() { | |
local conversation_id="${1:-}" response_text="${2:-}" | |
local input_text response_body | |
input_text="$(read_input "${response_text}")" | |
if [[ -z "$input_text" ]]; then | |
return 0 | |
fi | |
if [[ -z "$messages" ]]; then | |
messages=$(printf '<span font_weight="ultrabold">> %s</span>' "$input_text") | |
else | |
messages=$(printf '%s\n\n<span font_weight="ultrabold">> %s</span>' "$messages" "$input_text") | |
fi | |
if [[ -n "$conversation_id" ]]; then | |
response_body=$( | |
utils::ha_post /api/conversation/process \ | |
"language=${HA_ASSISTANT_LANG}" \ | |
"agent_id=${HA_ASSISTANT_ID}" \ | |
"conversation_id=${conversation_id}" \ | |
"text=${input_text}" | |
) | |
else | |
response_body=$( | |
utils::ha_post /api/conversation/process \ | |
"language=${HA_ASSISTANT_LANG}" \ | |
"agent_id=${HA_ASSISTANT_ID}" \ | |
"text=${input_text}" | |
) | |
fi | |
conversation_id=$(jq --raw-output '.conversation_id' <<< "$response_body") | |
response_text=$(jq --raw-output '.response.speech.plain.speech' <<< "$response_body") | |
if [[ -n "$response_text" ]]; then | |
if utils::is_terminal; then | |
echo -e "${response_text}\n" | |
else | |
messages=$(printf '%s\n%s' "$messages" "$response_text") | |
fi | |
fi | |
rofi_hass_assist "$conversation_id" | |
} | |
function read_input() { | |
local response_text="${1:-}" | |
if utils::is_terminal; then | |
read -p "> " -r input_text | |
echo "$input_text" | |
elif [[ -n "$messages" ]]; then | |
rofi -dmenu -i -p "Request" -markup -l 0 -mesg "$(echo -ne "${messages}")" | |
else | |
rofi -dmenu -i -p "Request" -l 0 | |
fi | |
} | |
rofi_hass_assist |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# Rofi / terminal interface to activate Home Assistant scenes. | |
# | |
# Requirements | |
# - xh: https://github.com/ducaale/xh | |
# - jq: https://stedolan.github.io/jq/ | |
# - rofi: https://github.com/davatorium/rofi | |
# - fzf: https://github.com/junegunn/fzf | |
# | |
# Resources | |
# - REST API: https://developers.home-assistant.io/docs/api/rest/ | |
# | |
# Copyright (c) 2025 Ristomatti Airo <[email protected]> | |
# | |
# This work is licensed under the terms of the MIT license. | |
# For a copy, see <https://opensource.org/licenses/MIT>. | |
set -Eeuo pipefail | |
trap cleanup SIGINT SIGTERM ERR EXIT | |
# Log file (optional) | |
LOG_FILE="/tmp/hass-scene.log" | |
function cleanup() { | |
trap - SIGINT SIGTERM ERR EXIT | |
} | |
cd "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" | |
source hass-utils.sh | |
function rofi_hass_scene() { | |
local entity_id="${1:-}" | |
utils::init_environment | |
if [[ -n "${entity_id}" ]]; then | |
activate_scene "${entity_id}" | |
else | |
activate_scene "$(select_scene)" | |
fi | |
} | |
function activate_scene() { | |
local entity_id="${1}" | |
if [[ ! "${entity_id}" == "null" ]] && [[ -n "${entity_id}" ]]; then | |
utils::log "Activating scene: ${entity_id}" | |
utils::ha_post "/api/services/scene/turn_on" "entity_id=${entity_id}" &> /dev/null & | |
fi | |
} | |
function select_scene() { | |
local scenes entity_id | |
scenes=$( | |
utils::ha_get "/api/states" | \ | |
jq '[ .[] | select(.entity_id | contains("scene.")) | |
| { entity_id, friendly_name: .attributes.friendly_name } | |
] | sort_by(.friendly_name)' | |
) | |
if ! utils::is_terminal; then | |
rofi_scene_picker "${scenes}" | |
else | |
fzf_scene_picker "${scenes}" | |
fi | |
} | |
function rofi_scene_picker() { | |
local scenes="${1}" scene_names friendly_name | |
scene_names=$( | |
jq -r 'map(.friendly_name) | sort[]' <<< "${scenes}" | |
) | |
friendly_name=$( | |
rofi \ | |
-dmenu \ | |
-normal-window \ | |
-sync -i \ | |
-p "Scene" <<< "${scene_names}" | |
) | |
jq --raw-output --arg name "$friendly_name" ' | |
.[] | select(.friendly_name == $name) | .entity_id | |
' <<< "${scenes}" | |
} | |
function fzf_scene_picker() { | |
local scenes="${1}" scene_headers scene_rows | |
scene_headers="Friendly name;Entity ID" | |
scene_rows=$( | |
jq -r '.[] | [.friendly_name, .entity_id] | join(";")' <<< "${scenes}" | |
) | |
column -t -s ";" <<< "$(printf "%s\n%s" "${scene_headers}" "${scene_rows}")" | \ | |
fzf \ | |
--with-nth 1,2 \ | |
--header-lines 1 \ | |
--delimiter ' +' \ | |
--height 15 \ | |
--reverse \ | |
--bind 'enter:become(echo {2})' | |
} | |
rofi_hass_scene "$@" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Utility functions for Home Assistant scripts. | |
# | |
# Copyright (c) 2025 Ristomatti Airo <[email protected]> | |
# | |
# This work is licensed under the terms of the MIT license. | |
# For a copy, see <https://opensource.org/licenses/MIT>. | |
# Read and validate environment variables | |
function utils::init_environment() { | |
if [ -z "${HA_BASE_URL:-}" ] || [ -z "${HA_API_TOKEN:-}" ]; then | |
utils::source_dotenv | |
fi | |
utils::validate_env_vars | |
} | |
# Read environment variables from .env, if it exists. | |
function utils::source_dotenv() { | |
if [[ -f .env ]]; then | |
set -a; source .env; set +a | |
else | |
utils::die "Error: .env file not found" | |
fi | |
} | |
# Validate required environment variables are set. | |
function utils::validate_env_vars() { | |
if [[ -z "$HA_BASE_URL" ]] ; then | |
utils::die "Error: Environment variable HA_BASE_URL not set" | |
elif [[ -z "$HA_API_TOKEN" ]]; then | |
utils::die "Error: Environment variable HA_API_TOKEN not set" | |
fi | |
} | |
# Send GET request to Home Assistant API | |
function utils::ha_get() { | |
local uri="$1" | |
xh --auth-type bearer --auth "$HA_API_TOKEN" get "${HA_BASE_URL}${uri}" | |
} | |
# Send POST request to Home Assistant API | |
function utils::ha_post() { | |
local uri="$1"; shift | |
local -a body=( "$@" ) | |
xh --ignore-stdin --auth-type bearer --auth "$HA_API_TOKEN" post "${HA_BASE_URL}${uri}" "${body[@]}" | |
} | |
# Log and print message | |
function utils::log() { | |
local -r message="${1:-}" | |
if [[ -n "$LOG_FILE" ]]; then | |
echo >&2 -e "$message" >> "$LOG_FILE" | |
fi | |
echo "$message" 1>&2 | |
} | |
function utils::is_terminal() { | |
test -t 0 | |
} | |
# Log error message and exit. | |
function utils::die() { | |
local error_msg="$1" | |
local code=${2:-1} # default exit status 1 | |
if [ -n "$error_msg" ]; then | |
utils::log "$error_msg" | |
fi | |
exit "$code" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example
.env
file: