Agent skill
hubitat-app-device
Add or remove devices from a Hubitat app instance (e.g., Maker API)
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/hubitat-app-device
SKILL.md
Hubitat App Device Skill
Add or remove devices from an installed app's device input. Primary use case: managing Maker API's device list for automated testing.
How It Works
A single POST to /installedapp/update/json with _action_update=Done saves the app configuration. The settings[{deviceInput}] field contains the comma-separated list of device IDs — include the new device for add, exclude it for remove. All other settings must be echoed back with their type metadata.
Instructions
Step 1: Read Configuration
Read .hubitat.json from the project root to get hub_ip and maker_api.app_id.
Step 2: Parse Arguments
Parse $ARGUMENTS:
add {device_id}— add device to Maker API (usesmaker_api.app_id)remove {device_id}— remove device from Maker APIadd {device_id} {installed_app_id}— add device to a specific app instanceremove {device_id} {installed_app_id}— remove device from a specific app instancelist [{installed_app_id}]— show devices currently in the app
If no installed app ID is given, default to maker_api.app_id from the config.
Step 3: Fetch App Configuration
curl -s "http://{hub_ip}/installedapp/configure/json/{installedAppId}"
From the JSON response, extract:
app.version— config version (usually1)app.label— the app's display labelconfigPage.name— page name (usuallymainPage)configPage.sections[].input[]— all input definitions (each hasname,type,multiple)configPage.sections[].body[]— body elements; find any withelement: "label"(these are label inputs)settings— current values for all inputs- Device input — the input whose
typestarts withcapability.(e.g.,pickedDeviceswith typecapability.*)
The settings value for a device input is either:
- A map like
{"12": "Device Name", "14": "Other"}— keys are device IDs - null — no devices selected
Step 4: For "list" — Display and Stop
Show the app name, device input name, and a table of current device IDs and names. Then stop.
Step 5: Validate
- add: confirm device ID is not already in the device list. If it is, report "already present" and stop.
- remove: confirm device ID IS in the device list. If not, report "not found" and stop.
Step 6: Build and Send the POST
Use Python (python3) with urllib.request and urllib.parse.
Build form fields as a list of tuples (preserves order, allows empty-string keys):
Fixed header fields
| Field | Value |
|---|---|
_action_update |
Done |
formAction |
update |
id |
installed app ID (string) |
version |
app.version (string) |
appTypeId |
empty string |
appTypeName |
empty string |
currentPage |
page name from config |
pageBreadcrumbs |
%5B%5D |
Label inputs
For each body element with element: "label":
| Field | Value |
|---|---|
{name}.type |
text |
{name} |
value from app.label (NOT from settings) |
Regular inputs
For each input from configPage.sections[].input[], in order:
| Field | Value |
|---|---|
{name}.type |
the input's type |
{name}.multiple |
true or false (lowercase string) |
Then, if the input is a bool type:
| Field | Value |
|---|---|
checkbox[{name}] |
on |
Then, if this is the device input (type starts with capability.):
| Field | Value |
|---|---|
settings[{name}] |
comma-separated device IDs (see below) |
deviceList |
the input name |
(empty string "") |
empty string |
Otherwise (non-device inputs):
| Field | Value |
|---|---|
settings[{name}] |
current value (see conversion rules below) |
Device ID list for settings[{deviceInput}]
- add: existing device IDs + the new device ID, comma-separated
- remove: existing device IDs minus the removed device ID, comma-separated
Fixed footer fields
| Field | Value |
|---|---|
referrer |
http://{hub_ip}/installedapp/list |
url |
http://{hub_ip}/installedapp/configure/{id}/{pageName} |
_cancellable |
false |
Settings Value Conversion
When reading settings values for the form body:
- String (
"true","false","some text") → use as-is - null →
[](the string[]) - Map (device inputs) → comma-separated keys
Send the POST
body = urllib.parse.urlencode(fields)
req = urllib.request.Request(
f"http://{hub_ip}/installedapp/update/json",
data=body.encode(),
headers={"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}
)
resp = urllib.request.urlopen(req)
Expected success response: {"status":"success","location":"/installedapp/list"}
Step 7: Verify
Fetch the config again and check settings.{deviceInputName} to confirm:
- add: the new device ID is now a key in the map
- remove: the device ID is no longer a key in the map
Step 8: Report Result
Summarize:
- Action performed (added/removed)
- Device ID, device name, and the app name
- Updated device list (ID and name table)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?