Springe zum Hauptinhalt
t.animal
Anagraphein!
t.animal
Anagraphein!

Blog

Das ist kein Blog. Ich poste hier in unregelmäßigen Abständen Dinge.

Dreame L10S Ultra Po-Wackel-Daemon

Seit ich Staubsaugerroboter habe, lasse ich sie nur mit Valetudo und ohne cloud laufen. Mein neuer Dreame L10s Ultra ist da keine Ausnahme. Dadurch kann ich den Sauger komplett lokal per Website und Home-Assistant steuern.

Dieses Modell hat eine Besonderheit: Um an Kanten besser bis zur Wand zu wischen, hat es einen Po-Wackel-Modus bei dem der Roboter sich kurz dreht, um mit dem Mops besser bis zur Kante zu kommen. Das bringt deutlich bessere Wischergebnisse, aber dauert auch viel länger. Wir benötigen es nur in der Küche und im Bad, also wäre es schön, diesen Modus auch nur dort einzuschalten. Das gibt die Valetudo-API bzw die Roboterfirmware so leider nicht her.

Also habe ich ein kleines Script gebaut, das den Zustand des Roboters von der Valetudo-API ausliest, die aktuellen Koordinaten extrahiert und mit konfigurierten Räumen abgleicht. Beim Übergang in einen Raum lässt sich der Modus so aktivieren. Möglicherweise ist es ja noch für jemanden nützlich:

#!/bin/sh
#
# Toggle robot feature based on its current position and configured rooms.
# Log room transitions to /data/rooms.log
#
# Dependencies: curl, jq, busybox sh 
# jq best installed as statically built binary using soar https://soar.qaidvoid.dev/
#


# 
# Run by adding this to /data/_root_postboot.sh
#
# if [[ -x /data/room-specific-quirks.sh ]]; then 
#   nohup watch -n 3 /data/room-specific-quirks.sh > /dev/null 2>&1 &
# fi


# ==============================
# Configuration
# ==============================

# Inline JSON room configuration
ROOMS_JSON='
{
  "rooms": [
    { "name": "Kitchen", "min_x": 3795, "max_x": 4185, "min_y": 3100, "max_y": 3390 },
    { "name": "Shower",  "min_x": 3935, "max_x": 4191, "min_y": 2930, "max_y": 3093 }
  ]
}
'

# Quirk ID
QUIRK_ID="7c71db1b-72b6-402e-89a4-d66c72cb9c8c"

# API endpoint
API_BASE="http://localhost/api/v2/robot"

# Log file
LOG_FILE="/data/rooms.log"

PATH="/data/soar/bin:$PATH"

# ==============================
# Helper functions
# ==============================

enable_feature() {
  curl --silent -X PUT \
    "$API_BASE/capabilities/QuirksCapability" \
    -H 'accept: */*' \
    -H 'Content-Type: application/json' \
    -d "{
      \"id\": \"$QUIRK_ID\",
      \"value\": \"each_cleanup\"
    }" >/dev/null
}

disable_feature() {
  curl --silent -X PUT \
    "$API_BASE/capabilities/QuirksCapability" \
    -H 'accept: */*' \
    -H 'Content-Type: application/json' \
    -d "{
      \"id\": \"$QUIRK_ID\",
      \"value\": \"off\"
    }" >/dev/null
}

is_within_room() {
  x=$1
  y=$2
  min_x=$3
  max_x=$4
  min_y=$5
  max_y=$6

  [ "$x" -ge "$min_x" ] && [ "$x" -le "$max_x" ] && \
  [ "$y" -ge "$min_y" ] && [ "$y" -le "$max_y" ]
}


# ==============================
# Main logic
# ==============================

main() {
  # Fetch robot state once
  state=$(curl --silent "$API_BASE/state")

  # Extract position
  coords=$(echo "$state" | jq '.map.entities[] | select(.type=="robot_position") | .points')
  x=$(echo "$coords" | jq '.[0]')
  y=$(echo "$coords" | jq '.[1]')

  # Extract operation mode
  mode=$(echo "$state" | jq -r '.attributes[] | select(.type=="operation_mode") | .value')

  # Default room
  current_room="None"

  # Determine current room
  echo "$ROOMS_JSON" | jq -c '.rooms[]' | while read -r room; do
    name=$(echo "$room" | jq -r '.name')
    min_x=$(echo "$room" | jq -r '.min_x')
    max_x=$(echo "$room" | jq -r '.max_x')
    min_y=$(echo "$room" | jq -r '.min_y')
    max_y=$(echo "$room" | jq -r '.max_y')

    if is_within_room "$x" "$y" "$min_x" "$max_x" "$min_y" "$max_y"; then
      echo "$name"
      exit 0
    fi
  done > /tmp/current_room.$$

  if [ -s /tmp/current_room.$$ ]; then
    current_room=$(cat /tmp/current_room.$$)
  fi
  rm -f /tmp/current_room.$$

  # Get last room from log
  last_room="None"
  if [ -f "$LOG_FILE" ]; then
    last_room=$(tail -n 1 "$LOG_FILE" | awk -F'\t' '{print $2}')
  fi

  # Exit early if same room OR mode is "vacuum"
  if [ "$current_room" = "$last_room" ] || [ "$mode" = "vacuum" ]; then
    exit 0
  fi

  # Log change
  timestamp=$(date -Iseconds)
  echo -e "${timestamp}\t${current_room}" >>"$LOG_FILE"

  # Toggle feature based on room and mode
  if [ "$current_room" != "None" ] &&  [ "$mode" != "vacuum" ]; then
    enable_feature
  else
    disable_feature
  fi
}

main "$@"