summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--functions.sh46
-rwxr-xr-xget_lights.sh17
-rwxr-xr-xregister_app.sh45
-rwxr-xr-xset.sh64
-rw-r--r--signify_root_cert.pem14
6 files changed, 188 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..63f322d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+my_bridge.txt
+lights.txt
diff --git a/functions.sh b/functions.sh
new file mode 100644
index 0000000..2c21a41
--- /dev/null
+++ b/functions.sh
@@ -0,0 +1,46 @@
+# This file is meant to be `source`d.
+
+function read_vars() {
+ local key value
+ while read -r line; do
+ key=${line%%=*}
+ value=${line#*=}
+ if [[ $key = ip ]]; then bridge_ip=$value
+ elif [[ $key = bridgeid ]]; then bridge_id=$value
+ elif [[ $key = username ]]; then bridge_username=$value
+ fi
+ done <my_bridge.txt
+}
+
+# arguments:
+# - API v2 path: "resource/light" for /clip/v2/resource/light
+# - (optional) HTTP method (default GET)
+# - (optional) data to send as HTTP body
+function hue_request() {
+ local path=$1 method=GET body=''
+ [[ -z $path ]] && { echo >&2 "functions.sh(hue_request): path required"; exit 1; }
+ [[ $# -ge 2 ]] && method=$2
+ [[ $# -ge 3 ]] && body=$3
+ [[ $# -ge 4 ]] && { echo >&2 "functions.sh(hue_request): too many arguments"; exit 1; }
+
+ local data_args=()
+ if [[ -n $body ]]; then
+ data_args=( -d "$body" )
+ fi
+
+ json=$(
+ curl -s --cacert signify_root_cert.pem \
+ --resolve "$bridge_id:443:$bridge_ip" -H "hue-application-key: $bridge_username" \
+ -X "$method" "${data_args[@]}" \
+ https://"$bridge_id"/clip/v2/"$path"
+ )
+
+ errors=$(jq -r 'if has("errors") then .errors.[] | "- " + .description else empty end' <<<"$json")
+ if [[ -n $errors ]]; then
+ echo >&2 "Errors from Hue bridge:"
+ echo >&2 "$errors"
+ exit 1
+ fi
+
+ echo "$json"
+}
diff --git a/get_lights.sh b/get_lights.sh
new file mode 100755
index 0000000..e1274cf
--- /dev/null
+++ b/get_lights.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+source functions.sh
+
+read_vars
+
+listing=$(hue_request resource/light | jq -r '.data.[] | .id + " " + .metadata.name')
+
+test -t 1 && echo >&2 -e "\x1B[4m(Write this listing to <lights.txt>.)\x1B[0m"
+
+echo "$listing"
+
+if [[ ! -t 1 ]]; then
+ echo >&2 "$(wc -l <<<"$listing") lights written:"
+ sed >&2 's/^[^ ]*/-/' <<<"$listing"
+fi
diff --git a/register_app.sh b/register_app.sh
new file mode 100755
index 0000000..9cec53b
--- /dev/null
+++ b/register_app.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+source functions.sh
+
+application_name=tom-hue-stuff
+
+read_vars
+
+if [[ -z $var_ip || -z $var_bridgeid ]]; then
+ echo >&2 "Variables ip and bridgeid required in my_bridge.txt"
+ exit 1
+fi
+
+echo "Registering a new user on the Hue Bridge (using API V1)."
+echo "Application name: $application_name"
+
+read -r -p "Device name (e.g. iphone peter): " device_name
+
+if [[ -z $device_name ]]; then
+ echo >&2 "Device name required"
+ exit 1
+fi
+if [[ -n "$(echo "$device_name" | tr -d 'a-zA-Z90-9 _-')" ]]; then
+ echo >&2 "Invalid characters in device name"
+ exit 1
+fi
+
+echo "Now press the button on the Bridge, and press enter here."
+echo "Please press enter within 30 seconds of pressing the button."
+
+read
+
+reqdata=$(jq -c --arg app "$application_name" --arg dev "$device_name" '{"devicetype": ($app + "#" + $dev)}' <<<0)
+# reqdata=$(jq -c --arg app "$application_name" --arg dev "$device_name" '{}' <<<0)
+response=$(curl -s --cacert signify_root_cert.pem --resolve "$var_bridgeid:443:$var_ip" -d "$reqdata" https://"$var_bridgeid"/api)
+
+if [[ $(jq '.[0] | has("success")' <<<"$response") = true ]]; then
+ username=$(jq '.[0].success.username' <<<"$response")
+ echo "username=$username"
+else
+ echo "Registration failed:"
+ echo "$response"
+ exit 1
+fi
diff --git a/set.sh b/set.sh
new file mode 100755
index 0000000..3cd3551
--- /dev/null
+++ b/set.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+cd "$(dirname "$(realpath "$0")")"
+
+source functions.sh
+
+read_vars
+
+if [[ ! -f lights.txt ]]; then
+ echo >&2 'First run:'
+ echo >&2 '$ ./get_lights.sh >lights.txt'
+ exit 1
+fi
+
+if [[ $# -eq 0 || ($1 = "-h" || $1 = "--help") ]]; then
+ echo >&2 "Usage: $0 <light name> [commands...]"
+ echo >&2 "The light name is case-insensitive and may be any unambiguous substring of the full name."
+ echo >&2 "Commands:"
+ echo >&2 "- on: turn on the light"
+ echo >&2 "- off: turn off the light"
+ echo >&2 "- switch <n>: turn on if n=1, off if n=0"
+ echo >&2 "- br <n>: set brightness; n in [0, 100]"
+ [[ $# -eq 0 ]] && exit 1
+ exit 0
+fi
+
+namesubstr=$1
+
+matches=$(cut -d' ' -f2- lights.txt | grep -niF "$namesubstr" | cut -d: -f1 || true)
+[[ -z $matches ]] && { echo >&2 "Light not found"; exit 1; }
+[[ $(wc -l <<<"$matches") -gt 1 ]] && { echo >&2 "Multiple lights matched, name must be unambiguous"; exit 1; }
+lightid=$(sed -n "$matches s/ .*//p" <lights.txt)
+
+shift
+
+commandstr="{}"
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ on) commandstr="$commandstr * {on:{on:true}}"; shift ;;
+ off) commandstr="$commandstr * {on:{on:false}}"; shift ;;
+
+ switch)
+ [[ $# -lt 2 ]] && { echo >&2 "Not enough arguments to 'switch' command"; exit 1; }
+ if [[ $2 = '1' ]]; then commandstr="$commandstr * {on:{on:true}}"
+ elif [[ $2 = '0' ]]; then commandstr="$commandstr * {on:{on:false}}"
+ else echo >&2 "Invalid argument to 'switch' command"; exit 1
+ fi
+ shift 2
+ ;;
+
+ br)
+ [[ $# -lt 2 ]] && { echo >&2 "Not enough arguments to 'br' command"; exit 1; }
+ commandstr="$commandstr * {dimming:{brightness:$2}}"
+ shift 2
+ ;;
+
+ *)
+ echo >&2 "Unrecognised command: '$1'"
+ exit 1
+ esac
+done
+
+hue_request resource/light/"$lightid" PUT "$(jq "$commandstr" <<<0)" >/dev/null
diff --git a/signify_root_cert.pem b/signify_root_cert.pem
new file mode 100644
index 0000000..1cef2d7
--- /dev/null
+++ b/signify_root_cert.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICMjCCAdigAwIBAgIUO7FSLbaxikuXAljzVaurLXWmFw4wCgYIKoZIzj0EAwIw
+OTELMAkGA1UEBhMCTkwxFDASBgNVBAoMC1BoaWxpcHMgSHVlMRQwEgYDVQQDDAty
+b290LWJyaWRnZTAiGA8yMDE3MDEwMTAwMDAwMFoYDzIwMzgwMTE5MDMxNDA3WjA5
+MQswCQYDVQQGEwJOTDEUMBIGA1UECgwLUGhpbGlwcyBIdWUxFDASBgNVBAMMC3Jv
+b3QtYnJpZGdlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjNw2tx2AplOf9x86
+aTdvEcL1FU65QDxziKvBpW9XXSIcibAeQiKxegpq8Exbr9v6LBnYbna2VcaK0G22
+jOKkTqOBuTCBtjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNV
+HQ4EFgQUZ2ONTFrDT6o8ItRnKfqWKnHFGmQwdAYDVR0jBG0wa4AUZ2ONTFrDT6o8
+ItRnKfqWKnHFGmShPaQ7MDkxCzAJBgNVBAYTAk5MMRQwEgYDVQQKDAtQaGlsaXBz
+IEh1ZTEUMBIGA1UEAwwLcm9vdC1icmlkZ2WCFDuxUi22sYpLlwJY81Wrqy11phcO
+MAoGCCqGSM49BAMCA0gAMEUCIEBYYEOsa07TH7E5MJnGw557lVkORgit2Rm1h3B2
+sFgDAiEA1Fj/C3AN5psFMjo0//mrQebo0eKd3aWRx+pQY08mk48=
+-----END CERTIFICATE-----