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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions