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.9.0",
"version": "1.9.1",
"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.9.0",
"version": "1.9.1",
"description": "Indigo home automation development toolkit \u2014 plugin development, API integration, and control page building",
"repository": "https://github.com/simons-plugins/indigo-claude-plugin"
}
8 changes: 7 additions & 1 deletion commands/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Expert assistant for Indigo home automation plugin development. Provides compreh
| `docs/plugin-dev/troubleshooting/common-issues.md` | 11KB | Troubleshooting |
| `docs/plugin-dev/patterns/api-patterns.md` | 5KB | Common API patterns |
| `docs/plugin-dev/patterns/open-source-contributing.md` | 3KB | Contributing to IndigoDomotics open source |
| `docs/plugin-dev/patterns/testing.md` | 4KB | Testing — pytest mocks (Pattern A) and TestingBase live integration (Pattern B) |

### Modular IOM Reference (Load by Topic)

Expand Down Expand Up @@ -70,6 +71,7 @@ These are complementary - load based on question type:
| Subscriptions | `docs/plugin-dev/api/iom/subscriptions.md` |
| Schedule/action group commands | `docs/plugin-dev/api/iom/command-namespaces.md` |
| Open source contributing | `docs/plugin-dev/patterns/open-source-contributing.md` |
| Testing, pytest, mocking, TestingBase | `docs/plugin-dev/patterns/testing.md` |

### Specific Routing

Expand Down Expand Up @@ -99,6 +101,9 @@ These are complementary - load based on question type:
**"How do I contribute to open source?" / "Add plugin to IndigoDomotics"**
1. Read `docs/plugin-dev/patterns/open-source-contributing.md`

**"How do I test my plugin?" / "Write tests" / "TestingBase" / "pytest"**
1. Read `docs/plugin-dev/patterns/testing.md`

**"What device properties exist?"**
1. Read `docs/plugin-dev/api/iom/devices.md`

Expand Down Expand Up @@ -143,7 +148,8 @@ docs/plugin-dev/
│ └── utilities.md # Helpers (4KB)
├── patterns/
│ ├── api-patterns.md # Common patterns (5KB)
│ └── open-source-contributing.md # IndigoDomotics contributing guide (3KB)
│ ├── open-source-contributing.md # IndigoDomotics contributing guide (3KB)
│ └── testing.md # pytest mocks + TestingBase (4KB)
├── examples/
│ └── sdk-examples-guide.md # Example catalog (8KB)
└── troubleshooting/
Expand Down
5 changes: 5 additions & 0 deletions docs/plugin-dev/patterns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ Core patterns for working with the Indigo Object Model:
- Variable and action group patterns
- Common anti-patterns to avoid

### [Testing](testing.md)
Two complementary testing patterns:
- Pattern A — `unittest.mock`-based unit tests (fast, no Indigo runtime, runs in CI)
- Pattern B — live integration tests with the upstream `TestingBase` git submodule (`APIBase` + `ValidateXmlFile` against a running server)

## Coming Soon

We're building additional implementation patterns including:
Expand Down
134 changes: 134 additions & 0 deletions docs/plugin-dev/patterns/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Testing Patterns

How to test Indigo plugins. Two complementary patterns:

- **Pattern A — Unit tests with mocks**: fast, no Indigo runtime required, runs in CI.
- **Pattern B — Live integration with TestingBase**: exercises a real running Indigo server, catches integration bugs and validates plugin XML.

They are complementary, not alternatives. Run A often, B before a release.

---

## Pattern A — Unit tests with a mocked Indigo runtime

Mock `indigo` via `unittest.mock` and import your plugin module directly. Tests can then exercise business logic — API parsing, state computation, ConfigUI validation, scheduling — without an Indigo install.

**When to use**: anything that doesn't depend on Indigo's database — most of your plugin's logic.

**Reference shape**:

```python
# tests/conftest.py
import sys
from pathlib import Path
from unittest.mock import Mock
import pytest

SERVER_PLUGIN_DIR = (
Path(__file__).parent.parent
/ "MyPlugin.indigoPlugin"
/ "Contents"
/ "Server Plugin"
)
sys.path.insert(0, str(SERVER_PLUGIN_DIR))


@pytest.fixture
def mock_logger():
# Mock() auto-creates these attributes on first access — the explicit
# assignment is for readability so test failures point at named mocks.
logger = Mock()
for level in ("debug", "info", "warning", "error", "exception"):
setattr(logger, level, Mock())
return logger
```

Tests then import plugin modules and inject `mock_logger` (and other fixtures) where the plugin would normally use `self.logger`. Run with `pytest` — no Indigo running, no credentials, no network.

---

## Pattern B — Live integration with TestingBase

Indigo ships [`TestingBase`](https://github.com/IndigoDomotics/TestingBase) as a shared git submodule. Tests subclass `APIBase` — an abstract `unittest.TestCase` — and exercise a running Indigo server's HTTP API. The companion `ValidateXmlFile` helper validates `Devices.xml`, `Actions.xml`, `Events.xml`, and `MenuItems.xml` against the Indigo schema.

**When to use**: pre-release smoke tests; XML schema validation; end-to-end checks against your plugin's HTTP responder.

**Setup**:

```bash
# At the top level of your plugin repo
git submodule add https://github.com/IndigoDomotics/TestingBase.git tests/shared
git submodule update --init
```

**Layout** (the convention TestingBase expects):

```
my-plugin/
├── tests/
│ ├── shared/ # submodule — do NOT edit
│ ├── .env # gitignored — Indigo API credentials
│ ├── testing-requirements.txt # references shared/module-requirements.txt
│ ├── venv/ # gitignored — your test venv
│ └── test_my_plugin.py
```

`tests/.env` carries credentials — see `tests/shared/ENV_TEMPLATE` for the exact keys. `tests/testing-requirements.txt` should chain to the shared list:

```
-r shared/module-requirements.txt
# anything else your tests need
```

Comment on lines +64 to +82
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 unlabeled fenced code blocks (MD040).

markdownlint-cli2 is flagging fenced blocks without a language at Line 64 and Line 76. Add a language tag (e.g., text) to avoid lint failures and improve readability.

✅ Proposed fix
-```
+```text
 my-plugin/
 ├── tests/
 │   ├── shared/                       # submodule — do NOT edit
 │   ├── .env                          # gitignored — Indigo API credentials
 │   ├── testing-requirements.txt      # references shared/module-requirements.txt
 │   ├── venv/                         # gitignored — your test venv
 │   └── test_my_plugin.py

...

- +text
-r shared/module-requirements.txt

anything else your tests need

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

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

(MD040, fenced-code-language)


[warning] 76-76: 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/patterns/testing.md` around lines 62 - 80, Two fenced code
blocks in the docs (the directory tree block starting with "my-plugin/" and the
requirements snippet starting with "-r shared/module-requirements.txt") lack
language identifiers causing MD040; add a language tag (e.g., ```text) to both
fenced code blocks so they become ```text ... ``` to satisfy markdownlint and
improve readability.

**Minimal `APIBase` test**:

```python
# tests/test_my_plugin.py
from shared import APIBase

class TestMyPlugin(APIBase):
def test_device_reachable(self):
device = self.get_indigo_object(<device_id>)
self.assertTrue(device["enabled"])
```

**XML validation** — `ValidateXmlFile` MUST come first in the MRO. Resolve the path relative to the test file so the test runs on any machine:

```python
import os
from shared import APIBase, ValidateXmlFile

class TestActionsXml(ValidateXmlFile, APIBase):
server_plugin_dir_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../MyPlugin.indigoPlugin/Contents/Server Plugin",
)
)
file_name = "Actions.xml"
```

**Maintenance**: `tests/shared` is a submodule. Pull updates with
`git submodule update --recursive --remote tests/shared` and never commit
local changes back to it — the upstream README is explicit about that.

---

## Choosing between A and B

| | Pattern A (mocks) | Pattern B (TestingBase) |
|---|---|---|
| Speed | Seconds | Slower — HTTP round-trips per assertion; helpers like `run_host_script` spawn an IPH3 process per call |
| Indigo install needed | No | Yes — running server + admin API access |
| What it catches | Logic errors | Integration + XML schema errors |
| Best for | CI on every PR | Pre-release smoke |

Use both for any non-trivial plugin: A on every commit, B before each release.

---

## References

- TestingBase upstream: https://github.com/IndigoDomotics/TestingBase
- Plugin HTTP API (consumed by Pattern B): see `/indigo:api`
- Plugin lifecycle (what you'd typically test): see `concepts/plugin-lifecycle.md`
53 changes: 53 additions & 0 deletions reference/Python3-Migration-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,56 @@
- `dict.iteritems()` is deprecated, `dict.items()` works just as well in both python 2 & 3 (same for all the iter* functions on dict and list objects).

- `pylint -py3k` may give you this error: `round built-in referenced (round-builtin)`. This warning can safely be ignored since it's primarily just a reminder that the [algorithm for calculating how an exact halfway cases](https://docs.python.org/3/whatsnew/3.0.html#builtins) has changed in python 3.

---

## Indigo 2025.2 / Python 3.11 → 3.13

Indigo 2025.2 bumps the embedded interpreter from Python **3.11 to 3.13**. The 3.11→3.13 step is small compared to 2→3, and most plugin code needs no changes. Two categories warrant a quick sweep before upgrading.

### Library-level breakages

For specific third-party library and stdlib import errors observed under 3.13 (`telnetlib`, `websockets` v14+, `matplotlib`'s `legendHandles` and `plot_date`), see [common-issues.md → Upgrading to Indigo 2025.2 / Python 3.13](../docs/plugin-dev/troubleshooting/common-issues.md#upgrading-to-indigo-20252--python-313). Those are the issues seen in the wild; the rest of this section covers generic 3.13 deprecations to clean up while you're in the area.

### Deprecations to clean up

These don't fail under 3.13 yet but are slated for removal:

- **`datetime.utcnow()`** — deprecated since 3.12, returns naive datetimes.
```python
# before
ts = datetime.utcnow()
# after
from datetime import datetime, UTC
ts = datetime.now(UTC)
```

- **`asyncio.get_event_loop()`** with no running loop emits a `DeprecationWarning` since 3.12 (cpython gh-100160) and will be removed in 3.14. Inside a coroutine, use `asyncio.get_running_loop()` instead.
```python
# inside a coroutine
loop = asyncio.get_running_loop()
# creating a new loop explicitly
loop = asyncio.new_event_loop()
```

- **`pkg_resources`** is deprecated. Prefer `importlib.metadata` for distribution introspection.

### Removed stdlib (PEP 594 — completed in 3.13)

If any of these appear in your imports, they will now fail at import time:

`aifc`, `audioop`, `cgi`, `cgitb`, `chunk`, `crypt`, `imghdr`, `mailcap`, `msilib`, `nis`, `nntplib`, `ossaudiodev`, `pipes`, `sndhdr`, `spwd`, `sunau`, `telnetlib`, `uu`, `xdrlib`.

For `telnetlib` specifically, the `telnetlib-313-and-up` PyPI shim restores the same import path — see the troubleshooting note linked above.

`imp` (removed in 3.12) and `distutils` (removed in 3.12) are gone too if you've been carrying any pre-3.12 code. Switch to `importlib` and `setuptools`/`packaging`.

### Vendored packages

Pre-built packages in `Contents/Packages/` are tagged with the Python version that created them. Pure-Python wheels (`.py` only — no `.so` files) usually transport across versions. Anything with compiled extensions needs rebuilding for 3.13. The safer pattern is `requirements.txt` — Indigo 2025.2 will reinstall under 3.13 automatically and writes a version-keyed `3.13-pip-install-log-success.txt` marker.

### Testing under 3.13

Run your existing test suite (Pattern A from [patterns/testing.md](../docs/plugin-dev/patterns/testing.md)) under 3.13 locally before installing 2025.2 on a live server. If you don't have a test suite, restart plugins one at a time after the upgrade and watch the Indigo Event Log for tracebacks — bump `PluginVersion` and ship a fix for any plugin that fails to start.

To roll back to 2025.1: quit 2025.2 server and client, relaunch 2025.1.
1 change: 1 addition & 0 deletions skills/dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ For detailed guidance on specific topics, read these files relative to `${CLAUDE
| Scripting shell & CLI | `docs/plugin-dev/concepts/scripting-shell.md` |
| Plugin preferences & PluginConfig.xml | `docs/plugin-dev/concepts/plugin-preferences.md` |
| API patterns (state updates, replaceOnServer) | `docs/plugin-dev/patterns/api-patterns.md` |
| Testing patterns (pytest mocks, TestingBase) | `docs/plugin-dev/patterns/testing.md` |
| Troubleshooting | `docs/plugin-dev/troubleshooting/common-issues.md` |
| SDK examples guide | `docs/plugin-dev/examples/sdk-examples-guide.md` |
| Indigo Object Model overview | `docs/plugin-dev/api/indigo-object-model.md` |
Expand Down
Loading