#!/usr/bin/env bash

# WildlifeSystems - onboard sensors
#
# Script to read onboard sensors from a Raspberry Pi (onboard CPU and GPU) as
# well as a number of pseudo-sensors (storage used, memory used) that are useful
# for monitoring the health of the device.
#
# Reading JSON is produced by ws-emit, so this driver emits exactly what the C
# drivers emit: the same sc-prototype template, the same unit spellings, and
# "{{node}}" resolved to the node's own position rather than passed through
# unresolved. Everything below is only about reading the values.
#
# This script is part of the WildlifeSystems project. For further information
# please refer to https://docs.wildlife.systems, or for more information on
# the project itself, please refer to https://wildlife.systems.

set -u
set -o pipefail

readonly EXIT_INVALID_ARG=20
readonly EXIT_IDENTIFY=60
readonly DEVICE=raspberry_pi

usage() {
  printf 'Usage: sensor-onboard [--version|identify|list|internal|external|all|%s]\n' \
    'onboard_gpu|onboard_cpu|storage_used|memory_used' >&2
}

# Default to "all", as the C drivers do, rather than failing on no argument.
WHAT="all"

if [[ "$#" -ge 1 ]]; then
  case "$1" in
    identify)
      exit "${EXIT_IDENTIFY}"
      ;;
    list)
      printf '%s\n' "onboard_gpu" "onboard_cpu" "storage_used" "memory_used"
      exit 0
      ;;
    --version|-v|version)
      ws-emit --version
      exit 0
      ;;
    external)
      # Every onboard sensor is internal, so external is always empty.
      printf '[]\n'
      exit 0
      ;;
    internal|all)
      WHAT="all"
      ;;
    onboard_gpu|onboard_cpu|storage_used|memory_used)
      WHAT="$1"
      ;;
    *)
      printf 'Unknown command: %s\n' "$1" >&2
      usage
      exit "${EXIT_INVALID_ARG}"
      ;;
  esac
fi

PI_SERIAL=$(grep Serial /proc/cpuinfo | cut -d ' ' -f 2)

# Each reading becomes one "--" group of arguments to ws-emit, so all of them
# are emitted by a single call and ws-emit owns the array and its commas.
ARGS=()

if [[ "${WHAT}" == "onboard_gpu" || "${WHAT}" == "all" ]]; then
  # A Pi without vcgencmd has no GPU temperature to report, so the reading is
  # omitted rather than reported as an error.
  if GPU_TEMP=$(vcgencmd measure_temp 2>/dev/null | grep -Eo "[0-9]+\.[0-9]+"); then
    ARGS+=(-- --sensor onboard_gpu --measures temperature --unit celsius
           --value "${GPU_TEMP}" --precision 1 --location '{{node}}')
  fi
fi

if [[ "${WHAT}" == "onboard_cpu" || "${WHAT}" == "all" ]]; then
  # Integer division, so whole degrees only: precision 0 rather than implying
  # a tenth of a degree this value does not have.
  CPU_TEMP=$(($(< /sys/class/thermal/thermal_zone0/temp) / 1000))
  ARGS+=(-- --sensor onboard_cpu --measures temperature --unit celsius
         --value "${CPU_TEMP}" --precision 0 --location '{{node}}')
fi

if [[ "${WHAT}" == "storage_used" || "${WHAT}" == "all" ]]; then
  if df --local 2>/dev/null | grep -q /dev/root; then
    PC_USED=$(df --local | grep /dev/root | tr -s ' ' | cut -d ' ' -f5 | tr -d '%')
  else
    PC_USED=$(df --local | grep '/$' | tr -s ' ' | cut -d ' ' -f5 | tr -d '%')
  fi
  ARGS+=(-- --sensor storage_used --measures storage --unit percentage
         --value "${PC_USED}" --precision 0 --location '{{none}}')
fi

if [[ "${WHAT}" == "memory_used" || "${WHAT}" == "all" ]]; then
  read -r MEM_USED MEM_AVAI < <(free | awk '/Mem:/ {print $3, $2}')
  PC_USED=$(( 100 * MEM_USED / MEM_AVAI ))
  ARGS+=(-- --sensor memory_used --measures memory --unit percentage
         --value "${PC_USED}" --precision 0 --location '{{none}}')
fi

if [[ "${#ARGS[@]}" -eq 0 ]]; then
  printf 'Error: Sensor not found\n' >&2
  exit "${EXIT_INVALID_ARG}"
fi

exec ws-emit --device "${DEVICE}" --internal --prefix "${PI_SERIAL}" "${ARGS[@]}"
