AgentStack
SKILL verified MIT Self-run

Ops Home

skill-lifecycle-innovations-limited-claude-ops-ops-home · by Lifecycle-Innovations-Limited

Smart home command center via Homey Pro. Devices, flows, scenes, energy, climate, presence, alarms. Works via Homey local API (preferred) + Athom cloud API fallback. Configure once via /ops:setup.

No reviews yet
0 installs
12 views
0.0% view→install

Install

$ agentstack add skill-lifecycle-innovations-limited-claude-ops-ops-home

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Ops Home? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

OPS ► HOME — Smart Home Command Center (Homey Pro)

Runtime Context

Before executing, load available context:

  1. Preferences: Read ${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json
  • timezone — display all timestamps in user's timezone
  • home_automation.homey_local_url — e.g. http://192.168.1.100 (preferred path, faster, no cloud dependency)
  • home_automation.homey_local_token — Personal Access Token for local API
  • home_automation.homey_cloud_token — Athom OAuth token for cloud fallback
  • home_automation.homey_id — Homey ID (cloud resolution)
  1. Daemon health: Read ${CLAUDE_PLUGIN_DATA_DIR}/daemon-health.json
  • If action_needed is not null → surface it before running any Homey operations
  • On any auth/connectivity failure in this skill, write action_needed back to daemon-health.json
  1. Secrets: Resolve Homey credentials via userConfig → env vars → Doppler → keychain (see Phase 1 below)

CLI/API Reference

Homey Pro Web API v3 — LOCAL (preferred)

Base URL: ${HOMEY_LOCAL_URL} (e.g. http://192.168.1.100)

| Endpoint | Method | Description | | ---------------------------------------------------------- | ------ | ----------------------------------------------------- | | /api/manager/devices/device | GET | List all devices | | /api/manager/devices/device/{id} | GET | Get one device with capabilities | | /api/manager/devices/device/{id}/capability/{capability} | PUT | Set capability (onoff, dim, target_temperature, etc.) | | /api/manager/flow/flow | GET | List all flows | | /api/manager/flow/flow/{id}/trigger | POST | Run a flow | | /api/manager/zones/zone | GET | List zones (rooms) | | /api/manager/energy/live | GET | Live power draw (watts) | | /api/manager/energy/report | GET | Historical energy report (kWh) | | /api/manager/presence | GET | Presence status (who is home) | | /api/manager/alarms/alarm | GET | Active alarms (smoke, water, security) | | /api/manager/system | GET | Homey system info (firmware, name, uptime) |

Auth header (local): Authorization: Bearer ${HOMEY_LOCAL_TOKEN}

Athom Cloud API — FALLBACK

Base URL: https://api.athom.com

| Endpoint | Method | Description | | ------------------------------------------ | ------ | ----------------- | | /v2/homey/${HOMEY_ID}/devices | GET | Devices via cloud | | /v2/homey/${HOMEY_ID}/flows | GET | Flows via cloud | | /v2/homey/${HOMEY_ID}/flows/{id}/trigger | POST | Trigger a flow | | /v2/homey/${HOMEY_ID}/zones | GET | Zones via cloud |

Auth header (cloud): Authorization: Bearer ${HOMEY_CLOUD_TOKEN}

Common capability strings (Homey)

onoff, dim (0.0–1.0), target_temperature, measure_temperature, measure_humidity, measure_power, meter_power, alarm_motion, alarm_smoke, alarm_water, alarm_contact, locked, windowcoverings_state, light_hue, light_saturation, volume_set.

Agent Teams support

If CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 is set, use Agent Teams for parallel hub probing:

TeamCreate("home-team")
Agent(team_name="home-team", name="devices-scanner", prompt="List all Homey devices, group by zone, return online/offline state and current capabilities")
Agent(team_name="home-team", name="flows-scanner", prompt="List all flows and their last-fired timestamps")
Agent(team_name="home-team", name="energy-scanner", prompt="Pull live power draw + today's kWh + top consumers, flag anomalies vs 7-day baseline")
Agent(team_name="home-team", name="presence-scanner", prompt="Return presence state and active alarms")

If the flag is NOT set, dispatch ops:home-agent as standard fire-and-forget subagents per scope (devices, flows, energy, presence, alarms).

Phase 1 — Resolve credentials

Resolve Homey credentials in this order. Local path is preferred (faster, works offline, lower latency):

PREFS_PATH="${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json"

# 1. Plugin userConfig (preferences.json)
HOMEY_LOCAL_URL=$(jq -r '.home_automation.homey_local_url // empty' "$PREFS_PATH" 2>/dev/null)
HOMEY_LOCAL_TOKEN=$(jq -r '.home_automation.homey_local_token // empty' "$PREFS_PATH" 2>/dev/null)
HOMEY_CLOUD_TOKEN=$(jq -r '.home_automation.homey_cloud_token // empty' "$PREFS_PATH" 2>/dev/null)
HOMEY_ID=$(jq -r '.home_automation.homey_id // empty' "$PREFS_PATH" 2>/dev/null)

# 2. Environment variables (override userConfig if set)
[ -n "$HOMEY_LOCAL_URL" ] || HOMEY_LOCAL_URL="${HOMEY_LOCAL_URL:-}"
[ -n "$HOMEY_LOCAL_TOKEN" ] || HOMEY_LOCAL_TOKEN="${HOMEY_LOCAL_TOKEN:-}"
[ -n "$HOMEY_CLOUD_TOKEN" ] || HOMEY_CLOUD_TOKEN="${HOMEY_CLOUD_TOKEN:-${HOMEY_ACCESS_TOKEN:-}}"
[ -n "$HOMEY_ID" ] || HOMEY_ID="${HOMEY_ID:-}"

# 3. Doppler fallback (project: homey-pro)
if [ -z "$HOMEY_LOCAL_TOKEN" ] && command -v doppler &>/dev/null; then
  HOMEY_LOCAL_TOKEN=$(doppler secrets get HOMEY_LOCAL_TOKEN --project homey-pro --plain 2>/dev/null)
fi
if [ -z "$HOMEY_CLOUD_TOKEN" ] && command -v doppler &>/dev/null; then
  HOMEY_CLOUD_TOKEN=$(doppler secrets get HOMEY_ACCESS_TOKEN --project homey-pro --plain 2>/dev/null)
fi
if [ -z "$HOMEY_LOCAL_URL" ] && command -v doppler &>/dev/null; then
  HOMEY_LOCAL_URL=$(doppler secrets get HOMEY_LOCAL_URL --project homey-pro --plain 2>/dev/null)
fi
if [ -z "$HOMEY_ID" ] && command -v doppler &>/dev/null; then
  HOMEY_ID=$(doppler secrets get HOMEY_ID --project homey-pro --plain 2>/dev/null)
fi

# 4. Keychain fallback
[ -z "$HOMEY_LOCAL_TOKEN" ] && HOMEY_LOCAL_TOKEN=$(security find-generic-password -s "homey-local-token" -w 2>/dev/null)
[ -z "$HOMEY_CLOUD_TOKEN" ] && HOMEY_CLOUD_TOKEN=$(security find-generic-password -s "homey-cloud-token" -w 2>/dev/null)

If neither a (HOMEY_LOCAL_URL + HOMEY_LOCAL_TOKEN) pair nor (HOMEY_CLOUD_TOKEN + HOMEY_ID) is resolvable, tell the user and exit gracefully:

No Homey credentials configured. Run /ops:setup --section home to configure your Homey Pro hub.

Write action_needed: "configure_homey" to daemon-health.json and exit.

Set helpers used by all phases below:

HOMEY_BASE_LOCAL="${HOMEY_LOCAL_URL}/api/manager"
HOMEY_BASE_CLOUD="https://api.athom.com/v2/homey/${HOMEY_ID}"
HOMEY_AUTH_LOCAL="Authorization: Bearer ${HOMEY_LOCAL_TOKEN}"
HOMEY_AUTH_CLOUD="Authorization: Bearer ${HOMEY_CLOUD_TOKEN}"

# homey_call  [method] [body] — tries local first, falls back to cloud on failure
homey_call() {
  local path="$1" method="${2:-GET}" body="${3:-}"
  local resp http_code
  if [ -n "$HOMEY_LOCAL_URL" ] && [ -n "$HOMEY_LOCAL_TOKEN" ]; then
    resp=$(curl -s -o /tmp/homey_resp -w "%{http_code}" -X "$method" \
      -H "$HOMEY_AUTH_LOCAL" -H "Content-Type: application/json" \
      ${body:+--data "$body"} \
      "${HOMEY_BASE_LOCAL}${path}" 2>/dev/null)
    if [ "$resp" -ge 200 ] && [ "$resp" -lt 300 ]; then
      cat /tmp/homey_resp; return 0
    fi
  fi
  # Fallback to cloud
  if [ -n "$HOMEY_CLOUD_TOKEN" ] && [ -n "$HOMEY_ID" ]; then
    # Map manager-style local paths to cloud equivalents where possible
    local cloud_path="${path/\/devices\/device/\/devices}"
    cloud_path="${cloud_path/\/flow\/flow/\/flows}"
    cloud_path="${cloud_path/\/zones\/zone/\/zones}"
    curl -s -X "$method" \
      -H "$HOMEY_AUTH_CLOUD" -H "Content-Type: application/json" \
      ${body:+--data "$body"} \
      "${HOMEY_BASE_CLOUD}${cloud_path}"
    return $?
  fi
  echo '{"error":"no homey transport available"}'
  return 1
}

Phase 2 — Route by $ARGUMENTS

| Input | Action | | --------------------------------------- | --------------------- | | (empty) | Home status dashboard | | status, dashboard | Home status dashboard | | devices, device, lights, locks, sensors | Devices manager | | flow, flows | Trigger / list flows | | scene, scenes | Trigger scene (alias) | | energy, power, kwh | Energy dashboard | | climate, temp, thermostat, heating | Climate manager | | presence, who, home | Presence | | alarm, alarms, arm, disarm, security | Alarms / security | | health, diagnose, outage | Health / outage scan | | sonos, speaker, music, play, volume, group | Sonos audio control | | setup, configure, init, token | Setup flow |


STATUS (default — empty $ARGUMENTS)

One-screen home dashboard. Run devices/flows/energy/presence/alarms calls in parallel (separate Bash calls or Agent Team), then render.

# Devices online state
homey_call "/devices/device" | jq '{
  total: (. | length),
  online: ([.[] | select(.available == true)] | length),
  offline: ([.[] | select(.available == false)] | length)
}'

# Flows
homey_call "/flow/flow" | jq '{
  total: (. | length),
  enabled: ([.[] | select(.enabled == true)] | length),
  last_fired: ([.[] | select(.lastExecuted != null)] | sort_by(.lastExecuted) | reverse | .[0] | {name, lastExecuted})
}'

# Live energy
homey_call "/energy/live" | jq '{watts: .totalPower // 0}'

# Presence
homey_call "/presence" | jq '[.[] | select(.present == true) | .name]'

# Alarms (active)
homey_call "/alarms/alarm" | jq '[.[] | select(.active == true)]'

# Zones
homey_call "/zones/zone" | jq '[.[] | .name]'

Desktop render:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 OPS ► HOME — [hub-name] — [timestamp]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

HUB          [name]  ([firmware version])
TRANSPORT    local | cloud  ([latency-ms]ms)

DEVICES      [N online] / [N total]   [N offline]
FLOWS        [N enabled] / [N total]
LAST FLOW    [name] fired [N min] ago

POWER        [W] now    [kWh] today
ZONES        [N rooms]
PRESENCE     [name1], [name2]   — home

ALARMS       [N active]
  [type] in [zone] — [device]    (if active)

──────────────────────────────────────────────────────
 [d] devices   [f] flows   [e] energy
 [c] climate   [p] presence   [a] alarms
 /ops:ops-home setup — configure credentials
──────────────────────────────────────────────────────

Mobile mode ($SSH_CONNECTION set or $OPS_MOBILE=1): plain text only, 5–8 lines max, no banners.

home: [N]/[N] devices · [W]W now · [kWh] today.
flows: [N] enabled · last "[name]" [N]m ago.
presence: [names] home.
alarms: [N] active.
next: /ops-home devices | flows | energy

If [N] alarms active is non-zero AND any are critical (smoke/leak/security), surface immediately at top of output and suggest piping to /ops:ops-comms to broadcast.

Use AskUserQuestion for next-action selection (max 4 options per Rule 1).


DEVICES

List devices, group by zone. Support filter and bulk-toggle.

# All devices with zone + capabilities
homey_call "/devices/device" | jq '[.[] | {
  id: .id,
  name: .name,
  zone: .zoneName,
  driverId: .driverId,
  class: .class,
  available: .available,
  capabilities: .capabilities,
  capabilityValues: (.capabilitiesObj // {} | to_entries | map({key: .key, value: .value.value}))
}]'

# Zones for grouping
homey_call "/zones/zone" | jq '[.[] | {id: .id, name: .name}]'

Apply filter from $ARGUMENTS:

  • devices lights → filter class == "light" or capability includes dim/light_hue
  • devices locks → filter class == "lock" or capability includes locked
  • devices climate → filter capability includes target_temperature or measure_temperature
  • devices sensors → filter capability starts with measure_ or alarm_
  • devices --off → set onoff=false on filtered set (REQUIRES AskUserQuestion confirmation — destructive)
  • devices --on → set onoff=true on filtered set (REQUIRES AskUserQuestion confirmation)

Render grouped by zone:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 OPS ► HOME ► DEVICES — [filter] — [timestamp]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[ZONE: Living Room]
  [Living Room Lamp]      light    on    dim:0.65    online
  [Sofa Outlet]           socket   off                online
  [Thermostat]            climate  22.5°C → 21.0°C   online

[ZONE: Kitchen]
  [Kitchen Ceiling]       light    on    dim:1.0     online
  ...

OFFLINE
  [Garage Sensor]         sensor                     offline 2h

──────────────────────────────────────────────────────
 Actions:
 a) Toggle a specific device
 b) Turn all [filter] off
 c) Filter by zone
 d) View device capabilities
──────────────────────────────────────────────────────

For toggle / set capability:

# Set onoff
homey_call "/devices/device/${DEVICE_ID}/capability/onoff" PUT '{"value": false}'

# Set dim (0.0 - 1.0)
homey_call "/devices/device/${DEVICE_ID}/capability/dim" PUT '{"value": 0.5}'

# Set target temperature
homey_call "/devices/device/${DEVICE_ID}/capability/target_temperature" PUT '{"value": 21.0}'

Bulk operations REQUIRE AskUserQuestion confirmation (Rule 5 — destructive-like behavior). Show device count + sample before executing.


FLOWS (and SCENES — alias)

Homey calls scenes "flows". Both flow and scene arguments route here.

# List flows
homey_call "/flow/flow" | jq '[.[] | {
  id: .id,
  name: .name,
  enabled: .enabled,
  lastExecuted: .lastExecuted,
  triggerable: .triggerable
}]'

If $ARGUMENTS includes a flow name (e.g. flow movie-time, scene good-night), fuzzy-match by name (case-insensitive substring), then trigger:

FLOW_ID="[matched id]"
homey_call "/flow/flow/${FLOW_ID}/trigger" POST '{}'

If multiple flows match, present top 4 via AskUserQuestion (Rule 1) and let user pick.

If flow list or no flow name provided, render the list with last-fired age.

Common scene names users may try: movie-time, good-night, leaving-home, coming-home, wake-up, dinner, away. Fuzzy match handles variations (goodnight, good night, night).

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 OPS ► HOME ► FLOWS — [timestamp]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ENABLED FLOWS ([N])
  [name]                 fired [N min] ago
  [name]                 never
  ...

DISABLED ([N])
  [name]
  ...

──────────────────────────────────────────────────────
 Actions:
 a) Trigger a flow
 b) Enable / disable a flow
 c) View flow definition
──────────────────────────────────────────────────────

Trigger results: confirm {"success": true} or surface the error.


ENERGY

Live power draw + today's kWh + top consumers + anomaly detection.

# Live power
homey_call "/energy/live" | jq '{
  totalWatts: .totalPower // 0,
  byDevice: [.devices // {} | to_entries[] | {id: .key, watts: .value.power}] | sort_by(-.watts) | .[:10]
}'

# Today's energy report
TODAY=$(date -u +"%Y-%m-%d")
homey_call "/energy/report?period=today" | jq '{
  todayKwh: .totalEnergy // 0,
  byZone: .zones // {}
}'

# 7-day baseline for anomaly detection
homey_call "/energy/report?period=last7days" | jq '.dailyAverage // 0'

Compute anomaly: if todayKwh > 1.5 * dailyAverage, flag as anomaly.

Render:

━━

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Lifecycle-Innovations-Limited](https://github.com/Lifecycle-Innovations-Limited)
- **Source:** [Lifecycle-Innovations-Limited/claude-ops](https://github.com/Lifecycle-Innovations-Limited/claude-ops)
- **License:** MIT
- **Homepage:** https://github.com/Lifecycle-Innovations-Limited/claude-ops/wiki

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.