Skip to content

Commit

Permalink
Provide compatibility support for existing configurations using manua…
Browse files Browse the repository at this point in the history
…lly created input entities.
  • Loading branch information
mang1985 committed Apr 12, 2024
1 parent 463a977 commit 476e940
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 29 deletions.
13 changes: 9 additions & 4 deletions custom_components/ytube_music_player/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ async def async_create_form(hass, user_input, page=1):
"select": {
"options": ALL_SHUFFLE_MODES,
"mode": "dropdown"
}
})
}
})
data_schema[vol.Optional(CONF_LIKE_IN_NAME, default=user_input[CONF_LIKE_IN_NAME])] = vol.Coerce(bool) # default like_in_name, TRUE/FALSE
data_schema[vol.Optional(CONF_DEBUG_AS_ERROR, default=user_input[CONF_DEBUG_AS_ERROR])] = vol.Coerce(bool) # debug_as_error, TRUE/FALSE
data_schema[vol.Optional(CONF_LEGACY_RADIO, default=user_input[CONF_LEGACY_RADIO])] = vol.Coerce(bool) # default radio generation typ
Expand All @@ -233,8 +233,13 @@ async def async_create_form(hass, user_input, page=1):
"select": {
"options": ALL_DROPDOWNS,
"multiple": "true"
}
})
}
})
# add for the old inputs.
for _old_conf_input in OLD_INPUTS.values():
if user_input.get(_old_conf_input) is not None:
data_schema[vol.Optional(_old_conf_input, default=user_input[_old_conf_input])] = str

data_schema[vol.Optional(CONF_TRACK_LIMIT, default=user_input[CONF_TRACK_LIMIT])] = vol.Coerce(int)
data_schema[vol.Optional(CONF_MAX_DATARATE, default=user_input[CONF_MAX_DATARATE])] = vol.Coerce(int)
data_schema[vol.Optional(CONF_BRAND_ID, default=user_input[CONF_BRAND_ID])] = str # brand id
Expand Down
43 changes: 40 additions & 3 deletions custom_components/ytube_music_player/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
CONF_PASSWORD,
STATE_PLAYING,
STATE_PAUSED,
STATE_ON,
STATE_OFF,
STATE_IDLE,
ATTR_COMMAND,
Expand All @@ -41,8 +42,16 @@
DOMAIN as DOMAIN_MP,
)

# add for old settings
from homeassistant.components.input_boolean import (
SERVICE_TURN_OFF as IB_OFF,
SERVICE_TURN_ON as IB_ON,
DOMAIN as DOMAIN_IB,
)

import homeassistant.components.select as select
import homeassistant.components.switch as switch
import homeassistant.components.input_select as input_select # add for old settings
import homeassistant.components.input_boolean as input_boolean # add for old settings

# Should be equal to the name of your component.
PLATFORMS = {"sensor", "select", "media_player" }
Expand Down Expand Up @@ -100,8 +109,7 @@
SERVICE_CALL_MOVE_TRACK = "move_track_within_queue"
SERVICE_CALL_APPEND_TRACK = "append_track_to_queue"


CONF_RECEIVERS = 'speakers' # list of speakers (media_players)
CONF_RECEIVERS = 'speakers' # list of speakers (media_players)
CONF_HEADER_PATH = 'header_path'
CONF_API_LANGUAGE = 'api_language'
CONF_SHUFFLE = 'shuffle'
Expand All @@ -124,6 +132,25 @@
CONF_PROXY_URL = 'proxy_url'
CONF_PROXY_PATH = 'proxy_path'

# add for old settings
CONF_SELECT_SOURCE = 'select_source'
CONF_SELECT_PLAYLIST = 'select_playlist'
CONF_SELECT_SPEAKERS = 'select_speakers'
CONF_SELECT_PLAYMODE = 'select_playmode'
CONF_SELECT_PLAYCONTINUOUS = 'select_playcontinuous'
OLD_INPUTS = {
"playlists": CONF_SELECT_PLAYLIST,
"speakers": CONF_SELECT_SPEAKERS,
"playmode": CONF_SELECT_PLAYMODE,
"radiomode": CONF_SELECT_SOURCE,
"repeatmode": CONF_SELECT_PLAYCONTINUOUS
}
DEFAULT_SELECT_PLAYCONTINUOUS = ""
DEFAULT_SELECT_SOURCE = ""
DEFAULT_SELECT_PLAYLIST = ""
DEFAULT_SELECT_PLAYMODE = ""
DEFAULT_SELECT_SPEAKERS = ""

DEFAULT_HEADER_FILENAME = 'header_'
DEFAULT_API_LANGUAGE = 'en'
DEFAULT_LIKE_IN_NAME = False
Expand Down Expand Up @@ -326,6 +353,16 @@ def ensure_config(user_input):
out[CONF_SHUFFLE_MODE] = PLAYMODE_DIRECT
_LOGGER.error(f"shuffle_mode: {_shuffle_mode} is a deprecated value and has been replaced with '{out[CONF_SHUFFLE_MODE]}'.")

# If old input(s) exists,uncheck the new corresponding select(s).
# If the old input is set to a blank space character, then permanently delete this field.
for dropdown in ALL_DROPDOWNS:
if (_old_conf_input := out.get(OLD_INPUTS[dropdown])) is not None:
if _old_conf_input.replace(" ","") == "":
del out[OLD_INPUTS[dropdown]]
else:
if dropdown in out[CONF_INIT_DROPDOWNS]:
out[CONF_INIT_DROPDOWNS].remove(dropdown)
_LOGGER.warning(f"old {dropdown} input_select: {_old_conf_input} exists,uncheck the corresponding new select.")
return out


Expand Down
77 changes: 59 additions & 18 deletions custom_components/ytube_music_player/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,20 @@ def __init__(self, hass, config, name_add):
# All entities are now automatically generated,will be registered in the async_update_selects method later.
# This should be helpful for multiple accounts.
self._selects = dict() # use a dict to store the dropdown entity_id should be more convenient.
self._selects['playlists'] = None
self._selects['playmode'] = None
self._selects['repeatmode'] = None # Previously, it was _select_playContinuous.
self._selects['speakers'] = None # Previously, it was _select_mediaPlayer.
self._selects['radiomode'] = None # Previously, it was _select_source.
# For old settings.
for k,v in OLD_INPUTS.items():
if v == CONF_SELECT_PLAYCONTINUOUS:
_domain = input_boolean.DOMAIN
else:
_domain = input_select.DOMAIN
try:
self._selects[k] = config.data.get(v)
except:
pass
if self._selects[k] is not None and self._selects[k].replace(" ","") != "":
self._selects[k] = _domain + "." + self._selects[k].replace(_domain + ".", "")
self.log_me('error', "Found old {} {}: {},Please consider using the new select entities.".format(_domain, k, self._selects[k] ))

self._like_in_name = config.data.get(CONF_LIKE_IN_NAME, DEFAULT_LIKE_IN_NAME)

self._attr_shuffle = config.data.get(CONF_SHUFFLE, DEFAULT_SHUFFLE)
Expand Down Expand Up @@ -488,9 +497,22 @@ async def async_set_repeat(self, repeat: str):
# Set repeat mode.
self._attr_repeat = repeat
if(self._selects['repeatmode'] is not None):
if self.hass.states.get(self._selects['repeatmode']).state != repeat:
data = {select.ATTR_OPTION: repeat, ATTR_ENTITY_ID: self._selects['repeatmode']}
await self.hass.services.async_call(select.DOMAIN, select.SERVICE_SELECT_OPTION, data)
if repeat == RepeatMode.ALL:
ib_repeat = STATE_ON
else:
ib_repeat = STATE_OFF
if (_state := self.hass.states.get(self._selects['repeatmode']).state) != repeat:
if input_boolean.DOMAIN in self._selects['repeatmode']:
if _state != ib_repeat:
data = {ATTR_ENTITY_ID: self._selects['repeatmode']}
if ib_repeat == STATE_ON:
await self.hass.services.async_call(input_boolean.DOMAIN, IB_ON, data)
else:
await self.hass.services.async_call(input_boolean.DOMAIN, IB_OFF, data)
else:
data = {select.ATTR_OPTION: repeat, ATTR_ENTITY_ID: self._selects['repeatmode']}
await self.hass.services.async_call(select.DOMAIN, select.SERVICE_SELECT_OPTION, data)

self.log_me('debug', f"[E] set_repeat: {repeat}")
self.async_schedule_update_ha_state()

Expand Down Expand Up @@ -1027,7 +1049,7 @@ async def async_update_selects(self, now=None):
self.log_me('debug', "[S] async_update_selects")
# -- Register dropdown(s). -- #
for dropdown in self._init_dropdowns:
if self._selects[dropdown] is None:
if not await self.async_check_entity_exists(self._selects[dropdown], unavailable_is_ok=False):
entity_id = self.hass.data[DOMAIN][self._attr_unique_id][f'select_{dropdown}'].entity_id
if await self.async_check_entity_exists(entity_id, unavailable_is_ok=False):
self._selects[dropdown] = entity_id
Expand Down Expand Up @@ -1072,9 +1094,18 @@ async def async_update_selects(self, now=None):
self._friendly_speakersList.update({a: friendly_name})
friendly_speakersList = list(self._friendly_speakersList.values())
if self._selects['speakers'] is not None:
await self.hass.data[DOMAIN][self._attr_unique_id]['select_speakers'].async_update(friendly_speakersList) # update speaker select
data = {select.ATTR_OPTION: friendly_speakersList[0], ATTR_ENTITY_ID: self._selects['speakers']} # select the first one in the list as the default player
await self.hass.services.async_call(select.DOMAIN, select.SERVICE_SELECT_OPTION, data)
if input_select.DOMAIN in self._selects['speakers']:
_select = input_select
else:
_select = select
data = {_select.ATTR_OPTIONS: friendly_speakersList, ATTR_ENTITY_ID: self._selects['speakers']}
if _select == input_select:
await self.hass.services.async_call(input_select.DOMAIN, input_select.SERVICE_SET_OPTIONS, data)
else:
await self.hass.data[DOMAIN][self._attr_unique_id]['select_speakers'].async_update(friendly_speakersList) # update speaker select

data = {_select.ATTR_OPTION: friendly_speakersList[0], ATTR_ENTITY_ID: self._selects['speakers']} # select the first one in the list as the default player
await self.hass.services.async_call(_select.DOMAIN, _select.SERVICE_SELECT_OPTION, data)

# finally call update playlist to fill the list .. if it exists
await self.async_update_playlists()
Expand Down Expand Up @@ -1143,7 +1174,12 @@ async def async_update_playlists(self, now=None):
# sort with case-ignore
playlists = sorted(list(self._playlist_to_index.keys()), key=str.casefold)
await self.async_update_extra_sensor('playlists', playlists_to_extra) # update extra sensor
await self.hass.data[DOMAIN][self._attr_unique_id]['select_playlists'].async_update() # update playlist select
if self._selects['playlists'] is not None: # update playlist select
if input_select.DOMAIN in self._selects['playlists']:
data = {input_select.ATTR_OPTIONS: list(playlists), ATTR_ENTITY_ID: self._selects["playlists"]}
await self.hass.services.async_call(input_select.DOMAIN, input_select.SERVICE_SET_OPTIONS, data)
else:
await self.hass.data[DOMAIN][self._attr_unique_id]['select_playlists'].async_update()
except:
self.exc()
msg = "Caught error while loading playlist. please log for details"
Expand Down Expand Up @@ -1192,14 +1228,15 @@ async def async_update_playmode(self, entity_id=None, old_state=None, new_state=
self.log_me('debug', "[S] async_update_playmode")
try:
if self._selects['repeatmode'] is not None:
await self.async_set_repeat(self.hass.states.get(self._selects['repeatmode']).state)
if (_state := self.hass.states.get(self._selects['repeatmode']).state) == STATE_ON:
_state = RepeatMode.ALL
await self.async_set_repeat(_state)
except:
self.log_me('debug', "- Selection field " + self._selects['repeatmode'] + " not found, skipping")

try:
if self._selects['playmode'] is not None:
_playmode = self.hass.states.get(self._selects['playmode']).state
if _playmode is not None:
if (_playmode := self.hass.states.get(self._selects['playmode']).state) is not None:
if _playmode in (PLAYMODE_SHUFFLE,PLAYMODE_DIRECT):
shuffle = False
else:
Expand Down Expand Up @@ -1573,8 +1610,12 @@ async def async_play_media(self, media_type, media_id, _player=None, **kwargs):
if _player is not None:
await self.async_update_remote_player(remote_player=_player)
if self._selects['speakers'] is not None:
data = {"option": _player, "entity_id": self._selects['speakers']}
await self.hass.services.async_call(select.DOMAIN, select.SERVICE_SELECT_OPTION, data)
if input_select.DOMAIN in self._selects['speakers']:
_select = input_select
else:
_select = select
data = {_select.ATTR_OPTION: _player, ATTR_ENTITY_ID: self._selects['speakers']}
await self.hass.services.async_call(_select.DOMAIN, _select.SERVICE_SELECT_OPTION, data)

# load Tracks depending on input
try:
Expand Down
14 changes: 12 additions & 2 deletions custom_components/ytube_music_player/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
"legacy_radio": "Create radio as watchlist of random playlist track",
"sort_browser": "Sort results in the media browser",
"extra_sensor": "Create sensor that provide extra information",
"dropdowns": "Create the dropdown(s) you want to use"
"dropdowns": "Create the dropdown(s) you want to use",
"select_speakers": "Entity id of input_select for speaker selection(Deprecated. Leaving a space can permanently delete this field)",
"select_playmode": "Entity id of input_select for playmode selection(Deprecated. Leaving a space can permanently delete this field)",
"select_source": "Entity id of input_select for playlist/radio selection(Deprecated. Leaving a space can permanently delete this field)",
"select_playlist": "Entity id of input_select for playlist selection(Deprecated. Leaving a space can permanently delete this field)",
"select_playcontinuous": "Entity id of input_boolean for play continuous selection(Deprecated. Leaving a space can permanently delete this field)"
}
}
},
Expand Down Expand Up @@ -74,7 +79,12 @@
"legacy_radio": "Create radio as watchlist of random playlist track",
"sort_browser": "Sort results in the media browser",
"extra_sensor": "Create sensor that provide extra information",
"dropdowns": "Create the dropdown(s) you want to use"
"dropdowns": "Create the dropdown(s) you want to use",
"select_speakers": "Entity id of input_select for speaker selection(Deprecated. Leaving a space can permanently delete this field)",
"select_playmode": "Entity id of input_select for playmode selection(Deprecated. Leaving a space can permanently delete this field)",
"select_source": "Entity id of input_select for playlist/radio selection(Deprecated. Leaving a space can permanently delete this field)",
"select_playlist": "Entity id of input_select for playlist selection(Deprecated. Leaving a space can permanently delete this field)",
"select_playcontinuous": "Entity id of input_boolean for play continuous selection(Deprecated. Leaving a space can permanently delete this field)"
}
}
},
Expand Down
14 changes: 12 additions & 2 deletions custom_components/ytube_music_player/translations/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
"legacy_radio": "将随机播放列表曲目创建为收藏夹电台",
"sort_browser": "在媒体浏览器中对结果进行排序",
"extra_sensor": "创建提供额外信息的传感器实体",
"dropdowns": "创建你需要的下拉菜单实体"
"dropdowns": "创建你需要的下拉菜单实体",
"select_speakers": "播放设备下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_playmode":"循环模式下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_source":"播放列表/电台选择下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_playlist":"播放列表下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_playcontinuous":"持续播放模式下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)"
}
}
},
Expand Down Expand Up @@ -74,7 +79,12 @@
"legacy_radio": "将随机播放列表曲目创建为收藏夹电台",
"sort_browser": "在媒体浏览器中对结果进行排序",
"extra_sensor": "创建提供额外信息的传感器实体",
"dropdowns": "创建你需要的下拉菜单实体"
"dropdowns": "创建你需要的下拉菜单实体",
"select_speakers": "播放设备下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_playmode":"循环模式下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_source":"播放列表/电台选择下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_playlist":"播放列表下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)",
"select_playcontinuous":"持续播放模式下拉菜单实体ID(已过期,留下一个空格字符可以永久删除这个字段)"
}
}
},
Expand Down

0 comments on commit 476e940

Please sign in to comment.