Witam.
Mam zintegrowane z Home Assistant radio internetowe Yoradio GitHub - e2002/yoradio: Web-radio based on ESP32-audioI2S library działa wyśmienicie po MQTT chciałbym dodać jeszcze cztery takie radia ale nie mam na tyle wiedzy jak to zrobić i jak zrobić encje do opcji odtwarzacza celem automatyzacji
dzięki za wszelką pomoc
Projekt wygląda fajnie (już kiedyś go gdzieś widziałem, ale wtedy mi się nie walało tyle niewykorzystanego sprzętu po szufladach, może kiedyś wprowadzę go do realizacji, chociaż nie jest mi tak naprawdę do niczego potrzebny).
A więc…
Czytałem, czytałem i znalazłem instrukcję (poniższe na jej bazie, bo raczej nie zbuduję joradio w najbliższych dniach)
https://github.com/e2002/yoradio?tab=readme-ov-file#home-assistant
Oprócz brokera MQTT są potrzebne
- integracja MQTT w HA (zapewne bez żadnych udziwnień) oraz zapewne plugin MQTT dla samego yoradio GitHub - e2002/yoradio: Web-radio based on ESP32-audioI2S library
- komponent niestandardowy stąd yoradio/HA/custom_components at main · e2002/yoradio · GitHub niestety nie można go dodać przez HACS, więc pozostaje ręczne skopiowanie katalogu
yoradio
wraz z jego zawartością we właściwe miejsce tj./homeassistant/custom_components/
ewentualnie (zależy skąd widzisz mapowanie katalogów/udziałów)/config/custom_components/
- oraz odpowiednia konfiguracja YAML (po instalacji 2. należy zrestartować HA i dopiero wtedy dopisywać cokolwiek do
configuration.yaml
należącego do HA) oczywiście po jej ustawieniu jest konieczny kolejny restart HA (warto sprawdzić poprawność konfiguracji przed tym ruchem) - dodać dowolną kartę media-playera wbudowaną lub niestandardową (moja ulubiona to dostępna w HACS GitHub - kalkih/mini-media-player: Minimalistic media card for Home Assistant Lovelace UI )
Gdyby ktoś szukał listy stacji z karadio wersje z roku 2019 i starsze
https://web.archive.org/web/20190714201852/http://karadio.karawin.fr/WebStations.txt
wersja z 2021
pokaż co masz i jak wygląda encja obecnego media-playera w narzędziach deweloperskich (może wystarczy nieco zmodyfikować co nieco w kolejnych egzemplarzach) kombinowałbym chyba ze zmianą tematu MQTT
z tego co widzę default to yoradio/100/
więc może yoradio/101/
itd.?
masz rękach to możesz ekserymentować
encja wygląda tak:
source_list:
- 1. RMF FM
- 2. RMF MAXXX
- 3. RMF 30 LAT
- 4. RMF 5 Lagodne Przeboje
- 5. RMF 24
- 6. Zlote przeboje nowy sącz
- 7. Radio ZET
- 8. Radio ZET 2000
- 9. Radio ZET 80s
- 10. Radio ZET 90s
- 11. Radio ZET Dance
- 12. Radio ZET Hits
- 13. Radio ZET Party
- 14. Radio ZET Polskie
- 15. ESKA
- 16. ESKA2
- 17. ESKA IMPRESKA
- 18. ESKA Gorąca 20
- 19. ESKA ROCK
- 20. ESKA Hity Na Czasie
- 21. Radio TOK FM
- 22. Radio Alex Zakopane
- 23. Radio Plus Krakow
- 24. Polskie Radio PR 1
- 25. Polskie Radio PR 2
- 26. Polskie Radio PR 3
- 27. Polskie Radio PR 4
- 28. Vox FM
- 29. Vox FM Best Lista
- 30. Radio FEST
- 31. Radio Rekord
- 32. Radio Top80
- 33. 80s80s
- 34. 80s80s Italo Disco
- 35. 80s80s Maxis
- 36. 80s80s in The Mix
- 37. 90s90s In The Mix
- 38. 90s90s Dance
- 39. 90s90s Techno
- 40. 2000er
- 41. 1000 2000er
- 42. Energy2000
- 43. 1Mix Radio-Trance
- 44. Radio 500
- 45. BOBs Death Metal
- 46. Rock Antenne
- 47. Antyradio
- 48. The Rock FM
volume_level: 1
media_title: Masterboy - Show Me Colours
media_artist: 90s90s Dance
media_album_name: ""
source: 38. 90s90s Dance
friendly_name: YoRadio
supported_features: 155573
zmiana z yoradio/100/ na yoradio/101/ nic nie wnosi za to zmiana wpisu przed ukośnikiem już tak.
Ustawiłem na jednym radiu kuchnia/100/ a na drugim salon/100/ i w configuration.yaml według pliku README zrobiłem wpisy:
# yoradio entity
media_player:
- platform: yoradio
name: YoRadio
root_topic: salon/100
i teraz mam kontrolę nad radiem salon/100/ i teraz jak zmienię w configuration.yaml z root_topic: salon/100 na root_topic: kuchnia/100 i po restarcie mam kontrolę nad drugim i tu jest problem bo albo jedno albo drugie próbowałem rozmaitych wpisów ale zawsze są błędne.
Dodam że w custom_components trzeba było wstawić katalog yoradio z trzema plikami:
init.py który jest pusty
manifest.json
{
"domain": "yoradio",
"name": "ёRadio",
"documentation": "https://github.com/e2002/yoradio",
"dependencies": ["http", "mqtt"],
"codeowners": ["@e2002"],
"requirements": [],
"version": "0.9.410"
}
media_player.py
import logging
import voluptuous as vol
import json
import urllib.request
import asyncio
from homeassistant.components import mqtt, media_source
from homeassistant.components.media_player.browse_media import async_process_play_media_url
from homeassistant.const import CONF_NAME
from homeassistant.helpers import config_validation as cv
from homeassistant.components.media_player import (
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
BrowseMedia,
MediaPlayerEntity,
MediaPlayerEntityFeature,
MediaPlayerState,
MediaPlayerEnqueue,
MediaType,
RepeatMode,
)
VERSION = '0.9.410'
_LOGGER = logging.getLogger(__name__)
SUPPORT_YORADIO = (
MediaPlayerEntityFeature.PAUSE
| MediaPlayerEntityFeature.PLAY
| MediaPlayerEntityFeature.STOP
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.VOLUME_STEP
| MediaPlayerEntityFeature.TURN_OFF
| MediaPlayerEntityFeature.TURN_ON
| MediaPlayerEntityFeature.PREVIOUS_TRACK
| MediaPlayerEntityFeature.NEXT_TRACK
| MediaPlayerEntityFeature.SELECT_SOURCE
| MediaPlayerEntityFeature.BROWSE_MEDIA
| MediaPlayerEntityFeature.PLAY_MEDIA
)
DEFAULT_NAME = 'yoRadio'
CONF_MAX_VOLUME = 'max_volume'
CONF_ROOT_TOPIC = 'root_topic'
MEDIA_PLAYER_PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend({
vol.Required(CONF_ROOT_TOPIC, default="yoradio"): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_MAX_VOLUME, default='254'): cv.string
})
def setup_platform(hass, config, add_devices, discovery_info=None):
root_topic = config.get(CONF_ROOT_TOPIC)
name = config.get(CONF_NAME)
max_volume = int(config.get(CONF_MAX_VOLUME, 254))
playlist = []
api = yoradioApi(root_topic, hass, playlist)
add_devices([yoradioDevice(name, max_volume, api)], True)
class yoradioApi():
def __init__(self, root_topic, hass, playlist):
self.hass = hass
self.mqtt = mqtt
self.root_topic = root_topic
self.playlist = playlist
self.playlisturl = ""
async def set_command(self, command):
try:
self.mqtt.async_publish(self.root_topic + '/command', command)
except:
await self.mqtt.async_publish(self.hass, self.root_topic + '/command', command)
async def set_volume(self, volume):
command = "vol " + str(volume)
try:
self.mqtt.async_publish(self.root_topic + '/command', command)
except:
await self.mqtt.async_publish(self.hass, self.root_topic + '/command', command)
def fetch_data(self):
try:
html = urllib.request.urlopen(self.playlisturl).read().decode("utf-8")
return str(html)
except Exception as e:
_LOGGER.error(f"Unable to fetch playlist from {self.playlisturl}: " + str(e))
return ""
async def set_source(self, source):
number = source.split('.')
command = "play " + number[0]
try:
self.mqtt.async_publish(self.root_topic + '/command', command)
except:
await self.mqtt.async_publish(self.hass, self.root_topic + '/command', command)
async def set_browse_media(self, media_content_id):
try:
self.mqtt.async_publish(self.root_topic + '/command', media_content_id)
except:
await self.mqtt.async_publish(self.hass, self.root_topic + '/command', media_content_id)
async def load_playlist(self, msg):
try:
self.playlisturl = msg.payload
file = await self.hass.async_add_executor_job(self.fetch_data)
except uException as e:
_LOGGER.error(f"Error load_playlist from {self.playlisturl}")
else:
file = file.split('\n')
counter = 1
self.playlist.clear()
for line in file:
res = line.split('\t')
if res[0] != "":
station = str(counter) + '. ' + res[0]
self.playlist.append(station)
counter=counter+1
class yoradioDevice(MediaPlayerEntity):
def __init__(self, name, max_volume, api):
self._name = name
self.api = api
self._state = MediaPlayerState.OFF
self._current_source = None
self._media_title = ''
self._track_artist = ''
self._track_album_name = ''
self._volume = 0
self._max_volume = max_volume
async def async_added_to_hass(self):
await asyncio.sleep(5)
await mqtt.async_subscribe(self.api.hass, self.api.root_topic+'/status', self.status_listener, 0, "utf-8")
await mqtt.async_subscribe(self.api.hass, self.api.root_topic+'/playlist', self.playlist_listener, 0, "utf-8")
await mqtt.async_subscribe(self.api.hass, self.api.root_topic+'/volume', self.volume_listener, 0, "utf-8")
async def status_listener(self, msg):
js = json.loads(msg.payload)
self._media_title = js['title']
self._track_artist = js['name']
if js['on']==1:
self._state = MediaPlayerState.PLAYING if js['status']==1 else MediaPlayerState.IDLE
else:
self._state = MediaPlayerState.PLAYING if js['status']==1 else MediaPlayerState.OFF
self._current_source = str(js['station']) + '. ' + js['name']
try:
self.async_schedule_update_ha_state()
except:
pass
async def playlist_listener(self, msg):
await self.api.load_playlist(msg)
try:
self.async_schedule_update_ha_state()
except:
pass
async def volume_listener(self, msg):
self._volume = int(msg.payload) / self._max_volume
try:
self.async_schedule_update_ha_state()
except:
pass
@property
def supported_features(self):
return SUPPORT_YORADIO
@property
def name(self):
return self._name
@property
def media_title(self):
return self._media_title
@property
def media_artist(self):
return self._track_artist
@property
def media_album_name(self):
return self._track_album_name
@property
def state(self):
return self._state
@property
def volume_level(self):
return self._volume
async def async_set_volume_level(self, volume):
await self.api.set_volume(round(volume * self._max_volume,1))
@property
def source(self):
return self._current_source
@property
def source_list(self):
return self.api.playlist
async def async_browse_media(
self, media_content_type: str | None = None, media_content_id: str | None = None
) -> BrowseMedia:
return await media_source.async_browse_media(
self.hass,
media_content_id,
)
async def async_play_media(
self,
media_type: str,
media_id: str,
enqueue: MediaPlayerEnqueue | None = None,
announce: bool | None = None, **kwargs
) -> None:
if media_source.is_media_source_id(media_id):
media_type = MediaType.URL
play_item = await media_source.async_resolve_media(self.hass, media_id, self.entity_id)
media_id = async_process_play_media_url(self.hass, play_item.url)
await self.api.set_browse_media(media_id)
async def async_select_source(self, source):
await self.api.set_source(source)
self._current_source = source
async def async_volume_up(self):
newVol = float(self._volume) + 0.05
await self.async_set_volume_level(newVol)
self._volume = newVol
async def async_volume_down(self):
newVol = float(self._volume) - 0.05
await self.async_set_volume_level(newVol)
self._volume = newVol
async def async_media_next_track(self):
await self.api.set_command("next")
async def async_media_previous_track(self):
await self.api.set_command("prev")
async def async_media_stop(self):
await self.api.set_command("stop")
self._state = MediaPlayerState.IDLE
async def async_media_play(self):
await self.api.set_command("start")
self._state = MediaPlayerState.PLAYING
async def async_media_pause(self):
await self.api.set_command("stop")
self._state = MediaPlayerState.IDLE
async def async_turn_off(self):
await self.api.set_command("turnoff")
self._state = MediaPlayerState.OFF
async def async_turn_on(self, **kwargs):
await self.api.set_command("turnon")
self._state = MediaPlayerState.ON
Zatem jeśli komponent jest napisany poprawnie to taka konfiguracja powinna dodać 2 mediaplayery
media_player:
# yoradio entity 1
- platform: yoradio
name: YoRadio Salon
root_topic: salon/100
# yoradio entity 2
- platform: yoradio
name: YoRadio Kuchnia
root_topic: kuchnia/100
Jeśli w ten sposób nie działa, to zgłoś issue u autora (no chyba że zajrzy tu jakiś spec od tworzenia integracji i to ogarnie, ale zwykle najlepiej zgłosić issue u autora, bo on już zna temat)
SUPER!!
dodało drugą encje mediaplayera jest tak jak chciałem próbowałem w ten sposób ale nie wstawiłem # yoradio entity 2 i występował błąd zdublowanej linii.
Dzięki za pomoc
To co jest za płotkiem (#) to tylko komentarz (te linie z płotkami można wyrzucić, dałem je tylko po to, aby wyróżnić co jest definicją każdego z odtwarzaczy).
Szkoda, że nie wstawiłeś tych złych konfiguracji, to bym pokazał gdzie był błąd.
Myślę, że miałeś zdublowane jakieś wpisy może media_player:
?
(albo inne klucze - nawet nazwy nie mogą się pokrywać, jeśli encje nie mają unikalnych identyfikatorów)
Nie mam tego zainstalowanego więc nie mogłem sprawdzić czy się da dodać im unique_id
więc możesz za-eksperymentować tak (to powinno dodać pewne opcje w GUI HA).
media_player:
- platform: yoradio
name: YoRadio Salon
root_topic: salon/100
unique_id: radyjko_salon1
- platform: yoradio
name: YoRadio Kuchnia
root_topic: kuchnia/100
unique_id: radyjko_kuchnia1
Komponent niestandardowy może nie obsługiwać unikalnych identyfikatorów (choć powinien, ale jak mówiłem nie mam na czym testować), a wtedy “sam sos” stanowi to
media_player:
- platform: yoradio
name: YoRadio Salon
root_topic: salon/100
- platform: yoradio
name: YoRadio Kuchnia
root_topic: kuchnia/100
a tak z komentarzami
media_player: # to jest sekcja wszystkich media-playerów i w całym YAMLu może być tylko jedna
- platform: yoradio # tu definiujesz platformę danego odtwarzacza - to z jakiej integracji on pochodzi
name: YoRadio Salon #nazwy encji muszą być różne jeśli nie mają unikalnych identyfikatorów
root_topic: salon/100 #a to już wynika z twojego eksperymentu gdzie trzeba wprowadzić rozróżnienie na etapie MQTT
- platform: yoradio
name: YoRadio Kuchnia
root_topic: kuchnia/100