Hi,
I’ve been bashing my head against a wall for some days now trying to figure out how one could make a Homey Script that can read out my ‘good morning message’ via ElevenLabs API (send input to elevenlabs then get an url which my Sonos should play). Thus far I’ve been able to create a script that sends a string to ElevenLabs API, and then recieves the audio, but in order to make it work on Sonos i need a URL, thus, i’ve used file.io to temporarily upload the audio via their API. Here is where I’m essentially stuck, because when the file gets uploaded to file.io, it is indeed an .mp3 file, but it does not play and seems to be corrupt. Also, there was a big hassle to figure out how to decode the data i got from the elevenlabs API, blob does not work in homeyscript.
A big caveat, I’ve attempted this together with ChatGPT, thus, my coding experience might not be on par with my mission. Any further guidance would be highly appreciated.
const apiKey = '';
const text = 'Hello there!';
const voiceId = '';
const fileIoApiKey = '';
/// Funktion för att skicka POST-begäran till ElevenLabs TTS API och hämta ljuddata
async function getAudioData(text, voiceId) {
console.log("Sending request to ElevenLabs API...");
const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {
method: 'POST',
headers: {
'xi-api-key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
text: text,
voice_settings: {
stability: 0.5,
similarity_boost: 0.5
}
})
});
if (!response.ok) {
const errorData = await response.text();
console.error(`HTTP error! Status: ${response.status}, Details: ${errorData}`);
throw new Error(`HTTP error! Status: ${response.status}, Details: ${errorData}`);
}
const arrayBuffer = await response.arrayBuffer();
console.log("Received audio data from ElevenLabs API...");
console.log(`Audio data length: ${arrayBuffer.byteLength}`);
return arrayBuffer;
}
// Funktion för att konvertera ArrayBuffer till en Base64-sträng utan `btoa`
function arrayBufferToBase64(buffer) {
const uint8Array = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < uint8Array.byteLength; i++) {
binary += String.fromCharCode(uint8Array[i]);
}
return Buffer.from(binary, 'binary').toString('base64');
}
// Funktion för att ladda upp rå binärdata till file.io och få en URL
async function uploadAudioData(audioBuffer) {
console.log("Uploading audio data to file.io...");
const boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW';
let body = `--${boundary}\r\n`;
body += `Content-Disposition: form-data; name="file"; filename="audio.mp3"\r\n`;
body += 'Content-Type: audio/mpeg\r\n\r\n';
body += arrayBufferToBase64(audioBuffer);
body += `\r\n--${boundary}--\r\n`;
const response = await fetch('https://file.io', {
method: 'POST',
headers: {
'Authorization': `Bearer ${fileIoApiKey}`,
'Content-Type': `multipart/form-data; boundary=${boundary}`
},
body: body
});
if (!response.ok) {
const errorData = await response.text();
console.error(`File upload error! Status: ${response.status}, Details: ${errorData}`);
throw new Error(`File upload error! Status: ${response.status}, Details: ${errorData}`);
}
const data = await response.json();
console.log("Uploaded audio data to file.io. URL:", data.link);
return data.link; // file.io returns the URL in the 'link' property
}
// Huvudfunktion för att hantera processen
async function main() {
try {
const audioBuffer = await getAudioData(text, voiceId);
// Kontrollera att vi har fått korrekt ljuddata
if (!audioBuffer || audioBuffer.byteLength === 0) {
console.error('Received empty audio data');
throw new Error('Received empty audio data');
}
const audioUrl = await uploadAudioData(audioBuffer);
console.log(`Generated Audio URL: ${audioUrl}`);
return audioUrl;
} catch (error) {
console.error(error);
return `An error occurred: ${error.message}`;
}
}
// Kör huvudfunktionen och returnera resultatet
return await main();