diff --git a/HA/custom_components/yoradio/__init__.py b/HA/custom_components/yoradio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/HA/custom_components/yoradio/manifest.json b/HA/custom_components/yoradio/manifest.json new file mode 100644 index 0000000..8530a54 --- /dev/null +++ b/HA/custom_components/yoradio/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "yoradio", + "name": "ёRadio", + "documentation": "https://github.com/e2002/yoradio", + "dependencies": ["http", "mqtt"], + "codeowners": ["@e2002"], + "requirements": [], + "version": "0.4.322" + } diff --git a/HA/custom_components/yoradio/media_player.py b/HA/custom_components/yoradio/media_player.py new file mode 100644 index 0000000..338ee44 --- /dev/null +++ b/HA/custom_components/yoradio/media_player.py @@ -0,0 +1,216 @@ +import aiohttp +import asyncio +import logging +import voluptuous as vol +import json + +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +_LOGGER = logging.getLogger(__name__) + +VERSION = '0.4.322' + +DOMAIN = "yoradio" + +from homeassistant.helpers import config_validation as cv + +from homeassistant.components.media_player import ( + MediaPlayerEntity, + PLATFORM_SCHEMA +) + +from homeassistant.components.media_player.const import ( + MEDIA_TYPE_MUSIC, + SUPPORT_TURN_ON, SUPPORT_TURN_OFF, + SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_SET, + SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_STOP, + SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, + SUPPORT_SELECT_SOURCE +) + +from homeassistant.const import ( + CONF_NAME, + STATE_IDLE, + STATE_PLAYING, + STATE_OFF +) + +SUPPORT_YORADIO = SUPPORT_PAUSE | SUPPORT_PLAY | SUPPORT_STOP |\ + SUPPORT_VOLUME_SET | SUPPORT_VOLUME_STEP | \ + SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \ + SUPPORT_SELECT_SOURCE + +DEFAULT_NAME = 'yoRadio' +CONF_MAX_VOLUME = 'max_volume' +CONF_ROOT_TOPIC = 'root_topic' +YORADIO_SOURCE_TYPE = [] + +PLATFORM_SCHEMA = 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)) + session = async_get_clientsession(hass) + api = yoradioApi(root_topic, session, hass) + add_devices([yoradioDevice(name, max_volume, api)], True) + +class yoradioApi(): + def __init__(self, root_topic, session, hass): + self.session = session + self.hass = hass + self.mqtt = hass.components.mqtt + self.root_topic = root_topic + + async def set_command(self, command): + self.mqtt.async_publish(self.root_topic + '/command', command) + + async def set_volume(self, volume): + command = "vol " + str(volume) + self.mqtt.async_publish(self.root_topic + '/command', command) + + async def set_source(self, source): + number = source.split('.') + command = "play " + number[0] + self.mqtt.async_publish(self.root_topic + '/command', command) + + async def load_playlist(self, msg): + try: + async with aiohttp.ClientSession() as session: + async with session.get(msg.payload) as resp: + file = await resp.text() + except aiohttp.ClientConnectorError as e: + _LOGGER.error('Error downloading ' + msg.payload) + else: + file = file.split('\n') + counter = 1 + for line in file: + res = line.split('\t') + station = str(counter) + '. ' + res[0] + YORADIO_SOURCE_TYPE.append(station) + counter=counter+1 + +class yoradioDevice(MediaPlayerEntity): + def __init__(self, name, max_volume, api): + self._name = name + self.api = api + self._state = STATE_OFF + self._current_source = None + self._media_title = '' + self._track_artist = '' + self._track_album_name = '' + self._volume = 0 + self._muted = False + self._max_volume = max_volume + self.api.mqtt.subscribe(self.api.root_topic+'/status', self.status_listener, 0, "utf-8") + self.api.mqtt.subscribe(self.api.root_topic+'/playlist', self.playlist_listener, 0, "utf-8") + self.api.mqtt.subscribe(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'] + self._state = STATE_PLAYING if js['status']==1 else STATE_IDLE + 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 YORADIO_SOURCE_TYPE + + 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_turn_off(self): + await self.api.set_command("stop") + self._state = STATE_IDLE + + async def async_media_stop(self): + await self.api.set_command("stop") + self._state = STATE_IDLE + + async def async_turn_on(self): + await self.api.set_command("start") + self._state = STATE_PLAYING + + async def async_media_play(self): + await self.api.set_command("start") + self._state = STATE_PLAYING + + async def async_media_pause(self): + await self.api.set_command("stop") + self._state = STATE_IDLE + diff --git a/HA/exsample_configuration.yaml b/HA/exsample_configuration.yaml new file mode 100644 index 0000000..4c7e1e7 --- /dev/null +++ b/HA/exsample_configuration.yaml @@ -0,0 +1,18 @@ + +# Configure a default setup of Home Assistant (frontend, api, etc) +default_config: + +# Text to speech +tts: + - platform: google_translate + +group: !include groups.yaml +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml + +# yoRadio entity +media_player: + - platform: yoradio + name: YoRadio (kitchen) + root_topic: yoradio/100 diff --git a/README.md b/README.md index 1c0e773..b694074 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ - [Quick start](#quick-start) - [Update](#update) - [MQTT](#mqtt) +- [Home Assistant](#home-assistant) - [More features](#more-features) - [Version history](#version-history) --- @@ -180,6 +181,17 @@ download _http://\/data/playlist.csv_ and _http://\/data 2. In the mqttoptions.h file, change the options to the ones you need 3. Well done! +--- +## Home Assistant +
+ +0. Requires [MQTT integration](https://www.home-assistant.io/integrations/mqtt/) +1. Copy directory HA/custom_components/yoradio to .homeassistant/custom_components/ +2. Add yoRadio entity into .homeassistant/configuration.yaml ([see exsample](HA/exsample_configuration.yaml)) +3. Restart Home Assistant +4. Add Lovelace Media Player card to UI (or [mini-media-player](https://github.com/kalkih/mini-media-player) card) +5. Well done! + --- ## More features - Сan add up to 65535 stations to a playlist. Supports and imports [KaRadio](https://github.com/karawin/Ka-Radio32) playlists (WebStations.txt) diff --git a/images/ha.jpg b/images/ha.jpg new file mode 100644 index 0000000..a1125d2 Binary files /dev/null and b/images/ha.jpg differ