Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "indigo",
"source": "./",
"description": "Indigo home automation development toolkit \u2014 plugin development, API integration, and control page building",
"version": "1.8.0",
"version": "1.9.0",
"repository": "https://github.com/simons-plugins/indigo-claude-plugin",
"license": "MIT",
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "indigo",
"version": "1.8.0",
"version": "1.9.0",
"description": "Indigo home automation development toolkit \u2014 plugin development, API integration, and control page building",
"repository": "https://github.com/simons-plugins/indigo-claude-plugin"
}
6 changes: 3 additions & 3 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ Indigo provides two primary integration APIs:

## Related Documentation

- **[Indigo Object Model](../indigo-object-model.md)** - Understanding device/variable objects
- **[Plugin Development](../../concepts/)** - Building Indigo plugins (server-side)
- **[Examples](../../examples/)** - SDK examples for plugin development
- **[Indigo Object Model](../plugin-dev/api/indigo-object-model.md)** - Understanding device/variable objects
- **[Plugin Development](../plugin-dev/concepts/)** - Building Indigo plugins (server-side)
- **[Examples](../plugin-dev/examples/)** - SDK examples for plugin development

## For Claude (Context Optimization)

Expand Down
2 changes: 1 addition & 1 deletion docs/api/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,4 @@ While both APIs provide access to core Indigo functionality (devices, variables,
- **[HTTP API Reference](http-api.md)** - Complete HTTP documentation
- **[Authentication Guide](authentication.md)** - Setup and security
- **[Device Commands](device-commands.md)** - All available commands
- **[Indigo Object Model](../indigo-object-model.md)** - Understanding device objects
- **[Indigo Object Model](../plugin-dev/api/indigo-object-model.md)** - Understanding device objects
2 changes: 1 addition & 1 deletion docs/plugin-dev/concepts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Documentation planned for this section:

## Contributing

Want to add documentation to this section? See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
Want to add documentation to this section? Open a PR or issue on the [GitHub repo](https://github.com/simons-plugins/indigo-claude-plugin).

**Valuable topics include**:
- State update performance patterns
Expand Down
2 changes: 2 additions & 0 deletions docs/plugin-dev/concepts/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Define custom plugin actions in `Actions.xml`. Users configure and trigger these
</Action>
```

To pre-populate fields with computed or live values when the dialog opens (e.g. seed `brightness` from the device's current state), override `getActionConfigUiValues(self, plugin_props, type_id, dev_id)` — see [`plugin-lifecycle.md`](plugin-lifecycle.md#configui-pre-population-callbacks).

### Action Targeting All Devices

```xml
Expand Down
4 changes: 4 additions & 0 deletions docs/plugin-dev/concepts/configui.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@ def get_device_list(self, filter="", values_dict=None, type_id="", target_id=0):

**Return**: List of `(value_string, display_string)` tuples.

## Pre-population Callbacks

To seed fields with computed values when a dialog opens (today's date, the latest sensor reading, a freshly minted token), override the matching `get*ConfigUiValues` callback. Static `defaultValue` only fires once and dynamic lists do not auto-select their first item — the callback is the supported way to set values that may change between opens. See [`plugin-lifecycle.md`](plugin-lifecycle.md#configui-pre-population-callbacks) for the full reference table covering all five callbacks (`getPrefsConfigUiValues`, `getDeviceConfigUiValues`, `getEventConfigUiValues`, `getMenuActionConfigUiValues`, `getActionConfigUiValues`).

## Validation

### Validation Callbacks
Expand Down
2 changes: 2 additions & 0 deletions docs/plugin-dev/concepts/devices.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ States store device data and can trigger events:

**Field Types**: `textfield`, `textarea`, `checkbox`, `menu`, `list`, `button`, `label`, `separator`

To pre-populate fields when the device dialog opens (e.g. seed a fresh API token, load the current sensor reading), override `getDeviceConfigUiValues(self, plugin_props, type_id, dev_id)` — see [`plugin-lifecycle.md`](plugin-lifecycle.md#configui-pre-population-callbacks).

## Device Factory Pattern

A Device Factory creates and manages a group of child devices from a single dialog. Use this when a hub or controller discovers multiple sub-devices (e.g., a bridge with relays, dimmers, and sensors).
Expand Down
2 changes: 2 additions & 0 deletions docs/plugin-dev/concepts/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ def getEventConfigUiValues(self, pluginProps, typeId, triggerId):
return valuesDict
```

This is one of five `get*ConfigUiValues` callbacks — see [`plugin-lifecycle.md`](plugin-lifecycle.md#configui-pre-population-callbacks) for the full reference (device, action, menu item, and plugin prefs dialogs).

### closedEventConfigUi

Called after event config is saved:
Expand Down
2 changes: 2 additions & 0 deletions docs/plugin-dev/concepts/menu-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ def send_custom_command(self, values_dict, menu_item_id):
self.logger.info(f"Sending command: {command}")
```

**Pre-populating fields when the dialog opens.** Override `getMenuActionConfigUiValues(self, menu_id)` to seed fields with computed values (today's date, the latest reading, etc.) before the dialog is shown. Static `defaultValue` only fires once and dynamic lists do not auto-select their first item — see [`plugin-lifecycle.md`](plugin-lifecycle.md#configui-pre-population-callbacks) for the full reference.

## Common Patterns

### Debug Toggle
Expand Down
43 changes: 43 additions & 0 deletions docs/plugin-dev/concepts/plugin-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,49 @@ def deviceDeleted(self, dev):
self._cleanup_device_data(dev.id)
```

## ConfigUI Pre-population Callbacks

Override any of these to seed dialog fields with computed or live values **before** the dialog opens. They run on every dialog open, so the values can change from one open to the next (e.g. defaulting a date dropdown to today). All return `(values_dict, errors_dict)` as `indigo.Dict()` instances.

| Callback | Dialog | Signature |
|--------------------------------|-----------------|-------------------------------------------------|
| `getPrefsConfigUiValues` | Plugin prefs | `(self)` |
| `getDeviceConfigUiValues` | Device config | `(self, plugin_props, type_id, dev_id)` |
| `getEventConfigUiValues` | Event / trigger | `(self, plugin_props, type_id, event_id)` |
| `getMenuActionConfigUiValues` | Menu item | `(self, menu_id)` |
| `getActionConfigUiValues` | Action config | `(self, plugin_props, type_id, dev_id)` |

All five exist in `plugin_base.py` on every supported Indigo version (2023.2, 2025.1, 2025.2). The Indigo SDK examples leave most of them off, which has historically made them easy to miss.

**Static `defaultValue` vs. dynamic lists.** A `<Field defaultValue="X">` only seeds the field on first open — and dynamic lists (`<List class="self" method="..."/>`) do **not** auto-select their first item if no `defaultValue` matches. If you want a value computed at open-time (today's date, the latest sensor reading, the user's last choice), use the callback above for that dialog type.

### Example — default a menu item's date dropdowns to today

```python
from datetime import date

def getMenuActionConfigUiValues(self, menu_id):
values_dict = indigo.Dict()
errors_dict = indigo.Dict()
if menu_id == "dailyReport":
today = date.today()
values_dict["rpt_day"] = f"{today.day:02d}"
values_dict["rpt_month"] = f"{today.month:02d}"
values_dict["rpt_year"] = str(today.year)
return (values_dict, errors_dict)
```

### Example — seed a device dialog with a fresh API token

```python
def getDeviceConfigUiValues(self, plugin_props, type_id, dev_id):
values_dict = indigo.Dict(plugin_props)
errors_dict = indigo.Dict()
if not values_dict.get("api_token"):
values_dict["api_token"] = self._mint_token()
return (values_dict, errors_dict)
```

## Common Patterns

### Deferred Initialization
Expand Down
2 changes: 2 additions & 0 deletions docs/plugin-dev/concepts/plugin-preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def startup(self):
debug = self.pluginPrefs.get("showDebugInfo", False)
```

To seed fields with computed values when the prefs dialog opens, override `getPrefsConfigUiValues(self)` — see [`plugin-lifecycle.md`](plugin-lifecycle.md#configui-pre-population-callbacks).

## Hidden Preferences

Store values not shown in the config UI:
Expand Down
8 changes: 5 additions & 3 deletions docs/plugin-dev/concepts/scripting-shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,26 @@ Useful for:

## Command-Line Interface

> Path examples below use `Indigo 2025.2`. Substitute your installed version (`2025.1`, `2023.2`, etc.) — see the [version reference table](../quick-start.md#version-reference).

### Run a Script File

```bash
/Library/Application\ Support/Perceptive\ Automation/Indigo\ 2023.2/IndigoPluginHost.app/Contents/MacOS/IndigoPluginHost -x /path/to/script.py
/Library/Application\ Support/Perceptive\ Automation/Indigo\ 2025.2/IndigoPluginHost.app/Contents/MacOS/IndigoPluginHost -x /path/to/script.py
```

### Run Inline Code

```bash
/Library/Application\ Support/Perceptive\ Automation/Indigo\ 2023.2/IndigoPluginHost.app/Contents/MacOS/IndigoPluginHost -e 'indigo.device.turnOn("Hallway Light")'
/Library/Application\ Support/Perceptive\ Automation/Indigo\ 2025.2/IndigoPluginHost.app/Contents/MacOS/IndigoPluginHost -e 'indigo.device.turnOn("Hallway Light")'
```

### SSH Remote Access

You can run Indigo scripts remotely via SSH:

```bash
ssh user@indigo-mac '/Library/Application\ Support/Perceptive\ Automation/Indigo\ 2023.2/IndigoPluginHost.app/Contents/MacOS/IndigoPluginHost -e "indigo.server.log(\"Hello from SSH\")"'
ssh user@indigo-mac '/Library/Application\ Support/Perceptive\ Automation/Indigo\ 2025.2/IndigoPluginHost.app/Contents/MacOS/IndigoPluginHost -e "indigo.server.log(\"Hello from SSH\")"'
```

## Notes
Expand Down
30 changes: 19 additions & 11 deletions docs/plugin-dev/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Your comprehensive guide to creating your first Indigo plugin in minutes.
## Prerequisites

- **macOS**: Indigo runs only on macOS
- **Indigo 2023.2+**: Home automation server installed and running
- **Python 3.10+**: Comes with macOS, used by Indigo
- **Indigo 2023.2+** (2025.x recommended): Home automation server installed and running
- **Python 3.10–3.13**: Comes bundled with Indigo (see version table below)
- **Text Editor**: VS Code, PyCharm, or any code editor
- **Basic Python**: Understanding of Python and object-oriented programming

Expand Down Expand Up @@ -241,9 +241,11 @@ import paho.mqtt.client as mqtt

**How it works:**
- Indigo runs `pip install` into `Contents/Packages/` automatically on first load
- Indigo tracks installation via `pip-install-log-success.txt` in `Packages/`
- Subsequent restarts skip installation if the success marker exists
- If you change `requirements.txt`, delete the success marker to force reinstall
- Indigo writes a success marker in `Packages/` — filename is version-keyed:
- **2025.2**: `3.13-pip-install-log-success.txt`
- **2025.1 and older**: `pip-install-log-success.txt`
- Subsequent restarts skip installation if the marker exists
- If you change `requirements.txt`, delete the marker to force reinstall

**Important:**
- Do NOT commit `Contents/Packages/` to git — add it to `.gitignore`
Expand Down Expand Up @@ -298,17 +300,23 @@ Place `.py` files here to make them importable by any plugin. After adding or up

**Solutions**:
- Add a `requirements.txt` in `Contents/Server Plugin/` listing your dependencies
- Delete `Contents/Packages/pip-install-log-success.txt` to force Indigo to reinstall
- To force Indigo to reinstall, delete the success marker in `Contents/Packages/` (`3.13-pip-install-log-success.txt` on 2025.2, `pip-install-log-success.txt` on older versions)
- If you see `dlopen` errors with architecture mismatches, remove any manually bundled `.so` files from `Contents/Packages/` and let Indigo reinstall via `requirements.txt`
- Use Python 3 syntax (not Python 2)
- Check package compatibility with Python 3.10+

## Python Version Reference
## Version Reference

| Indigo Version | Python Version | Notes |
|----------------|----------------|-------|
| 2023.2+ | Python 3.10+ | Current, recommended |
| 2022.x | Python 2.7 | Legacy, end-of-life |
| Indigo | Python | Plugin install path | Runtime API |
|--------|--------|----------------------------------------------------|-------------|
| 2025.2 | 3.13.9 | `.../Perceptive Automation/Indigo 2025.2/Plugins/` | 3.8 |
| 2025.1 | 3.11.6 | `.../Perceptive Automation/Indigo 2025.1/Plugins/` | 3.7 |
| 2023.2 | 3.10+ | `.../Perceptive Automation/Indigo 2023.2/Plugins/` | (see docs) |
| 2022.x | 2.7 | Legacy, end-of-life | n/a |

`ServerApiVersion` in `Info.plist` declares the **minimum** API your plugin requires. `3.0` continues to work on all 2023.2+ versions — there is no need to bump it just because you're on a newer Indigo release. The path examples elsewhere in this guide use 2023.2; substitute the row above that matches your installation.

**Upgrading to 2025.2?** Python 3.13 has several library breaking changes — see [`troubleshooting/common-issues.md`](troubleshooting/common-issues.md#upgrading-to-indigo-20252--python-313).

**For Python 3 migration**, see: [`reference/Python3-Migration-Guide.md`](../../reference/Python3-Migration-Guide.md)

Expand Down
51 changes: 49 additions & 2 deletions docs/plugin-dev/troubleshooting/common-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,22 @@ zeroconf==0.148.0
**How Indigo handles it:**
1. On plugin load, Indigo checks for `requirements.txt`
2. Runs `pip install` into `Contents/Packages/` using Indigo's Python
3. Creates `Contents/Packages/pip-install-log-success.txt` as a marker
3. Creates a success marker in `Contents/Packages/` (filename varies — see below)
4. On subsequent restarts, skips installation if the marker exists

The marker filename is version-keyed:

| Indigo | Success file (in `Contents/Packages/`) |
|----------------|-----------------------------------------------|
| 2025.2 | `3.13-pip-install-log-success.txt` |
| 2025.1 / older | `pip-install-log-success.txt` |

Never `pip install` into the system Python — those packages disappear on any Python upgrade. `requirements.txt` is the only supported path on every Indigo version.

### Force Reinstall

If you change `requirements.txt` or packages are corrupted:
1. Delete `Contents/Packages/pip-install-log-success.txt`
1. Delete the success marker (see filename table above)
2. Restart the plugin
3. Indigo will re-run `pip install`

Expand Down Expand Up @@ -332,6 +341,44 @@ import requests
- Files built for Python 3.11 won't load on Python 3.10
- `__file__` is not defined in Indigo's plugin environment, breaking `os.path.dirname(__file__)` patterns

## Upgrading to Indigo 2025.2 / Python 3.13

*Applies to 2025.2 only — earlier Indigo versions ship Python 3.10 or 3.11.*

Indigo 2025.2 ships Python 3.13.9. Several stdlib and popular-library changes between 3.11 and 3.13 break plugins that worked on 2025.1.

### `telnetlib` removed from the Python 3.13 stdlib

```
ModuleNotFoundError: No module named 'telnetlib'
```

**Fix**: add `telnetlib-313-and-up` to your `requirements.txt`. The shim restores the same import path so existing code keeps working.

### `websockets` v14+ — `extra_headers` renamed

```
TypeError: connect() got unexpected keyword argument 'extra_headers'
```

**Fix**: rename `extra_headers=` to `additional_headers=` in `websockets.connect()`. This is a `websockets` library change (v14+), not Python 3.13 itself — pinning `websockets<14` in `requirements.txt` is the alternative.

### `matplotlib` — `legendHandles` removed

```
AttributeError: 'Legend' object has no attribute 'legendHandles'
```

**Fix**: use `legend_handles` (snake_case) instead — e.g. `ax.legend().legend_handles`.

### `matplotlib` — `plot_date` deprecated

```
MatplotlibDeprecationWarning: plot_date was deprecated in 3.9 and will be removed in 3.11. Use plot() instead.
```
Comment on lines +352 to +378
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifiers to the new fenced code blocks.

The new error-output fences in this section omit language tags, which triggers MD040.

Suggested fix
-```
+```text
 ModuleNotFoundError: No module named 'telnetlib'

...
- +text
TypeError: connect() got unexpected keyword argument 'extra_headers'

...
-```
+```text
AttributeError: 'Legend' object has no attribute 'legendHandles'

...
- +text
MatplotlibDeprecationWarning: plot_date was deprecated in 3.9 and will be removed in 3.11. Use plot() instead.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 352-352: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 360-360: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 368-368: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 376-376: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/plugin-dev/troubleshooting/common-issues.md` around lines 352 - 378,
Update the new fenced code blocks that show error output to include a language
identifier (use "text") so Markdown lint MD040 is satisfied; specifically add
```text to the blocks containing the strings "ModuleNotFoundError: No module
named 'telnetlib'", "TypeError: connect() got unexpected keyword argument
'extra_headers'", "AttributeError: 'Legend' object has no attribute
'legendHandles'", and "MatplotlibDeprecationWarning: plot_date was deprecated in
3.9 and will be removed in 3.11. Use plot() instead." so each error-output fence
is ```text ... ``` instead of just ``` ... ```.


**Fix**: replace `ax.plot_date(...)` with `ax.plot(...)`. Matplotlib auto-formats date axes on `plot()` — no extra setup needed.

## Python 3 Issues

### Common Migration Errors
Expand Down
4 changes: 2 additions & 2 deletions skills/dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ class Plugin(indigo.PluginBase):
- Use `self.sleep()` not `time.sleep()` in concurrent threads
- Handle `self.StopThread` in `runConcurrentThread`
- Log with `self.logger.debug/info/warning/error/exception()`
- Python 3.10+ (Indigo 2023+)
- Bundle dependencies in `Contents/Packages/` (not system pip)
- Python 3.10–3.13 (see version table in `docs/plugin-dev/quick-start.md`)
- Use `requirements.txt` in `Contents/Server Plugin/` — Indigo auto-installs into `Contents/Packages/`. Never `pip install` into system Python.
- `CFBundleIdentifier` in Info.plist must be globally unique

## Device Types (Devices.xml)
Expand Down
4 changes: 2 additions & 2 deletions snippets/plugin-base-template.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
4. Implement your plugin logic in the appropriate methods

Requirements:
- Indigo 2023.2+ (Python 3.10+)
- ServerApiVersion 3.0 in Info.plist
- Indigo 2023.2+ (Python 3.10–3.13; 2025.2 recommended)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace EN DASH with ASCII hyphen in docstring version range.

Line 13 uses , which Ruff flags as ambiguous Unicode punctuation (RUF002).

Suggested fix
-    - Indigo 2023.2+ (Python 3.10–3.13; 2025.2 recommended)
+    - Indigo 2023.2+ (Python 3.10-3.13; 2025.2 recommended)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Indigo 2023.2+ (Python 3.103.13; 2025.2 recommended)
- Indigo 2023.2+ (Python 3.10-3.13; 2025.2 recommended)
🧰 Tools
🪛 Ruff (0.15.12)

[warning] 13-13: Docstring contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF002)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@snippets/plugin-base-template.py` at line 13, Docstring version range uses an
en dash (Unicode “–”) causing RUF002; replace it with an ASCII hyphen "-" in the
docstring text "Indigo 2023.2+ (Python 3.10–3.13; 2025.2 recommended)" so it
becomes "Indigo 2023.2+ (Python 3.10-3.13; 2025.2 recommended)"; update the
top-level docstring literal in snippets/plugin-base-template.py (the line
containing that version string) accordingly.

- ServerApiVersion 3.0 in Info.plist (declares minimum API; works on all 2023.2+)
"""

import indigo
Expand Down
Loading