Install
$ agentstack add skill-lifecycle-innovations-limited-claude-ops-ops-home ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
About
OPS ► HOME — Smart Home Command Center (Homey Pro)
Runtime Context
Before executing, load available context:
- Preferences: Read
${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json
timezone— display all timestamps in user's timezonehome_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 APIhome_automation.homey_cloud_token— Athom OAuth token for cloud fallbackhome_automation.homey_id— Homey ID (cloud resolution)
- Daemon health: Read
${CLAUDE_PLUGIN_DATA_DIR}/daemon-health.json
- If
action_neededis not null → surface it before running any Homey operations - On any auth/connectivity failure in this skill, write
action_neededback to daemon-health.json
- 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→ filterclass == "light"or capability includesdim/light_huedevices locks→ filterclass == "lock"or capability includeslockeddevices climate→ filter capability includestarget_temperatureormeasure_temperaturedevices sensors→ filter capability starts withmeasure_oralarm_devices --off→ setonoff=falseon filtered set (REQUIRESAskUserQuestionconfirmation — destructive)devices --on→ setonoff=trueon filtered set (REQUIRESAskUserQuestionconfirmation)
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.
Write a review
Versions
- v0.1.0 Imported from the upstream source.