DoorBird: add transmit audio functionality

I tried to solve this issue/problem. But unfortunately I couldn’t implement a satified solution because I’m not a python developer too.
But I could solve issue.

First of all I had to use the Advanced SSH & Web Terminal addon to install some missing python modules (I saw these errors when I executed the python script in the terminal…):

  1. Error:
File "/config/custom_scripts/doorbird_transmit_audio.py", line 3, in <module>
    import requests
ModuleNotFoundError: No module named 'requests'

Solution:

python3 -m pip install requests
  1. Error:
[Errno 2] No such file or directory: 'ffmpeg'#

Solution:

apk add ffmpeg

Then the script was executed and the audio was transfered to the doorbird but still with the error:

stdout: “”
stderr: (‘Connection aborted.’, BadStatusLine(‘\r\n’))
returncode: 0

But unfortunately the audio was terrible to listen on the doorbird.

Then I adapted the Python script of @Phuturist to this (like in Doorbird play audio clip through speaker - #7 by Thorbeen with “clipping a little”):

import subprocess
import sys
import requests
from requests.exceptions import RequestException
from time import sleep, time
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.
  """
  next_chunk_time = time()
  while True:
    start_time = time()
    chunk = output_stream.read(chunk_size)
    if not chunk:
      break
    #sleep(1) # We are rate limiting to chunk_size per second
    yield chunk
    elapsed_time = time() - start_time
    next_chunk_time += 0.5
    sleep_time = max(0, next_chunk_time - time())
    #self._log_timing(self.CHUNK_SIZE, elapsed_time, sleep_time)
    sleep(sleep_time)

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()

the important part is the _generate_audio_chunks method with the new sleep time. BUT even then the audio file is not played clear and satified on the doorbird. the “still clipping a little” from the mentioned link is a showstopper for me… :frowning:

Maybe there is someone who can solve this issue finally please? That would be great :pray: