Skip to content

Conversation

@dmamelin
Copy link
Contributor

@dmamelin dmamelin commented Oct 9, 2025

StateVal Helpers

  • New StateVal helpers wrap Home Assistant’s forgiving converters, so input_number.brightness.as_int(default=0) or sensor.timestamp.as_datetime() just work - no more manual int(...)/float(...) juggling or wrapping those casts in try/except.
  • Added availability checks: .is_unknown(), .is_unavailable(), and .has_value() replace ad-hoc comparisons with unavailable/unknown.

Example (for illustration only):

@state_trigger("float(sensor.some_number)>10") #got error on unavailable/unknown
def old_style():
    try:
        sensor_value = int(sensor.another_sensor)
    except Exception:
        sensor_value = 0
    if sensor_value > 10 or binary_sensor.motion == "on":
        try:
            temperature = float(sensor.temperature)
            if temperature > 20:
                if climate.ac not in ["unavailable", "unknown"]: #check device online
                    climate.ac.set_temperature(temperature=sensor_value+10)
        except Exception:
            pass

@state_trigger("sensor.some_number.as_float(default=0)>10") # no error on unavailable/unknown
def new_style():
    sensor_value = sensor.another_sensor.as_int(default=0)
    
    if sensor_value > 10 or binary_sensor.motion.as_bool(False):
        temperature = sensor.temperature.as_float(-100)
        if temperature > 20 and climate.ac.has_value():
            climate.ac.set_temperature(temperature=sensor_value+10)

@craigbarratt
Copy link
Member

This is awesome! Thanks for the PR.

@craigbarratt craigbarratt merged commit 29a9891 into custom-components:master Oct 9, 2025
6 checks passed
@dmamelin
Copy link
Contributor Author

dmamelin commented Oct 9, 2025

Found a regression affecting state_active and as_*; working on a fix.

@dmamelin dmamelin deleted the feature/stateval-conversion-helpers branch October 11, 2025 17:47
@stefanuytterhoeven
Copy link

Very nice!

@ALERTua
Copy link
Contributor

ALERTua commented Dec 10, 2025

@dmamelin will this work on .old sensor values?

@state_trigger(  # boiler boost mode if the water is too cold
    f"{BOTTOM_TEMP}.as_float(default={MINIMUM_TEMP}) < {MINIMUM_TEMP} and {BOTTOM_TEMP}.old.as_float(default={MINIMUM_TEMP}) >= {MINIMUM_TEMP} and {BOTTOM_TEMP}.as_float(default={MINIMUM_TEMP}) > 0",
    watch=[BOTTOM_TEMP],
    state_hold_false=60,
)

Thank you!

@dmamelin
Copy link
Contributor Author

dmamelin commented Dec 10, 2025

@ALERTua Yes, of course.
This works for all available StateVal instances.

BOTTOM_TEMP = "sensor.heatingpump_watboxtemset"
MINIMUM_TEMP = 42


@state_trigger(f""
               f"{BOTTOM_TEMP}.as_float(default={MINIMUM_TEMP}) < {MINIMUM_TEMP} and "
               f"{BOTTOM_TEMP}.old.as_float(default={MINIMUM_TEMP}) >= {MINIMUM_TEMP} and "
               f"{BOTTOM_TEMP}.as_float(default={MINIMUM_TEMP}) > 0")
def test(**kwargs):
    log.info(f"{kwargs}")

{'trigger_type': 'state', 'var_name': 'sensor.heatingpump_watboxtemset', 'value': '41.0', 'old_value': '42.0', 'context': <homeassistant.core.Context object at 0x7f4945af7f60>}

@ALERTua
Copy link
Contributor

ALERTua commented Dec 13, 2025

alas, if the entity_id is unavailable (or does not exist at all), there's an AttributeError

2025-12-13 10:01:16.016 ERROR (MainThread) [custom_components.pyscript.file.autovents.func_laundry_auto_fan_on] Exception in <file.autovents.func_laundry_auto_fan_on @state_trigger()> line 1:
    any([sensor.laundry_temperature_latest.as_float(default=0) >= 30, sensor.laundry_humidity_latest.as_float(default=0) >= 48, sensor.alert_server_cpu_temperature.as_float(default=0) >= 80, sensor.mini_cpu_temperature.as_float(default=0) >= 80])
                                                                                                                                ^
AttributeError: 'NoneType' object has no attribute 'as_float'

sensor.alert_server_cpu_temperature has become unavailable due to the power outage (the server has been automatically turned off), and its StateVal somehow became None.
Before this PR I worked around this using the blunt

def float_(obj, default=None):
    try:
        return float(obj)
    except:
        if default is not None:
            return default
        return -666.0

@dmamelin
Copy link
Contributor Author

@ALERTua Yes, this is an architectural issue. I have a few ideas, but I’m currently busy with another large PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants