Skip to content

AttributeError: 'StateVal' object has no attribute 'old' #831

@ALERTua

Description

@ALERTua

This is not about the 2.0.0; this behavior is old.
Using the .old StateVal field can not be reliable, as it exists only if it is set.

OCCUPANCY = 'binary_sensor.office_motion_cat_mat_presence'

@state_trigger(
    f"{OCCUPANCY} == 'on' and {OCCUPANCY}.old == 'off'",
    watch=[
        OCCUPANCY,
    ],
    state_hold=10,
    state_hold_false=15,
)
def cat_mat_on(trigger_type=None, var_name=None, value=None, old_value=None, context=None, **kwargs):
    ...

results in

2026-05-03 11:12:05.212 ERROR (MainThread) [custom_components.pyscript.file.cat_mat] 
  File "/config/custom_components/pyscript/decorators/base.py", line 56, in check_expression_vars
    return await self._ast_expression.eval(state_vars)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/pyscript/cat_mat.py", line 1, in file.cat_mat.cat_mat_on_func @state_trigger()
    binary_sensor.office_motion_cat_mat_presence == 'on' and binary_sensor.office_motion_cat_mat_presence.old == 'off'
                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'StateVal' object has no attribute 'old'

While I know I can use the old_value kwarg, I have several @state_trigger decorators around this function. To use old_value, I am forced to have a dedicated dummy function that contains the old_value check and returns the main function. This balloons the code and the newcomer experience.
E.g.:

def cat_mat_on(trigger_type=None, var_name=None, value=None, old_value=None, context=None, **kwargs):
    ...

@state_trigger(
    f"{MOTION} in ('large')",
    watch=[
        MOTION,
    ],
    state_hold=5,
    state_hold_false=15,
)
def cat_mat_on_1(trigger_type=None, var_name=None, value=None, old_value=None, context=None, **kwargs):
    cat_mat_on(trigger_type=trigger_type, var_name=var_name, value=value, old_value=old_value, context=context, **kwargs)

@state_trigger(
    f"{OCCUPANCY} == 'on'",
    watch=[
        OCCUPANCY,
    ],
    state_hold=10,
    state_hold_false=15,
)
def cat_mat_on_2(trigger_type=None, var_name=None, value=None, old_value=None, context=None, **kwargs):
    if old_value != 'off':
        return
    
    cat_mat_on(trigger_type=trigger_type, var_name=var_name, value=value, old_value=old_value, context=context, **kwargs)

Having this field's default value would solve this for me.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions