Look at the very first post pinned at the top of the forum telling you how to ask a good question… Start with a title that actually says what you need help with and go from there…
I don’t have insight into how the is_state_attr function performs its comparisons but it’s clear it doesn’t like comparing to a null value. In contrast, the equivalence operator == handles a null value properly.
Your second example, involving true and false, works as one would expect. A boolean value (true/false) is never a null value so is_state_attr handles it correctly.
EDIT
The function is_state_attr is located in Home Assistant’s template.py file. I believe this line of code is responsible for how it handles a comparison with None (null value).
return attr is not None and attr == value
It returns a result but only if its notNone (except in this case, it isNone so it returns nothing).
The title should capture the essence of your question: Why does None appear to be handled inconsistently?
I may have worded it that way had I realized that (None handled inconsistently unlike True) before submitting the question. As you would notice, I only observed that after results of further investigation (my second post).
Edit: Nevertheless, I will try to use better titles next time.
Null value is no value which isn’t quite the same as unknown value. It may sound like quibbling about semantics but there is a difference. If I haven’t been able to acquire a value then it’s reported to be unknown (a string). If I acquired it and it’s null then it’s reported to be None (a constant).
FWIW, when Home Assistant reports unknown that’s a string value. Here’s an example of an entity with an unknown state. When compared to the string 'unknown' it evaluates to True. It’s False when compared to unknown (there’s no constant called unknown) or to None (because it’s not null; its value is … unknown).
The answer is in the code. Basically, is_state_attr checks attr is not None, which guarantees that it will always return False if the value is None. Your check using == bypasses that extra condition.
I don’t see this mentioned in the documentation, so it probably needs to be updated to mention that behavior.
I think the is_state_attr function is dealing with a ‘corner-case situation’. Here’s what I mean:
is_state_attr calls the state_attr function with this line:
attr = state_attr(hass, entity_id, name)
The state_attr function gets the entity with this:
state_obj = _get_state(hass, entity_id)
Then, if state_obj is not None, it proceeds to get the attribute’s value and returns it to is_state_attr.
if state_obj is not None:
return state_obj.attributes.get(name) # if attribute value is None, it returns None
However, and here’s the interesting part, is state_obj is None then it immediately returns None to is_state_attr (which, in turn, reports False). Effectively, None is used like a flag to indicate the entity_id it received doesn’t exist.
The ‘corner-case situation’ is if the entity does exist but has a value that’s None. The following two lines get executed and the attribute’s true value, None, is returned to is_state_attr.
if state_obj is not None:
return state_obj.attributes.get(name)
In other words, the attribute’s legitimate value, None, is misinterpreted to be the flag that’s used when indicating the entity_id is non-existent.
Not to say that I agree or disagree with the code, but I interpret the logic more like a shortcut - if entity_id does not exist, then attributes (and values) also do not exist.
The resolution for is_state_attr() is handled by this line of code:
return attr is not None and attr == value
attr contains the value inside the attribute. So when attr is None, that section of the if statement resolves to false. Even if attr == value, the first section of the return statement resolves false, so no matter what, you’ll get false if the attribute is null/None.