Skip to content

Commit

Permalink
README & scripts: ruuvimetrics.sh
Browse files Browse the repository at this point in the history
Simple example how to feed Ruuvi data into Prometheus.
  • Loading branch information
susji committed Apr 7, 2024
1 parent b08f271 commit dab85fb
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,59 @@ $ mosquitto_sub \

For an example how to insert the data into a database, see the documentation for
[ruuviscan](https://github.com/susji/ruuviscan?tab=readme-ov-file#storing-temperature-values-in-an-sqlite-database).

# Grabbing Ruuvi data from MQTT and exposing it as Prometheus metrics

For a minimalistic example on how to expose Ruuvi values in Prometheus'
[text-based exposition
format](https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md)
see [scripts/ruuvimetrics.sh](scripts/ruuvimetrics.sh). The idea of the script
is this: We will again use `mosquitto_sub` to obtain Ruuvi messages, parse and
output them as JSON with `ruuviparse` and then generate a Prometheus-compatible
metrics file of the sensor values. In addition to this script, you will probably
need a way to serve the generated file over HTTP so Prometheus or a similar tool
can periodically read its contents. After this, it's trivial to consume the
sensor values with something like Grafana.

In the script, you will want to modify the broker settings and the `ACT`
variable to copy the generated metrics file to your intended `TARGETDIR`. The
resulting file might look like something like this:

```
# Generated at Sun Apr 7 14:36:07 UTC 2024 by ruuvimetrics.sh
# TYPE ruuvi_temperature gauge
ruuvi_temperature{mac="aa:aa:aa:aa:aa:aa"} 5.185 1712500560000
ruuvi_temperature{mac="bb:bb:bb:bb:bb:bb"} 2.615 1712500546000
ruuvi_temperature{mac="cc:cc:cc:cc:cc:cc"} 2.865 1712500567000
ruuvi_temperature{mac="dd:dd:dd:dd:dd:dd"} 22.289999 1712500567000
# TYPE ruuvi_voltage gauge
ruuvi_voltage{mac="aa:aa:aa:aa:aa:aa"} 2.929 1712500560000
ruuvi_voltage{mac="bb:bb:bb:bb:bb:bb"} 2.865 1712500546000
ruuvi_voltage{mac="cc:cc:cc:cc:cc:cc"} 2.943 1712500567000
ruuvi_voltage{mac="dd:dd:dd:dd:dd:dd"} 3.038 1712500567000
# TYPE ruuvi_movement gauge
ruuvi_movement{mac="aa:aa:aa:aa:aa:aa"} 183 1712500560000
ruuvi_movement{mac="bb:bb:bb:bb:bb:bb"} 125 1712500546000
ruuvi_movement{mac="cc:cc:cc:cc:cc:cc"} 88 1712500567000
ruuvi_movement{mac="dd:dd:dd:dd:dd:dd"} 154 1712500567000
```

For testing the idea, something like this will suffice to serve the resulting
metrics file over HTTP:

$ cd $TARGETDIR && python3 -m http.server 9200 --bind 127.0.0.1

If you think you need elevated privileges to run the script, you should instead
modify your target directory privileges accordingly. For some error-tolerance,
wrap the invocation in a loop like

```sh
while true; do
echo "begin $(date)"
./ruuvimetrics.sh
echo exited
sleep 10
done
```

or use some kind of a process manager.
86 changes: 86 additions & 0 deletions scripts/ruuvimetrics.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/bin/sh

set -u

BROKERHOST=broker.example.com
BROKERPORT=1883
TOPIC=ruuvi/topic
CLIENTID=ruuvimetrics
AUTH='--cert cert.cert.pem --key key.private.key --cafile ca.crt'
F=metrics.txt
# Change this according to your setup.
ACT="/bin/cp $HOME/ruuvimetrics/${F} /var/www/htdocs/metrics"

# Due to fromdateiso8601.
export TZ=UTC

val() {
_mac="$1"
_kind="$2"
_ok="$3"
_val="$4"
_tmpf="$5"
echo "[$_mac] $_kind: $_val ($_ok)"
_p="ruuvi_${_kind}"
if [ "$_ok" = "true" ]; then
# We have a new valid value so filter out the old one.
_m="{mac=\"$_mac\"}"
cat "$F" | fgrep "${_p}{mac=" | fgrep -v "$_m" > "$_tmpf"
echo "${_p}${_m} $_val $_ts" >> "$_tmpf"
else
# No new valid value so just copy all we have.
cat "$F" | fgrep "${_p}{mac=" > "$_tmpf"
fi
}

dump() {
_kind="$1"
_tmpf="$2"
{ echo "# TYPE ruuvi_${_kind} gauge"; cat "$_tmpf"; } | sort >> "$F"
}

activate() {
$ACT
}

rm -f "$F" && touch "$F"
mosquitto_sub \
-h "$BROKERHOST" \
-p "$BROKERPORT" \
-t "$TOPIC" \
-i "$CLIENTID" \
$AUTH \
| jq --raw-output --unbuffered \
'.ads[].ad | ascii_downcase | select(.[8:14] == "ff9904") | .[14:]' \
| ruuviparse \
| jq --raw-output --unbuffered \
'. + { "OriginalTimestamp": .Timestamp }
| .Timestamp |= .[:index(".")] + "Z"
| .Timestamp |= fromdateiso8601 * 1000
| . + {"CompareTimestamp": (.Timestamp / 1000 | todate) }
| [.OriginalTimestamp,
.Timestamp,
.MAC,
.Temperature.Valid,
.Temperature.Value,
.BatteryVoltage.Valid,
.BatteryVoltage.Value,
.MovementCounter.Valid,
.MovementCounter.Value]
| @tsv' \
| while read -r _ots _ts _mac _vtemp _temp _vvolt _volt _vmove _move; do
echo "[$_mac] $_ots"
_ttemp=$(mktemp)
_tvolt=$(mktemp)
_tmove=$(mktemp)
val "$_mac" "temperature" "$_vtemp" "$_temp" "$_ttemp"
val "$_mac" "voltage" "$_vvolt" "$_volt" "$_tvolt"
val "$_mac" "movement" "$_vmove" "$_move" "$_tmove"
rm -f "$F"
echo "# Generated at $(date) by $(basename "$0")" > "$F"
dump "temperature" "$_ttemp"
dump "voltage" "$_tvolt"
dump "movement" "$_tmove"
activate
rm -f "$_ttemp" "$_tvolt" "$_tmove"
done

0 comments on commit dab85fb

Please sign in to comment.