What does "AttributeError: 'bool' object has no attribute 'lower'" mean in English?

Yes, there are five other posts with this error message in the title, but the answers are all to specific situations. My questions are:

  1. What does the message mean?
  2. Why on earth does a boolean need an attribute ‘lower’? What does ‘lower’ mean?
  3. Is there also an attribute ‘upper’? And if so, what does it mean?
  4. Most importantly: how do I find out what object the message refers to?
  5. And what should I do about it?

Comments on my draft code (not working) are welcome, but rather than giving me a fish, I would prefer a fishing rod :slight_smile:

### ------------------------------------------------------------------------------------
###
###      NIGHT WALK
###      ----- ----
###
### Controls one or more lights from one or more motion and illuminance sensors
### ... but delays first, in case user operates lights him/her self
### ... and fades up slowly to avoid confusion to dementia sufferer  
###
### 17-Jun-24 | Andy Symons | First complete version
### 18-Jun-24 | Andy Symons | (a) Added stagger times (b) reversed order for turning off
### 19-Jun-24 | Andy Symons | Added optional start and end times. 
###                           Moved response conditions to variables, see 'respond_to_motion'. 
###                           Logs start conditions.   
###
### ------------------------------------------------------------------------------------

blueprint:
  name: Night Walk
  description:
    Fade up lights slowly when motion detected, but only if dark and if not
    already turned on manually or by voice assistant.
    Converse for turning off and only after a minimum on time.
  domain: automation

  input:

    motion_sensors:
      name: Motion sensor(s)
      selector:
        entity:
          filter:
            domain: binary_sensor
            device_class: motion
          multiple: true

    dimmable_lights:
      name: Dimmable light(s)
      description: Light(s) to be faded up and down
      selector:
        entity:
          filter:
            domain: light
          multiple: true
      default: []

    non_dimmable_lights:
      name: Non-dimmable light(s)
      description: Light(s) to be simply turned on and off
      selector:
        entity:
          filter:
            - domain: light
          multiple: true
      default: []

    test_mode_switch:
      name: Test mode switch
      description: Switch or sensor that can be turned on to override the Night time / darkness conditions
      selector:
        entity:
          filter:
            - domain: 
              - binary_sensor
              - input_boolean
              - switch
          multiple: false
      default: ""

    night_sensor:
      name: Night sensor
      description: Sensor that is 'true' for night time (the hours of darkness or low sun elevation) 
      selector:
        entity:
          filter:
            - domain:
              - binary_sensor
              - switch
          multiple: false
      default: ""

    illuminance_sensors:
      name: Luminance sensor(s)
      selector:
        entity:
          filter:
            domain: sensor
            device_class: illuminance
          multiple: true
      default: []

    stagger_time:
      name: Stagger time
      description: Gap between fade or switch starting times for of each light 
      selector:
        number:
          min: 0
          max: 30
          unit_of_measurement: seconds
      default: 3

    start_time:
      name: Start time
      description: Earliest time at which this automation is in operation  
      selector:
        time:
      default: "00:00"

    end_time:
      name: Start time
      description: Earliest time at which this automation is in operation  
      selector:
        time:
      default: "00:00"

    fade_up_time:
      name: Fade up time
      description: Time to fade up from 0 to 100%
      selector:
        number:
          min: 0
          max: 30
          unit_of_measurement: seconds
      default: 20

    fade_down_time:
      name: Fade down time
      description: Time to fade down from 100 to 0%
      selector:
        number:
          min: 0
          max: 30
          unit_of_measurement: seconds
      default: 15

    on_delay:
      name: On delay
      description: Wait time before acting on detected motion
      selector:
        number:
          min: 0
          max: 120
          unit_of_measurement: seconds
      default: 3
      
    off_delay:
      name: Off delay
      description: Time to leave the lights on after last motion is detected.
      selector:
        number:
          min: 0
          max: 120
          unit_of_measurement: seconds
      default: 3

    on_timer:
      name: On timer
      description: The timer to be used to time the on-time
      selector:
        entity:
          filter:
            - domain:
                - timer
          multiple: false

    minimum_on_time:
      name: Minimum on time
      description: Wait time before automatically turning off
      selector:
        number:
          min: 0
          max: 60
          unit_of_measurement: minutes
      default: 5

    logging_service_name:
      name: LOGGING - Specify the service for logging
      description: Enter service name (not filename) as a string
      default: ""

mode: queued
max_exceeded: silent

trigger:

  # motion detected
  - platform: state
    id: motion_detected
    entity_id: !input motion_sensors
    to: "on"
    for: !input on_delay

  # motion clear
  - platform: state
    id: motion_clear
    entity_id: !input motion_sensors
    to: "off"
    for: !input off_delay

  # On timer end
  - platform: state
    id: timer_ended
    entity_id: !input on_timer
    to: idle

variables:
  #
  # Log message preamble identifies the source of the log messages
  #
  log_message_preamble: "{{ this.attributes.friendly_name }}"
  #
  # Determine if we are currently in test mode. Defaults to False if there is no test switch.  
  #
  input_test_mode_switch: !input test_mode_switch
  test_mode: > 
    {% if states(input_test_mode_switch) is defined %}
      {{ states(input_test_mode_switch) == 'on' }} 
    {% else %}
      {{ False }}
    {% endif %}
  #
  # Determine if we are currently witihn the operating time range
  #
  input_start_time: !input start_time  
  input_end_time: !input end_time
  within_time_range: >          
    {% set start_timestamp = as_timestamp(today_at(input_start_time)) %}
    {% set end_timestamp   = as_timestamp(today_at(input_end_time)) %}
    {% set now_timestamp   = as_timestamp(now()) %}
    {% if end_timestamp == start_timestamp %}
      {{ True }}
    {% elif end_timestamp > start_timestamp %}
      {{ now_timestamp >= start_timestamp and now_timestamp < end_timestamp }}
    {% else %}
      {{ now_timestamp >= start_timestamp or  now_timestamp < end_timestamp }} 
    {% endif %}
  #
  # Determine if it is night. If there is no sensor then defaults to false.
  #
  input_night_sensor: !input night_sensor
  is_night: >
    {% if states(input_night_sensor) is defined %}
      {{ states(input_night_sensor) }}
    {% else %}
      {{ False }}
    {% endif %}
  #
  # Determine if luminance is low. If there is no sensor then defaults to false. 
  #
  input_illuminance_sensors: !input illuminance_sensors
  illuminance_is_low: >     
    {% set ns = namespace(is_dark = false) %}
    {% for sensor in input_illuminance_sensors %}
      {% if states(sensor) is defined %} 
        {% if states(sensor) < 50 %} 
          {% set ns.is_dark = true %}
        {% endif %}
      {% endif %}
    {% endfor %}
    {{ ns.is_dark}} 
  #
  # Determine whether to respond to motion (based on the above). 
  #
  respond_to_motion: >
    {% if states(test_mode) %}
      {{ True }} 
    {% elif not states(within_time_range) %} 
      {{ False }} 
    {% else %}
      {{ states(is_night) or states(illuminance_is_iow) }}
    {% endif %}
  #
  # Identify the lights to change 
  #
  input_dimmable_lights: !input dimmable_lights
  input_non_dimmable_lights: !input non_dimmable_lights

  # 1 Lights to fade up = dimmable lights in listed order that are currently off    
  lights_to_fade_up: >
    {% set input_list = input_dimmable_lights %}
    {% set ns = namespace(output_list=[]) %}
    {% for light in input_list %} 
      {% if states(light) == 'off' %} 
        {% set ns.output_list = ns.output_list + [light] %}
      {% endif %}
    {% endfor %}
    {{ ns.output_list }}

  # 2 Lights to turn on = non-dimmable lights in listed order that are currently off    
  lights_to_turn_on: >
    {% set input_list = input_non_dimmable_lights %}
    {% set ns = namespace(output_list=[]) %}
    {% for light in input_list %} 
      {% if states(light) == 'off' %} 
        {% set ns.output_list = ns.output_list + [light] %}
      {% endif %}
    {% endfor %}
    {{ ns.output_list }}

  # 3 Lights to fade down = dimmable lights in reverse of listed order that are currently on    
  lights_to_fade_down: >
    {% set input_list = input_dimmable_lights %}
    {% set ns = namespace(output_list=[]) %}
    {% for light in input_list | reverse %} 
      {% if states(light) == 'on' %} 
        {% set ns.output_list = ns.output_list + [light] %}
      {% endif %}
    {% endfor %}
    {{ ns.output_list }}

  # 4 Lights to turn off = non-dimmable lights in reverse of listed order that are currently on    
  lights_to_turn_off: >
    {% set input_list = input_non_dimmable_lights %}
    {% set ns = namespace(output_list=[]) %}
    {% for light in input_list | reverse %} 
      {% if states(light) == 'on' %} 
        {% set ns.output_list = ns.output_list + [light] %}
      {% endif %}
    {% endfor %}
    {{ ns.output_list }}

action:
  ## -------------------------------------------------------------------------------------------------
  ## ACTION[0,1,2] -- LOG BLANK LINE, THE TRIGGER AND THE STARTING CONDITIONS
  ## -------------------------------------------------------------------------------------------------
  # First a blank line to start the new entry 
  - service: script.logfile_entry
    data: 
      notification_service: !input logging_service_name
      logfile_title: Night Walk
      message_preamble: "{{ log_message_preamble }}"
      message_body: ---

  # Then the trigger details line 
  - service: script.logfile_entry
    data: 
      notification_service: !input logging_service_name
      logfile_title: Night Walk
      message_preamble: "{{ log_message_preamble }}"
      message_body: >
        {% if trigger.id is undefined %}
          {{ "Run manually." }} 
        {% elif trigger.entity_id is undefined %}
          {{ "Triggered by: " + trigger.id | string + " (no entity id)" }}
        {% else %} 
          {{ "Triggered by: " + trigger.id | string + " from '" + state_attr(trigger.entity_id, 'friendly_name') 
              + "', state = " + states(trigger.entity_id) }}
        {% endif %}

  # Then log the starting conditions line 
  - service: script.logfile_entry
    data: 
      notification_service: !input logging_service_name
      logfile_title: Night Walk
      message_preamble: "{{ log_message_preamble }}"
      message_body: >
          {{ "Conditions: test_mode = " + states(test_mode)|string  }}

  ## -------------------------------------------------------------------------------------------------
  ## ACTION[3] -- ACT ON THE TRIGGER
  ## -------------------------------------------------------------------------------------------------

  - choose:
      #
      # ACTION[3]CHOOSE[0] Motion detected and it is within the operating time range AND dark OR in test mode 
      #
      - conditions:
        - alias: "Motion detected"
          condition: trigger
          id: motion_detected

        - alias: "Respond to motion (see variables definitions)"
          condition: template 
          value_template: "{{ states(respond_to_motion) }}"
        # Note: this condition only appears here, so fade and turn off will still happen if the conditions change in the meantime  

        sequence:
          # ACTION[3]CHOOSE[0]SEQUENCE[0] Fade up dimmable lights
          - if:
              - condition: template 
                value_template: "{{ lights_to_fade_up | count > 0  }}"
            then:
              - repeat: 
                  for_each: "{{ lights_to_fade_up }}"
                  sequence: 
                    - service: light.turn_on
                      data:
                        brightness_pct: 100
                        transition: !input fade_up_time
                      target:
                        entity_id: "{{ repeat.item }}"
                    # timer started only if a light is actually turned on
                    - service: timer.start
                      metadata: {}
                      data:
                        duration:
                          minutes: !input minimum_on_time
                      target:
                        entity_id: !input on_timer
                    # stagger the start times
                    - delay: 
                        seconds: !input stagger_time

          # ACTION[3]CHOOSE[0]SEQUENCE[1] Turn on any non-dimmable lights
          - if: 
              - condition: template 
                value_template: "{{ lights_to_turn_on | count > 0 }}"
            then:
              - repeat: 
                  for_each: "{{ lights_to_turn_on }}"
                  sequence: 
                    - service: light.turn_on
                      data: {} 
                      target:
                        entity_id: "{{ repeat.item }}"
                    # timer started only if a light is actually turned on
                    - service: timer.start
                      metadata: {}
                      data:
                        duration:
                          minutes: !input minimum_on_time
                      target:
                        entity_id: !input on_timer
                    # stagger the start times
                    - delay: 
                        seconds: !input stagger_time
      #
      # ACTION[3]CHOOSE[1] Motion clear 
      #
      - conditions: 
        - alias: "Motion clear"
          condition: trigger
          id: motion_clear
        - alias: "The on timer is not still running" 
          condition: state
          entity_id: !input on_timer
          state: "idle"
        sequence:
          # Turn off non-dimmable lights  
          - if: 
            - condition: template 
              value_template: "{{ lights_to_turn_off | count > 0 }}"
            then:
              - repeat: 
                  for_each: "{{ lights_to_turn_off }}"
                  sequence: 
                    - service: light.turn_off
                      data: {} 
                      target:
                        entity_id: "{{ repeat.item }}"
                    # stagger the turn-off times
                    - delay: 
                        seconds: !input stagger_time 
        # Fade down dimmable lights 
          - if:
            - condition: template 
              value_template: "{{ lights_to_fade_down | count > 0 }}"
            then: 
              - repeat: 
                  for_each: "{{ lights_to_fade_down }}"
                  sequence: 
                    - service: light.turn_on
                      data:
                        brightness_pct: 0
                        transition: !input fade_down_time
                      target:
                        entity_id: "{{ repeat.item }}"
                    # stagger the fade-down start times
                    - delay: 
                        seconds: !input stagger_time 

      #
      # ACTION[3]CHOOSE[2] Timer ended
      #
      - conditions: 
        - alias: " timer ended"
          condition: trigger
          id: timer_ended
        - alias: "All motion sensors clear"
          condition: state
          entity_id: !input motion_sensors
          state: "off"
        sequence:

          # Turn off non-dimmable lights  
          - if: 
            - condition: template 
              value_template: "{{ lights_to_turn_off | count > 0 }}"
            then:
              - repeat: 
                  for_each:  "{{ lights_to_turn_off }}"
                  sequence: 
                    - service: light.turn_off
                      data: {} 
                      target:
                        entity_id: "{{ repeat.item }}"
                    # stagger the turn-off times
                    - delay: 
                        seconds: !input stagger_time 
        # Fade down dimmable lights 
          - if:
            - condition: template 
              value_template: "{{ lights_to_fade_down | count > 0 }}"
            then: 
              - repeat: 
                  for_each: "{{ lights_to_fade_down }}"
                  sequence: 
                    - service: light.turn_on
                      data:
                        brightness_pct: 0
                        transition: !input fade_down_time
                      target:
                        entity_id: "{{ repeat.item }}"
                    # stagger the fade-down start times
                    - delay: 
                        seconds: !input stagger_time 

  1. The message means just what you think it does. That a bool object does not have an attribute lower.

  2. It does not, but something tried to use that attribute. Lower can be anything, like lower case for a string or lower limit for a timer. The thing here is that the code that tried to access the lower attribute probably expected another type of object.

  3. Maybe and maybe not. I do not think there is not an upper attribute for a bool, but it could be for the object type that was expected instead of the bool one.

  4. That can be hard to say. The error can be directly related to your code or it could be indirectly related, which means you used a piece of code and that code then called a code further down that cause the issue.
    This is just an example on how such issue can rise and though I use a timer object in the example, then it is not guaranteed to be the same as in HA.

A timer object might be defined with 3 attributes.

  • A boolean called enabled, which tells if the timer is running or not.
  • An integer value called lower to say what value the timer should be set to when a reset is called.
  • An integer value called upper that say when the timer should automatically stop by setting the enabled value to false.

Your code sets up the timer with the following piece.

timer.lower=0
timer.upper=60
timer=true

You then call timer.reset and you get the error.
What have happend here is that the lower and upper value was set correctly, but then you should have written timer.enabled=true, but instead wrote timer=true.
Since true is a value for a bool, then the timer object was set to be equal to a bool object with the value true. The old timer object with upper and lower are now gone.

  1. first thing is to check all your own variables that they are in fact what you expect them to be.
    Be careful with reading the actual variable names in your code and not what you think it should read.
    If that does not give any clues, then start to comment out code to figure out what runs and what does not.
1 Like

Thanks for the detailed reply.

  1. The code is my own, see embedded.
  2. I am just using boolean variables. I have not declared ‘lower’ and cannot imagine where it came from or why it should matter!
  3. My best guess at where the error is (since the message doesn’t say) is the last thing I changed, which is the variable ‘within_time_range’.
  4. However, I have had this error several times (sometimes with some location info) in this blueprint but because I don’t understand it I can only use trial and error to fix it! That is very frustrating, hence my question.
  5. I can’t even print the booleans to a log flie to see what’s going on!

So what DO I do?