Martin_ Posted August 19 Posted August 19 (edited) Hi everyone. Sometimes the best way to learn is to do, so i made this Shelly script that uses 3 virtual components with Sonos information from HA. I get the current song, album art and can control the volume with a slider. The information path is Shelly -> HA -> Sonos. I made a separate script for setting the volume (It can be 1 script, but i liked to have them separate) We could implement more controls to be able to control Sonos from the Shelly app. We could also get HA to control the components (so no polling is needed), but i wanted to do it like this to learn scripting. I would love to be able to add the virtual component group to the dashboard, Shelly is this a thing that could be implemented? App view of final result: You need 3 virtual components (and put them in a group) - 2 with text and label. - 1 with number and slider. Device: Code: The tabbing gets strange when posting the code, sorry about that. Code for getting song, album art and current volume: // CONFIG START let CONFIG = { homeAssistantUrl: 'http://10.0.0.1:8123', // URL of your Home Assistant instance accessToken: 'YOUR HA TOKEN', // Your long-lived access token sonosEntityId: 'media_player.sonos', // Entity ID of your Sonos speaker checkInterval: 10000, // Check every 10 seconds debug: false, // Show debug info debug_error: true, // Show debug error info vd_textID: 200, // VD text id to hold the current song vd_textImageID: 202, // VD text id to hold the Album art volumeSliderID: 200, // VD of the virtual slider to hold the volume album_undefined_icon: 'https://www.shareicon.net/data/128x128/2015/08/12/83951_info_512x512.png' //Change to what you like }; // Debug log function function log(log_info) { if (CONFIG.debug) { print(log_info) }; }; //Function log end // Error log function function log_e(log_error) { if (CONFIG.debug_error) { print("ERROR: " + log_error) }; }; //Function log error end function GetSonosData() { print("Starting to fetch Sonos info from Home Assistant..."); // Debug message Timer.set(CONFIG.checkInterval, true, function () { Shelly.call( "http.request", { method: "GET", url: CONFIG.homeAssistantUrl + '/api/states/' + CONFIG.sonosEntityId, // Home Assistant API endpoint headers: { "Authorization": "Bearer " + CONFIG.accessToken, // Bearer token for authentication "Content-Type": "application/json" }, timeout: 5 // Set a timeout for the request }, function (res, error_code, error_message, ud) { if (error_code === 0) { try { let status = JSON.parse(res.body); // Parse the JSON response // Create the album art URL let albumArtImage = status.attributes.entity_picture; let albumArtUrl = CONFIG.homeAssistantUrl + albumArtImage; // Get current track information let currentTrack = status.attributes.media_title; // Get song let currentArtist = status.attributes.media_artist; // Get artist // Fetch the volume level let volumeLevel = status.attributes.volume_level; // Float between 0 and 1 // Update Album art if (typeof albumArtImage !== "undefined") { // Display the album art Shelly.call("Text.Set", { id: CONFIG.vd_textImageID, // ID of the virtual text component value: albumArtUrl // Text to display (link to image) }); // Print the current album art URL information log("Current album art URL: " + albumArtUrl); } else { // Update the virtual text component to indicate no album art Shelly.call("Text.Set", { id: CONFIG.vd_textImageID, value: CONFIG.album_undefined_icon }); log("No album art currently available, setting default image"); }; // End Update album art IF // Update track info if (currentTrack) { // Create the text let displayText = "Song: " + currentTrack + " by " + currentArtist; // Update the virtual text component Shelly.call("Text.Set", { id: CONFIG.vd_textID, // ID of the virtual text component value: displayText // Text to display }); // Print the current track information log("Current track info: " + displayText); } else { // Update the virtual text component to indicate no track is playing Shelly.call("Text.Set", { id: CONFIG.vd_textID, value: "No track is currently playing." }); log("No track is currently playing."); } // end of currentTrack IF // Update volume level if (volumeLevel) { // Update the virtual slider with the current volume Shelly.call("Number.Set", { id: CONFIG.volumeSliderID, // ID of the virtual slider component value: volumeLevel * 100 // Convert to percentage (0-100) }); // Print the current album art URL information log("Current album art URL:" + albumArtUrl); } else { // Update the virtual slider with no volume Shelly.call("Number.Set", { id: CONFIG.volumeSliderID, // ID of the virtual slider component value: 0 // Convert to percentage (0-100) }); log("No volume value received."); }; // End Update volume level I // Catch potential error } catch (e) { // Check JSON reply for errors log_e("Error parsing JSON: " + e.message); // Log for error debugging log_e("Response body: " + res.body); // Log for error debugging } } else { log_e("", "Error fetching Sonos info: " + error_message + " (Code: " + error_code + ")"); } // End of if error_code 0 }, // End of response func null); // End of Shelly.call http request }, null); // End of Timer.set }; // End of CheckSonosCurrentSong function GetSonosData(); Code for setting the volume: // CONFIG START let CONFIG = { homeAssistantUrl: 'http://10.0.0.1:8123', // URL of your Home Assistant instance accessToken: 'YOUR HA TOKEN', // Your long-lived access token sonosEntityId: 'media_player.sonos', // Entity ID of your Sonos speaker debug: false, // Show debug info debug_error: true, // Show debug error info volumeSliderID: "number:200" // ID of the virtual slider to hold the volume }; // Debug log function function log(log_info) { if (CONFIG.debug) { print(log_info) }; }; //Function log end // Error log function function log_e(log_error) { if (CONFIG.debug_error) { print("ERROR: " + log_error) }; }; //Function log error end // Function to set the volume in Home Assistant function SetSonosVolume(volume) { log("Setting Sonos volume to: " + volume); // Debug message Shelly.call( "http.request", { method: "POST", url: CONFIG.homeAssistantUrl + '/api/services/media_player/volume_set', // Home Assistant API endpoint to set volume headers: { "Authorization": "Bearer " + CONFIG.accessToken, // Bearer token for authentication "Content-Type": "application/json" }, body: JSON.stringify({ entity_id: CONFIG.sonosEntityId, // Entity ID of the Sonos speaker volume_level: volume / 100 // Convert percentage (0-100) to float (0-1) }), timeout: 5 // Set a timeout for the request }, function (res, error_code, error_message, ud) { if (error_code === 0) { log("Volume set successfully."); } else { log_e("Error setting Sonos volume: " + error_message + " (Code: " + error_code + ")"); } }, // End volume response check null ); // End Shelly.call }; // End SetSonosVolume function function onEvent(event_data) { log("Event data: " + JSON.stringify(event_data)); // Shows event data let component = event_data.component; // Get event data name let componet_ID = event_data.delta.id; // Get event data ID let componet_Value = event_data.delta.value; // Get event data Value let info = event_data.info; // Get event data info if (component === CONFIG.volumeSliderID) { // Check for inpout from slider with type and slider ID log("VD information ID: " + componet_ID + " Volume: " + componet_Value) if (event_data.delta.value === 0) { log("Volume is off"); SetSonosVolume(event_data.delta.value); } else if (event_data.delta.value >= 0) { log("Volume is on"); SetSonosVolume(event_data.delta.value) } }; // End IF component if (info) { //Check for false info log_e("Error in received event data") log_e(JSON.stringify(event_data)); return; }; // End IF check for bad data }; // End of onEvent function print("Started Statushandler for Sonos VD Volume Slider"); // Create status hadler Shelly.addStatusHandler(onEvent); Edited August 19 by Martin_ 1 Quote Translate Revert translation? English (American) Finnish French German Italian Portuguese (European) Spanish
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.