From ed9e8ca5f75ff0e08e308376d56ff7e24e66d183 Mon Sep 17 00:00:00 2001 From: Dmitrii Amelin Date: Tue, 14 Apr 2026 20:01:13 +0200 Subject: [PATCH 1/2] allow None for time_active hold_off and state_trigger watch (#819) --- .../pyscript/decorators/state.py | 2 +- .../pyscript/decorators/timing.py | 6 +-- tests/test_function.py | 39 +++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/custom_components/pyscript/decorators/state.py b/custom_components/pyscript/decorators/state.py index 7b588b9..4c1b44a 100644 --- a/custom_components/pyscript/decorators/state.py +++ b/custom_components/pyscript/decorators/state.py @@ -79,7 +79,7 @@ class StateTriggerDecorator(TriggerDecorator, ExpressionDecorator, AutoKwargsDec vol.Optional("state_hold"): vol.Any(None, cv.positive_float), vol.Optional("state_hold_false"): vol.Any(None, cv.positive_float), vol.Optional("state_check_now"): cv.boolean, - vol.Optional("watch"): vol.Coerce(set[str], msg="should be type list or set"), + vol.Optional("watch"): vol.Any(None, vol.Coerce(set), msg="should be type list or set"), vol.Optional("__test_handshake__"): vol.Coerce(list), } ) diff --git a/custom_components/pyscript/decorators/timing.py b/custom_components/pyscript/decorators/timing.py index 6c2be06..67a4371 100644 --- a/custom_components/pyscript/decorators/timing.py +++ b/custom_components/pyscript/decorators/timing.py @@ -30,15 +30,15 @@ class TimeActiveDecorator(TriggerHandlerDecorator, AutoKwargsDecorator): name = "time_active" args_schema = vol.Schema(vol.All([vol.Coerce(str)], vol.Length(min=0))) - kwargs_schema = vol.Schema({vol.Optional("hold_off", default=0.0): cv.positive_float}) + kwargs_schema = vol.Schema({vol.Optional("hold_off", default=0.0): vol.Any(None, cv.positive_float)}) - hold_off: float + hold_off: float | None last_trig_time: float = 0.0 async def handle_dispatch(self, data: DispatchData) -> bool: """Handle dispatch.""" - if self.last_trig_time > 0.0 and self.hold_off > 0.0: + if self.last_trig_time > 0.0 and self.hold_off is not None and self.hold_off > 0.0: if time.monotonic() - self.last_trig_time < self.hold_off: return False diff --git a/tests/test_function.py b/tests/test_function.py index 419c02c..e8d2058 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -750,6 +750,45 @@ def func10d(var_name=None, value=None, trigger_type=None, context=None, old_valu ) +@pytest.mark.asyncio +async def test_trigger_kwargs_none(hass): + """Test that explicit None kwargs are accepted for trigger decorators.""" + notify_q = asyncio.Queue(0) + + await setup_script( + hass, + notify_q, + None, + [dt(2020, 7, 1, 10, 59, 59, 999998)], + """ +seq_num = 0 + +@state_trigger("True", watch={"pyscript.var1"}) +@time_active(hold_off=None) +def func1(var_name=None, value=None): + global seq_num + + seq_num += 1 + pyscript.done = ["hold_off", seq_num, var_name, value] + +@state_trigger("pyscript.var2 == '2'", watch=None) +def func2(var_name=None, value=None): + pyscript.done = ["watch_none", var_name, value] +""", + ) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + hass.states.async_set("pyscript.var1", 2) + assert literal_eval(await wait_until_done(notify_q)) == ["hold_off", 1, "pyscript.var1", "2"] + + hass.states.async_set("pyscript.var1", 0) + assert literal_eval(await wait_until_done(notify_q)) == ["hold_off", 2, "pyscript.var1", "0"] + + hass.states.async_set("pyscript.var2", 2) + assert literal_eval(await wait_until_done(notify_q)) == ["watch_none", "pyscript.var2", "2"] + + @pytest.mark.asyncio async def test_state_trigger_time(hass, caplog): """Test state trigger.""" From df01c7b0ce7c449b95b46148447bcf689fb45aaa Mon Sep 17 00:00:00 2001 From: Dmitrii Amelin Date: Tue, 14 Apr 2026 23:16:37 +0200 Subject: [PATCH 2/2] document legacy_decorators reload limitation --- docs/configuration.rst | 7 ++++--- docs/reference.rst | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index e46fcd7..d338acc 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -20,9 +20,10 @@ Configuration Starting with version ``2.0.0``, pyscript uses the new decorator subsystem by default. If you run into a problem in the new implementation, you can temporarily set - ``legacy_decorators: true`` to switch back to the legacy subsystem. If you do that, - please also file a bug report in the `GitHub issue tracker `__ - so the problem can be fixed. + ``legacy_decorators: true`` to switch back to the legacy subsystem. This setting takes + effect only after restarting Home Assistant. If you do that, please also file a bug report + in the `GitHub issue tracker `__ so + the problem can be fixed. - Add files with a suffix of ``.py`` in the folder ``/pyscript``. - Restart HASS after installing pyscript. diff --git a/docs/reference.rst b/docs/reference.rst index 8616cee..a03f8a3 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -13,7 +13,8 @@ time by selecting "configure" under Pyscript Python scripting on the Settings -> Devices & services -> Integrations page. For the new settings to take effect, you will need to either select "Reload" from the overflow menu (button with three dots) next to pyscript or call the ``pyscript.reload`` -service from Developer tools -> Services. +service from Developer tools -> Services, except for ``legacy_decorators``, +which currently takes effect only after restarting Home Assistant. Alternatively, for yaml configuration, add ``pyscript:`` to ``/configuration.yaml``. You can't mix these two methods - your initial choice determines how you should update @@ -35,8 +36,9 @@ in ``/configuration.yaml``: Starting with version ``2.0.0``, pyscript uses the new decorator subsystem by default. If you find a problem in the new implementation, you can temporarily set -``legacy_decorators: true`` to switch back to the legacy subsystem. If you do, -please also file a bug report in the `GitHub issue tracker `__ +``legacy_decorators: true`` to switch back to the legacy subsystem. This setting takes effect +only after restarting Home Assistant. If you do, please also file a bug report in the +`GitHub issue tracker `__ so the problem can be fixed. It is recommended you put your pyscript configuration its own ``yaml`` file in the ``pyscript``