DoorBird: add transmit audio functionality

The DoorBird LAN-API allows for transmitting audio to it’s intercom through the /bha-api/audio-transmit.cgi endpoint (see page 18). This can be useful for sending short TTS messages and for instance letting people know you are not at home.

There is a separate AppDaemon app which seems to add this functionality. I havent tested it as I dont use AppDaemon (yet). I’d like to see this feature integrated in the default DoorBird integration.

I already tested the endpoint and functionality myself using curl and also with a Nodejs script. This works like a charm. Unfortunately my Python skills are to limited to come up with a pull request.

@oblogic7 or @bdraco , you seem to be listed as code owners for the DoorBird integration. Does this feature request sound viable?

For now I have created a work around which doesnt require AddDaemon which I’ll share here for anyone interested in adding this functionality.

All credits to @coobnoob for the script used. I only edited his/her AppDaemon script so it works as a standalone shell script. I’m in no way a Python coder so there are probably lots of improvements possible but the script works.

  1. Add the script below somewhere in your Home Assistant folder. I added it to a folder called custom_scripts.
  2. Add the following configuration to configuration.yaml and make sure you reference the name and location of the script correctly.
shell_command:
  doorbird_transmit_audio: python3 /config/custom_scripts/doorbird_transmit_audio.py {{ address }} {{ username }} {{ password }} {{ audio_file_path }}
  1. Create an automation with an action that looks similar to this, obviously using the correct IP address and credentials of your DoorBird.
action:
  - service: shell_command.doorbird_transmit_audio
    metadata: {}
    data:
      address: 192.168.0.134
      username: gxxxxxxxx2
      password: hxxxxxxxxk
      audio_file_path: /config/www/sounds/doorbird/not_at_home.mp3

The script

import subprocess
import sys
import requests
from requests.exceptions import RequestException
from time import sleep 
from io import BytesIO

# Credits for this script go to Kristian (coobnoob). The base of this script is a copy from https://github.com/coobnoob/ha-appdaemon-doorbird-audio/tree/main

# usage examples
# python3 /config/custom_scripts/doorbird_transmit_audio.py 192.168.0.134 username password /config/www/sounds/doorbird/not_at_home.mp3

# parameters
base_url = "http://%s/bha-api" % (sys.argv[1])
auth = (sys.argv[2], sys.argv[3])

def _generate_audio_chunks(output_stream, chunk_size=8*1024):
  """
  Generator function to yield chunks of audio data from a file.
  Doorbird is rate limited to 8K per second

  Args:
      output_stream: Stream of audio
      chunk_size (int): Size of each chunk in bytes.

  Yields:
      bytes: Chunks of audio data.
  """
  while True:
    chunk = output_stream.read(chunk_size)
    if not chunk:
      break
    sleep(1) # We are rate limiting to chunk_size per second
    yield chunk

def send_audio(audio_file_path):
  """
  Send audio to Doorbird

  Args:
      audio_file_path (str): Path to the audio file to be transmitted.
      
  Raises:
      Exception
  """
  try:
    response = requests.get(f"{base_url}/getsession.cgi", auth=auth)
    response.raise_for_status()
       
    # Check if the request was successful (HTTP status code 200)
    if response.status_code == 200:
      data = response.json()
      session_id = data["BHA"]["SESSIONID"]
    else:
      raise Exception(f"Failed to obtain session ID. Status code: {response.status_code}")

    # Prepare headers
    headers = {
      "Content-Type": "audio/basic",
      "Connection": "Keep-Alive",
      "Cache-Control": "no-cache"
    }

    # Command to convert the file to an a mono, 8000Khz mu-law wav
    command = [
      "ffmpeg",
      "-y",
      "-i", audio_file_path,
      "-ac", "1",
      "-ar", "8000",
      "-f", "wav",
      "-codec:a", "pcm_mulaw",
      "-"
    ]

    # Convert the file to an in-memory, mono, 8000Khz mu-law wav
    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    result.check_returncode()
    
    # Make the POST request with the audio data generator to chunk the file
    with requests.post(
      f"{base_url}/audio-transmit.cgi?sessionid={session_id}",
      headers=headers,
      data=_generate_audio_chunks(BytesIO(result.stdout), chunk_size=8*1024),
      auth=auth,
      stream=True
    ) as response:
      if response.status_code != 200:
        sys.stderr.write("Failed to transmit audio. \n")
        response.raise_for_status()
        sys.exit()
  except requests.exceptions.RequestException as e:
    sys.stderr.write(str(e))
    sys.exit()

# Make the request
try:
  send_audio(sys.argv[4])
except Exception as e:
  sys.stderr.write(str(e))
  sys.exit()
2 Likes

Thanks for sharing! I will try to implement this next month. The intended use case is to send an audio message, if someone locks the door using the Doorbird keypad and a door or window is still left open.

I hadnt even thought about that use case but that’s a great one as well. Looking forward to the proper implementation.

Thanks

I was using gstreamer for this case.
But every Home Assistant update, I have to install gstreamer again