You will have to add a “Entities Card”
Have a look at https://github.com/wills106/homeassistant-config/tree/master/packages/lovelace-example for an example.
Look at the ChromeCastRadio file.
You will have to add a “Entities Card”
Have a look at https://github.com/wills106/homeassistant-config/tree/master/packages/lovelace-example for an example.
Look at the ChromeCastRadio file.
Hi @ASNNetworks
Can you share the card config, not everything behind, but the build-up of the card and the styling?
Unfortunately not. I use LMS for music now, which renders my previous setup redundant. I used that for Google Cast service calls which I no longer use.
But basically what you see there is:
This all inside a vertical stack, which opens through popup with browser mod.
Thanks for the quick reply!
I’ll se where I get to
Oh yeah, I forgot. To make things more complicated (lol) I put multiple custom media card inside a swiper card and each custom media card is inside a conditional card. So I could swipe between currently active speakers and only see those that are playing/paused.
Yeah, I realized that from the forum posts. I’ll skip that, what I’m looking for is anice popup to handle selcetion of source, playlist and maybe the group management.
Have you seen my question in your Fully loaded tread? Fully loaded media player card (picture-elements project)
I don’t use group managing options, so I can’t help with that sorry. You could try with card mod to get the styling fixed, like how I edited the below bar of mini media player with card mod. You could maybe also create button cards and use those control the group. Each button could represent an input_boolean, which you can tie to a speaker through automations. But you will need to go a little deeper with automations.
Makes sense! Thanks
Works great thanks,
just one question, I would like to display the artwork cover is that possible with this option?
Kr,
André
you need to create a camera that points to a jpg file.
I retrieve artwork, artist and track via a separate python script (scrape using BS4 and send to HA via MQTT)
Then I use a template sensor to figure out which station I’m playing and a picture-glance lovelace card to display the template sensors (artist, track, artwork camera):
Lovelace config extract for that:
- type: custom:stack-in-card
mode: vertical
cards:
- type: picture-glance
camera_image: camera.chromecast_radio_pic
entities:
- entity: input_select.chromecast_radio_station
- entity: input_text.custom_station
- entity: input_select.chromecast_radio_speakers
- entity: switch.chromecast_radio_vol_down
- entity: switch.chromecast_radio_mute
- entity: switch.chromecast_radio_vol_up
- entity: switch.chromecast_radio_stop
- entity: switch.chromecast_radio_play
- type: conditional
conditions:
- entity: binary_sensor.radio_is_streaming
state: "on"
card:
type: entities
show_header_toggle: false
entities:
- entity: sensor.stream_artist
name: Artist
secondary_info: last-changed
icon: mdi:account-music
- entity: sensor.stream_track
name: Track
secondary_info: last-changed
icon: mdi:music-circle
Great howto, I will try it tonight.
Can you please share this script? I’m not familiar with BS4, can you explain?
BS4 is Beautiful Soup. It’s the engine that’s used by the Scrape Sensor
But the scrape sensor only refreshes once every min and I find it a bit more complicated than using BS4 directly. In addition I want a single call per radio, as opposed to 1 call per attribute, which might end up black listing me as spam/DDOS
Here is the python script I’ve written to extract artist, track and album art from the various radios I listen to:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
from urllib.request import urlopen, Request
import paho.mqtt.client as mqtt
import time
from threading import Thread
from datetime import datetime
import json
import secrets
MQTT_Host = secrets.MQTT_Host
MQTT_Port = secrets.MQTT_Port
MQTT_User = secrets.MQTT_User
MQTT_Password = secrets.MQTT_Password
client = mqtt.Client("HA_Scraper") # must be unique on MQTT network
client.username_pw_set(str(MQTT_User),str(MQTT_Password))
client.connect(MQTT_Host, port=MQTT_Port, keepalive=60)
client.loop_start()
headers = {'User-Agent': 'Mozilla/5.0'}
class Radio:
# Initializer / Instance Attributes
def __init__(self, name, url, pic, pic_sel, pic_att, artist, artist_sel, artist_att, track, track_sel, track_att):
self.name = name
self.url = url
self.pic = pic
self.pic_sel = pic_sel
self.pic_att = pic_att
self.artist = artist
self.artist_sel = artist_sel
self.artist_att = artist_att
self.track = track
self.track_sel = track_sel
self.track_att = track_att
class Podcast:
# Initializer / Instance Attributes
def __init__(self, name, url, track, track_sel, track_att, mp3_url, mp3_sel, mp3_att):
self.name = name
self.url = url
self.track = track
self.track_sel = track_sel
self.track_att = track_att
self.mp3_url = mp3_url
self.mp3_sel = mp3_sel
self.mp3_att = mp3_att
RTL2 = Radio("RTL2", "https://www.6play.fr/rtl2/quel-est-ce-titre", "", ".ecfper-2", "src", "", ".ecfper-6", "", "", ".ecfper-5", "") #https://timeline.rtl.fr/RTL2/songs
Absolute_Radio_CR = Radio("Absolute_Radio_CR", "https://planetradio.co.uk/absolute-radio-60s/player/", "", ".station-cards.cf > div:nth-child(3) > a .main-img", "style", "", ".station-cards.cf > div:nth-child(3) > a > .text-wrapper > div > .artist", "", "", ".station-cards.cf > div:nth-child(3) > a > .text-wrapper > div > .track", "")
Absolute_Radio_CR2 = Radio("Absolute_Radio_CR2", # Name
"https://planetradio.co.uk/absolute-classic-rock/player/", # URL
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .image", # Picture Select
"style", # Picture Select Attribute
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .info-wrapper.fr > div > .title.extended-info", # Artist Select
"", # Artist Select Attribute
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .info-wrapper.fr > div > .track.extended-info", # Track Select
"" # Track Select Attribute
)
Absolute_Radio = Radio("Absolute_Radio", "https://planetradio.co.uk/absolute-radio-60s/player/", "", ".station-cards.cf > div:nth-child(1) > a .main-img", "style", "", ".station-cards.cf > div:nth-child(1) > a > .text-wrapper > div > .artist", "", "", ".station-cards.cf > div:nth-child(1) > a > .text-wrapper > div > .track", "")
Absolute_Radio2 = Radio("Absolute_Radio2", # Name
"https://planetradio.co.uk/absolute-radio/player/", # URL
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .image", # Picture Select
"style", # Picture Select Attribute
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .info-wrapper.fr > div > .title.extended-info", # Artist Select
"", # Artist Select Attribute
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .info-wrapper.fr > div > .track.extended-info", # Track Select
"" # Track Select Attribute
)
IoT_Podcast = Podcast("IoT Podcast", "https://iotpodcast.com/feed/", "", "item:nth-of-type(1) title", "", "","enclosure:nth-of-type(1)", "url")
Hass_Podcast = Podcast("Hass Podcast", "https://hasspodcast.io/feed/podcast", "", "item:nth-of-type(1) title", "", "","enclosure:nth-of-type(1)", "url")
Chill = Radio("Chill", "https://www.smoothradio.com/chill/radio/playlist/", "", ".js-lazy", "data-src", "", ".now-playing__text-content__details__artist", "", "", ".now-playing__text-content__details__track", "")
Scala = Radio("Scala", "https://planetradio.co.uk/jazz-fm/player/", "", ".station-cards.cf > div:nth-child(1) > a .main-img", "style", "", ".station-cards.cf > div:nth-child(1) > a > .text-wrapper > div > .artist", "", "", ".station-cards.cf > div:nth-child(1) > a > .text-wrapper > div > .track", "")
Scala2 = Radio("Scala2", # Name
"https://planetradio.co.uk/scala-radio/player/", # URL
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .image", # Picture Select
"style", # Picture Select Attribute
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .info-wrapper.fr > div > .title.extended-info", # Artist Select
"", # Artist Select Attribute
"", #Leave Blank
".now-playing-wrapper > .now-playing.cf > .now-playing-block.cf.right.extended-info > .info-wrapper.fr > div > .track.extended-info", # Track Select
"" # Track Select Attribute
)
def bs_get_value_radio(radio):
req = Request(radio.url, headers=headers)
content = urlopen(req).read()
try:
raw_data = BeautifulSoup(content,"html.parser")
except Exception as e:
print("%s Unable to get BS from URL" % e)
try:
if(radio.pic != raw_data.select(radio.pic_sel)[0][radio.pic_att]):
radio.pic = raw_data.select(radio.pic_sel)[0][radio.pic_att].replace("background-image:url(", "").replace(")", "")
client.publish("RadioStream/"+radio.name+"/pic_url",radio.pic.encode('ascii'), qos=0, retain=True)
except Exception as e:
print ("Can't get "+radio.name+" Image")
try:
if (radio.artist_att):
if(radio.artist != raw_data.select(radio.artist_sel)[0][radio.artist_att]):
radio.artist = raw_data.select(radio.artist_sel)[0][radio.artist_att]
client.publish("RadioStream/"+radio.name+"/artist",radio.artist, qos=0, retain=True)
else:
if(radio.artist != raw_data.select(radio.artist_sel)[0].text.title()):
radio.artist = raw_data.select(radio.artist_sel)[0].text.title().strip()
client.publish("RadioStream/"+radio.name+"/artist",radio.artist, qos=0, retain=True)
except Exception as e:
print ("Can't get "+radio.name+" Artist")
try:
if (radio.track_att):
if(radio.track != raw_data.select(radio.track_sel)[0][radio.track_att]):
radio.track = raw_data.select(radio.track_sel)[0][radio.track_att]
client.publish("RadioStream/"+radio.name+"/track",radio.track, qos=0, retain=True)
else:
if(radio.track != raw_data.select(radio.track_sel)[0].text.title()):
radio.track = raw_data.select(radio.track_sel)[0].text.title().strip()
client.publish("RadioStream/"+radio.name+"/track",radio.track, qos=0, retain=True)
except Exception as e:
print ("Can't get "+radio.name+" Track")
def bs_get_value_podcast(podcast):
req = Request(podcast.url, headers=headers)
content = urlopen(req).read()
try:
raw_data = BeautifulSoup(content,"html.parser")
except Exception as e:
print("%s Unable to get BS from URL" % e)
try:
if (podcast.mp3_att):
if(podcast.mp3_url != raw_data.select(podcast.mp3_sel)[0][podcast.mp3_att]):
podcast.mp3_url = raw_data.select(podcast.mp3_sel)[0][podcast.mp3_att]
client.publish("RadioStream/"+podcast.name+"/mp3_url",podcast.mp3_url, qos=0, retain=True)
else:
if(podcast.mp3_url != raw_data.select(podcast.mp3_sel)[0].text.title()):
podcast.mp3_url = raw_data.select(podcast.mp3_sel)[0].text.title().strip()
client.publish("RadioStream/"+podcast.name+"/mp3_url",podcast.mp3_url, qos=0, retain=True)
except Exception as e:
print ("Can't get "+podcast.name+" MP3 URL")
try:
if (podcast.track_att):
if(podcast.track != raw_data.select(podcast.track_sel)[0][podcast.track_att]):
podcast.track = raw_data.select(podcast.track_sel)[0][podcast.track_att]
client.publish("RadioStream/"+podcast.name+"/track",podcast.track, qos=0, retain=True)
else:
if(podcast.track != raw_data.select(podcast.track_sel)[0].text.title()):
podcast.track = raw_data.select(podcast.track_sel)[0].text.title().strip()
client.publish("RadioStream/"+podcast.name+"/track",podcast.track, qos=0, retain=True)
except Exception as e:
print ("Can't get "+podcast.name+" Track")
def planetradio(name, url):
url = url + str(datetime.now().strftime('%Y-%m-%d')) + "/" + str(datetime.now().strftime('%H:%M')) +"/1"
req = Request(url, headers=headers)
content = str(urlopen(req).read()).replace("b'","").replace("'","")
json_content = json.loads(content)
client.publish("RadioStream/"+name+"/pic_url",json_content[0]["nowPlayingImage"], qos=0, retain=True)
client.publish("RadioStream/"+name+"/artist",json_content[0]["nowPlayingArtist"], qos=0, retain=True)
client.publish("RadioStream/"+name+"/track",json_content[0]["nowPlayingTrack"], qos=0, retain=True)
def streamguys(url):
# url = "https://jeta.streamguys.com:8444/8944a2fe68caa986fdee0f2a3c03675d624bab9a/scraper/9cebb028-9f73-4062-830b-478535e516a1/metadata"
req = Request(url, headers=headers)
content = str(urlopen(req).read()).replace("b'","").replace("'","")
json_content = json.loads(content)
metadata = json_content["StreamTitle"].split(" - ")
try:
client.publish("RadioStream/Radio_Fiji_Two/artist",metadata[-1], qos=0, retain=True)
except:
client.publish("RadioStream/Radio_Fiji_Two/artist","N/A", qos=0, retain=True)
pass
try:
client.publish("RadioStream/Radio_Fiji_Two/track",metadata[-2], qos=0, retain=True)
except:
client.publish("RadioStream/Radio_Fiji_Two/track","N/A", qos=0, retain=True)
pass
Refresh_Timer = 0
while(True):
Thread(target=bs_get_value_radio, args=[RTL2]).start()
Thread(target=planetradio, args=["Absolute_Radio_CR","https://listenapi.bauerradio.com/api9/eventsdadi/absolute-classic-rock/"]).start()
Thread(target=planetradio, args=["Absolute_Radio","https://listenapi.bauerradio.com/api9/eventsdadi/absolute-radio/"]).start()
Thread(target=planetradio, args=["Scala","https://listenapi.bauerradio.com/api9/eventsdadi/scala-radio/"]).start()
Thread(target=streamguys, args=["https://jeta.streamguys.com:8444/8944a2fe68caa986fdee0f2a3c03675d624bab9a/scraper/9cebb028-9f73-4062-830b-478535e516a1/metadata"]).start()
Thread(target=bs_get_value_podcast, args=[IoT_Podcast]).start()
if(Refresh_Timer == 240):
Thread(target=bs_get_value_podcast, args=[Hass_Podcast]).start()
Refresh_Timer = 0
Thread(target=bs_get_value_radio, args=[Chill]).start()
time.sleep(15)
Refresh_Timer = Refresh_Timer + 1
it probably needs a bit of cleaning… I originally scraped content of the html player page, but that would fail every now and again. I then found out that some radios offered a json page with the details I needed (e.g. planet radio stations) so I adjusted.
I then call the script at HA startup and it keeps running…
Hope it makes sense / is helpful to others
Hi @ASNNetworks
So I’m getting there now.
Are you able to see my mistake?
Thanks in advance!
Use stack-in-card instead of vertical-stack. That allows you create vertical and horizontal stacks where the button cards have no gaps and look like one single card. This is what I use for most of my UI to give it a more clean look.
For example:
Make sure you use 0.1.1, 0.2.0 has a bug with nested cards.
Nice!
I’ll give a go
Thanks man!
Guys,
Streaming to Sonos will not work by me. Streaming to googlehome mini or Chromecast is no problem. What should be the problem?
Hello dear people…i have been reading the original post and scrolling till 2021. I am quite confuse to which is the working iteration now on the current HA version.
I got a google home mini and interested in streaming tunein or other radio to the mini. I don’t have a Spotify account. If anyone can point me to the right direction please?
Thanks
If you haven’t already, setup the package folder / file
Place this in the yaml file:
input_select:
radio_station:
name: 'Select Radio Station:'
options:
- Hit 92-9
- Nova 93-7
- Mix 94-5
- 96FM
- 80's 1
- 80's 2
- OldSkool Hits
- Old Skool Anthems
- Raw FM (dance)
- 181FM Power (Todays Hits)
- 181FM 90's Dance
- 181FM Star 90's
- 181FM The Breeze
- Heat Radio (RnB)
- Fresh 92-7
- DI Chill & Tropical House
- DI Disco House
- DI Funky House
- DI Liquid D&B
- Aloha Joe's Relaxation Island
- Spectrum Fit
- Energy FM Australia
- Jazz Relax
- Australian Country
- Rebel FM
initial: Raw FM (dance)
icon: mdi:radio
chromecast_radio:
name: 'Select Speakers:'
options:
- Lounge
- Bedroom
- Office
- Lounge and Office
- House
- House except office
- Everywhere
- Everywhere except office
- Back yard
initial: House
icon: mdi:speaker-wireless
input_number:
volume_radio:
name: Volume
icon: mdi:volume-high
min: 0
max: 1
step: 0.05
automation:
- alias: 'Listen Radio'
trigger:
- platform: state
entity_id: input_select.radio_station
action:
- service: script.radio
- alias: 'Set Chromecast Radio Volume'
trigger:
platform: state
entity_id: input_number.volume_radio
action:
service: media_player.volume_set
data_template:
entity_id: >
{% if is_state("input_select.chromecast_radio", "Lounge") %} media_player.lounge_speakers
{% elif is_state("input_select.chromecast_radio", "Bedroom") %} media_player.bedroom
{% elif is_state("input_select.chromecast_radio", "Office") %} media_player.chromecastaudio3249
{% elif is_state("input_select.chromecast_radio", "Lounge and Office") %} media_player.lounge_and_office
{% elif is_state("input_select.chromecast_radio", "House") %} media_player.house
{% elif is_state("input_select.chromecast_radio", "House except office") %} media_player.house_except_office
{% elif is_state("input_select.chromecast_radio", "Everywhere") %} media_player.everywhere
{% elif is_state("input_select.chromecast_radio", "Everywhere except office") %} media_player.all_except_office
{% elif is_state("input_select.chromecast_radio", "Back yard") %} media_player.back_yard
{% endif %}
volume_level: '{{ states.input_number.volume_radio.state }}'
script:
radio:
alias: Play Radio on Chromecast Audio
sequence:
- service: media_player.volume_set
data_template:
entity_id: >
{% if is_state("input_select.chromecast_radio", "Lounge") %} media_player.lounge_speakers
{% elif is_state("input_select.chromecast_radio", "Bedroom") %} media_player.bedroom
{% elif is_state("input_select.chromecast_radio", "Office") %} media_player.chromecastaudio3249
{% elif is_state("input_select.chromecast_radio", "Lounge and Office") %} media_player.lounge_and_office
{% elif is_state("input_select.chromecast_radio", "House") %} media_player.house
{% elif is_state("input_select.chromecast_radio", "House except office") %} media_player.house_except_office
{% elif is_state("input_select.chromecast_radio", "Everywhere") %} media_player.everywhere
{% elif is_state("input_select.chromecast_radio", "Everywhere except office") %} media_player.all_except_office
{% elif is_state("input_select.chromecast_radio", "Back yard") %} media_player.back_yard
{% endif %}
volume_level: '{{ states.input_number.volume_radio.state }}'
- service: media_player.play_media
data_template:
entity_id: >
{% if is_state("input_select.chromecast_radio", "Lounge") %} media_player.lounge_speakers
{% elif is_state("input_select.chromecast_radio", "Bedroom") %} media_player.bedroom
{% elif is_state("input_select.chromecast_radio", "Office") %} media_player.chromecastaudio3249
{% elif is_state("input_select.chromecast_radio", "Lounge and Office") %} media_player.lounge_and_office
{% elif is_state("input_select.chromecast_radio", "House") %} media_player.house
{% elif is_state("input_select.chromecast_radio", "House except office") %} media_player.house_except_office
{% elif is_state("input_select.chromecast_radio", "Everywhere") %} media_player.everywhere
{% elif is_state("input_select.chromecast_radio", "Everywhere except office") %} media_player.all_except_office
{% elif is_state("input_select.chromecast_radio", "Back yard") %} media_player.back_yard
{% endif %}
media_content_id: >
{% if is_state("input_select.radio_station", "Hit 92-9") %} http://ic6ti.scahw.com.au/6ppm_128
{% elif is_state("input_select.radio_station", "Nova 93-7") %} http://streaming.novaentertainment.com.au/nova937
{% elif is_state("input_select.radio_station", "Mix 94-5") %} http://sc01.scahw.com.au/6mix_32
{% elif is_state("input_select.radio_station", "96FM") %} https://icy.ihrcast.arn.com.au/au_012_icy
{% elif is_state("input_select.radio_station", "80's 1") %} http://ic2ti.scahw.com.au/2easy_128
{% elif is_state("input_select.radio_station", "80's 2") %} http://18243.live.streamtheworld.com/T_RAD_80S_S01_SC?
{% elif is_state("input_select.radio_station", "OldSkool Hits") %} http://sc01.scahw.com.au/loveland_32
{% elif is_state("input_select.radio_station", "Old Skool Anthems") %} http://nebula.shoutca.st:8075/stream
{% elif is_state("input_select.radio_station", "Raw FM (dance)") %} https://frontend.stream.rawfm.net.au/i/syd-stream-192k.mp3
{% elif is_state("input_select.radio_station", "181FM Power (Todays Hits)") %} http://listen.181fm.com/181-power_128k.mp3?
{% elif is_state("input_select.radio_station", "181FM 90's Dance") %} http://listen.181fm.com/181-90sdance_128k.mp3
{% elif is_state("input_select.radio_station", "181FM Star 90's") %} http://listen.181fm.com/181-star90s_128k.mp3
{% elif is_state("input_select.radio_station", "181FM The Breeze") %} http://listen.181fm.com/181-breeze_128k.mp3
{% elif is_state("input_select.radio_station", "Heat Radio (RnB)") %} http://174.37.159.206:8106/stream
{% elif is_state("input_select.radio_station", "Fresh 92-7") %} http://live.fresh927.com.au:80/freshaac
{% elif is_state("input_select.radio_station", "DI Chill & Tropical House") %} http://pub1.diforfree.org:8000/di_chillntropicalhouse_hi
{% elif is_state("input_select.radio_station", "DI Disco House") %} http://pub1.diforfree.org:8000/di_discohouse_hi
{% elif is_state("input_select.radio_station", "DI Funky House") %} http://pub1.diforfree.org:8000/di_funkyhouse_hi
{% elif is_state("input_select.radio_station", "DI Liquid D&B") %} http://pub1.diforfree.org:8000/di_liquiddnb_hi
{% elif is_state("input_select.radio_station", "Aloha Joe's Relaxation Island") %} http://s2.voscast.com:7932/
{% elif is_state("input_select.radio_station", "Spectrum Fit") %} http://51.255.235.165:5292/
{% elif is_state("input_select.radio_station", "Energy FM Australia") %} http://s3.viastreaming.net:8502/
{% elif is_state("input_select.radio_station", "Jazz Relax") %} http://199.195.194.94:8036/stream
{% elif is_state("input_select.radio_station", "Australian Country") %} https://streaming.radio.co/s5ea3fdd1c/listen
{% elif is_state("input_select.radio_station", "Rebel FM") %} https://au1.fastcast4u.com/proxy/rblgc?mp=/stream
{% endif %}
media_content_type: 'audio/mp4'
Adjust the entity id’s for your media players accordingly, change the radio station URL’s to whatever you want etc.
In Lovelace add a card:
entities:
- input_select.radio_station
- input_select.chromecast_radio
- script.radio
- input_number.volume_radio
header:
image: /local/images/internet_radio.jpg
type: picture
show_header_toggle: false
type: entities
Save this file under: www\images\internet_radio.jpg
…and you should end up with this:
thanks a lot. It works