From d3163c1241283d2d44ba055d3ea23dc75eea9133 Mon Sep 17 00:00:00 2001 From: Simon Clark Date: Wed, 22 Jul 2026 23:23:14 +0100 Subject: [PATCH] =?UTF-8?q?feat(automations):=20delay/duration=20on=20trig?= =?UTF-8?q?ger=5Fenable=20+=20schedule=5Fenable=20=E2=80=94=20self-rearmin?= =?UTF-8?q?g=20holiday=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'Disable for two weeks' now self-rearms: both enable tools accept the IOM's optional delay/duration (seconds until the entity flips back to its prior state), reusing the shared validator from the device tools. Closes #37. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EYAGXd4bE9D9Z5kZeSHeo7 --- .../Contents/Info.plist | 2 +- .../Server Plugin/tools/automations.py | 42 +++++++++++++++---- pyproject.toml | 2 +- tests/test_automations.py | 19 +++++++++ 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/Indigo MCP Lite.indigoPlugin/Contents/Info.plist b/Indigo MCP Lite.indigoPlugin/Contents/Info.plist index 95c3eb5..ec4bf6f 100644 --- a/Indigo MCP Lite.indigoPlugin/Contents/Info.plist +++ b/Indigo MCP Lite.indigoPlugin/Contents/Info.plist @@ -4,7 +4,7 @@ PluginVersion - 2026.7.0 + 2026.7.1 ServerApiVersion 3.6 IwsApiVersion diff --git a/Indigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/automations.py b/Indigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/automations.py index 301bc9b..3bfcb0b 100644 --- a/Indigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/automations.py +++ b/Indigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/automations.py @@ -10,6 +10,7 @@ description, matching the IOM's own guidance. """ +from tools.control import _optional_delay_duration from tools.lookup import ( _lookup_or_raise, _normalize_pagination, @@ -79,6 +80,18 @@ def register(handler, *, indigo_module, **_): "properties": { "id": {"type": "integer"}, "enabled": {"type": "boolean"}, + "delay": { + "type": "integer", "minimum": 0, + "description": "Seconds to wait before applying.", + }, + "duration": { + "type": "integer", "minimum": 0, + "description": ( + "Seconds until the entity flips BACK to its prior " + "state — e.g. disable with duration=1209600 disarms " + "for two weeks then self-rearms." + ), + }, }, } @@ -99,7 +112,10 @@ def register(handler, *, indigo_module, **_): ) handler.register_tool( name="trigger_enable", - description="Enable or disable an Indigo trigger by id.", + description=( + "Enable or disable an Indigo trigger by id. Optional " + "duration auto-reverts after N seconds (holiday mode)." + ), input_schema=enable_schema, handler=lambda **args: _trigger_enable_handler(args, indigo_module), ) @@ -137,7 +153,11 @@ def register(handler, *, indigo_module, **_): ) handler.register_tool( name="schedule_enable", - description="Enable or disable an Indigo schedule by id.", + description=( + "Enable or disable an Indigo schedule by id. Optional " + "duration auto-reverts after N seconds — e.g. disable " + "with duration for a self-rearming holiday mode." + ), input_schema=enable_schema, handler=lambda **args: _schedule_enable_handler(args, indigo_module), ) @@ -174,19 +194,25 @@ def _get_schedule_handler(args, indigo_module): def _trigger_enable_handler(args, indigo_module): - _reject_unknown_args(args, ("id", "enabled")) - _lookup_or_raise(indigo_module.triggers, _require_int_id(args), "trigger") + _reject_unknown_args(args, ("id", "enabled", "delay", "duration")) + entity_id = _require_int_id(args) + _lookup_or_raise(indigo_module.triggers, entity_id, "trigger") indigo_module.trigger.enable( - _require_int_id(args), value=_optional_bool(args, "enabled", None) + entity_id, + value=_optional_bool(args, "enabled", None), + **_optional_delay_duration(args), ) return {"status": "ok"} def _schedule_enable_handler(args, indigo_module): - _reject_unknown_args(args, ("id", "enabled")) - _lookup_or_raise(indigo_module.schedules, _require_int_id(args), "schedule") + _reject_unknown_args(args, ("id", "enabled", "delay", "duration")) + entity_id = _require_int_id(args) + _lookup_or_raise(indigo_module.schedules, entity_id, "schedule") indigo_module.schedule.enable( - _require_int_id(args), value=_optional_bool(args, "enabled", None) + entity_id, + value=_optional_bool(args, "enabled", None), + **_optional_delay_duration(args), ) return {"status": "ok"} diff --git a/pyproject.toml b/pyproject.toml index 3148b08..130df69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "indigo-mcp-lite" -version = "2026.7.0" +version = "2026.7.1" description = "Stdlib-only Indigo MCP server plugin (Intel Mac / Python 3.13 replacement for indigo-mcp-server)" requires-python = ">=3.10" diff --git a/tests/test_automations.py b/tests/test_automations.py index bf27c39..91140a5 100644 --- a/tests/test_automations.py +++ b/tests/test_automations.py @@ -136,3 +136,22 @@ def _missing(i): def test_unknown_arg_rejected(mock_indigo): with pytest.raises(ValueError, match="unknown argument"): _trigger_execute_handler({"id": 5, "force": True}, mock_indigo) + + +def test_enable_with_duration_self_rearm(mock_indigo): + mock_indigo.schedules.__getitem__.side_effect = lambda i: _fake_schedule(6, "S") + _schedule_enable_handler( + {"id": 6, "enabled": False, "duration": 1209600}, mock_indigo) + mock_indigo.schedule.enable.assert_called_once_with( + 6, value=False, duration=1209600) + mock_indigo.triggers.__getitem__.side_effect = lambda i: _fake_trigger(5, "T") + _trigger_enable_handler( + {"id": 5, "enabled": True, "delay": 60}, mock_indigo) + mock_indigo.trigger.enable.assert_called_once_with(5, value=True, delay=60) + + +def test_enable_rejects_negative_duration(mock_indigo): + mock_indigo.schedules.__getitem__.side_effect = lambda i: _fake_schedule(6, "S") + with pytest.raises(ValueError, match="duration"): + _schedule_enable_handler( + {"id": 6, "enabled": False, "duration": -5}, mock_indigo)