UK Blood donation Custom Component

I’m a regular blood platelet donor, so I’ve created a blood donor custom component.
It accesses my donation stats from the Blood.co.uk website (through their API) and allows booking of appointment slots.
I’ve set it up so it automatically books a new appointment every time I donate.
The appointment will be booked that is nearest to my target date and time.

Mainly a bit of fun, but if anyone is interested in the code, I’d be happy to share.


Thanks,
Bruce.

4 Likes

Hi @BruceH5200

As a regular donor too, I’d like to have a play with this.

Thanks

I’ve uploaded it to:

I don’t think there is anything personal in the code, but please let me know if you spot anything.
Let me know if you manage to make it work.

And please bear in mind it’s thrown together.

Thanks,
Bruce.

Hi @BruceH5200

When I try installing with HACS I see this:

I’ll scip HACS and copy the folder accross.

Thanks

Yeah, it said in the README, haven’t actually done the HACS stuff so install manually.
I’ve reconfigure it now so it should work. Haven’t tested it yet though.

Hi @BruceH5200

I’ve installed your integration. I’m going to have a play with the appointment booking service soon.

I did notice this in my HA log:

Detected that custom integration ‘blood_donor’ calls async_forward_entry_setup for integration, blood_donor with title: g****@****.org.uk and entry_id: *********, which is deprecated, await async_forward_entry_setups instead at custom_components/blood_donor/blood_donor.py, line 70: await hass.config_entries.async_forward_entry_setup(entry, “sensor”). This will stop working in Home Assistant 2025.6, please report it to the author of the ‘blood_donor’ custom integration

I was going to post this to your GitHub issues but I didn’t seem to be able to. You’re probably aware of this anyway.

Thanks for your work on this

Also, where can I find the venue_id’s?

I go you go into the blood.co.uk and start the booking process I think you’ll see it in the URL.
If you don’t. I can add a venue service to let you look it up.

Thanks @BruceH5200

I’m calling the following action:

action: blood_donor.booking_helper
data:
  venue_id: SL85A
  target_date: "2025-06-04"
  target_time: "12:00"
  tolerance_hours: 12
  procedure_code: WB
  target_day_of_week: wednesday

In my logs I get this:

Logger: homeassistant.components.websocket_api.http.connection
Source: custom_components/blood_donor/booking_helper.py:391
integration: Home Assistant WebSocket API (documentation, issues)
First occurred: 15:23:58 (17 occurrences)
Last logged: 15:56:56

[140090946789024] Error handling message: Unknown error (unknown_error) Administrator from 127.0.0.1 (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0)
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/decorators.py", line 28, in _handle_async_response
    await func(hass, connection, msg)
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 816, in handle_execute_script
    script_result = await script_obj.async_run(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
        msg.get("variables"), context=context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 1808, in async_run
    return await asyncio.shield(create_eager_task(run.async_run()))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 464, in async_run
    await self._async_step(log_exceptions=False)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 528, in _async_step
    self._handle_exception(
    ~~~~~~~~~~~~~~~~~~~~~~^
        ex, continue_on_error, self._log_exceptions or log_exceptions
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 558, in _handle_exception
    raise exception
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 526, in _async_step
    await getattr(self, handler)()
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 762, in _async_call_service_step
    response_data = await self._async_run_long_action(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<9 lines>...
    )
    ^
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 727, in _async_run_long_action
    return await long_task
           ^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2794, in async_call
    response_data = await coro
                    ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2837, in _execute_service
    return await target(service_call)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/blood_donor/booking_helper.py", line 391, in async_booking_helper_service
    target_day_idx = DAYS_OF_WEEK[day_of_week]  # The day user requested (e.g., "wednesday" = 2)
                                  ^^^^^^^^^^^
UnboundLocalError: cannot access local variable 'day_of_week' where it is not associated with a value

What have I done wrong?

Also, I think it would be better if all the actions returned data in the response variable, rather than as persistant notifications.

Hi @BruceH5200

If I call this action:

action: blood_donor.available_appointments
data:
  venue_id: SL85A
  procedure_code: WB
  start_date: "2025-06-06"
  end_date: "2025-06-06"

I get this in the notification:

service: blood_donor.session_slots
data:
  session_id: "CS42KF"
  session_date: "2025-06-06T00:00:00"

If I call that, I get for example:

service: blood_donor.book_appointment
data:
  session_id: "CS42KF"
  session_date: "2025-06-06T00:00:00"
  session_time: "T0825"
  venue_id: "TB78A"
  procedure_code: "WHB"

But that’s a different venue_id

Is that a bug?

yes, it looks like it, that is my venue_id, i must have hardcoded it. I’ll take a look. Also I’ve got the HACS install working now, so if you use that it will keep you up to date.

fixed, I think

btw.
I have this automation that theoretically books me another appointment when it registers I’ve donated.


# Blood Donor Booking Automation
# This automation triggers when your blood donation credit increases,
# and books a platelet donation appointment at your preferred location and time
automation:
  - alias: Book Blood Donation When Credit Increases
    id: book_blood_donation
    mode: single
    max_exceeded: silent  
    description: Books a platelet donation when donation credits increase and sends a Telegram notification
    trigger:
      - platform: state
        entity_id: sensor.blood_donor_donation_credit
    condition:
      # Only run when previous state was numeric and >0 and new state is greater than previous state
      - condition: template
        value_template: >-
          {% set from_state = trigger.from_state.state %}
          {% set to_state = trigger.to_state.state %}
          {% if from_state not in ['unavailable', 'unknown', 'error', 'none', ''] and to_state not in ['unavailable', 'unknown', 'error', 'none', ''] %}
            {% if from_state | float > 0 and to_state | float > from_state | float %}
              true
            {% else %}
              false
            {% endif %}
          {% else %}
            false
          {% endif %}
    action:
      # Call the blood donor booking helper service and store the response
      - service: blood_donor.booking_helper
        data:
          venue_id: AB12A
          target_day_of_week: thursday
          target_time: "12:00:00"
          tolerance_hours: 2
          min_days_from_last_appointment: 14
          procedure_code: PLT
          #auto_book: true
          auto_book: false
        response_variable: booking_response
      
      # Choose based on booking success
      - choose:
          # Successful booking
          - conditions:
              - condition: template
                value_template: "{{ booking_response.success }}"
              - condition: template
                value_template: "{{ booking_response.appointment is not none }}"
              - condition: template
                value_template: "{{ booking_response.message is search('booked successfully') }}"
            sequence:
              # Send Telegram notification with appointment details from the response data
              - service: notify.telegram_bruce
                data:
                  title: Blood Donation Appointment Booked
                  message: >-
                    A platelet donation appointment has been automatically booked with the following details:
                    
                    Date: {{ booking_response.appointment.date }} ({{ booking_response.appointment.day_of_week }})
                    
                    Time: {{ booking_response.appointment.time }}
                    
                    Location: {{ booking_response.appointment.venue_id }}
                    
                    Procedure: {{ booking_response.appointment.procedure }}
                    
                    This was booked automatically because your donation credit increased to {{ trigger.to_state.state }}.
                    
                    The appointment is {{ booking_response.appointment.time_difference }} hours from your preferred time.
          
          # Found appointment but not booked
          - conditions:
              - condition: template
                value_template: "{{ booking_response.success }}"
              - condition: template
                value_template: "{{ booking_response.appointment is not none }}"
              - condition: template
                value_template: "{{ not (booking_response.message is search('booked successfully')) }}"
            sequence:
              - service: notify.telegram_bruce
                data:
                  title: Blood Donation Appointment Found (Not Booked)
                  message: >-
                    A suitable platelet donation appointment was found but not booked:
                    
                    Date: {{ booking_response.appointment.date }} ({{ booking_response.appointment.day_of_week }})
                    
                    Time: {{ booking_response.appointment.time }}
                    
                    Location: {{ booking_response.appointment.venue_id }}
                    
                    Procedure: {{ booking_response.appointment.procedure }}
                    
                    This was triggered because your donation credit increased to {{ trigger.to_state.state }}.
                    
                    Please use the Home Assistant app to complete the booking if desired.
        # Error fallback for failures
        default:
          - service: notify.telegram_bruce
            data:
              title: Blood Donation Booking Failed
              message: >-
                Unable to book a platelet donation appointment:
                
                Error: {{ booking_response.error }}
                
                Message: {{ booking_response.message }}
                
                This was triggered because your donation credit increased to {{ trigger.to_state.state }}.
                
                Please try again later or book manually through the Blood Donor app.

Hi @BruceH5200

The HACS install is working now. Thanks

Hi @BruceH5200

If I call this action:

action: blood_donor.available_appointments
data:
  venue_id: SL85A
  procedure_code: WB
  start_date: "2025-03-01"
  end_date: "2025-03-31"

I get this:

and then, if I call the suggested action, I get this:

I’m not sure how that can be correct.

Thanks for all your efforts on this.

EDIT: Maybe it’s because I have an appointment booked in May

I think I’ll have to wait untill appointments become available 12 weeks after my current booking. I’ll then be able to test more of this.

Thanks again

ok, no worries.
I do platelets so can have appointments every 2 weeks.

I’ve created a venue search service to make it easier to get the venue ids you need.

full disclosure about this development.
the integration has been written pretty much entirely by claude.ai

Yes I have to guide it, and help it to debug itself, but it has done all the heavy lifting.
It’s really amazing what it can achieve.

I applaud your dedication to donation. Well done!

1 Like