Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,32 @@ def _(s):
factory = ServerFactory(TransportFactory())


def get_server():
def get_server(deviceId):
return factory.create(user=LMS_SETTINGS.username,
password=LMS_SETTINGS.password,
cur_player_id=LMS_SETTINGS.default_player,
deviceId=deviceId,
debug=LMS_SETTINGS.debug)


def lambda_handler(event, context, server=None):
""" Route the incoming request based on type (LaunchRequest, IntentRequest,
etc.) The JSON body of the request is provided in the event parameter.
"""
deviceId = ''
echomaps = {}
try:
sqa = SqueezeAlexa(server=server or get_server(),
if event:
deviceId = event['context']['System']['device']['deviceId']
server = server or get_server(deviceId)
echomaps = server.get_echomaps()
except KeyError as e:
if not SKILL_SETTINGS.use_spoken_errors:
raise e
if deviceId in echomaps:
server.cur_player_id = echomaps[deviceId]['name']
try:
sqa = SqueezeAlexa(server=server,
app_id=SKILL_SETTINGS.application_id)
return sqa.handle(event, context)
except Exception as e:
Expand Down
79 changes: 71 additions & 8 deletions metadata/intents/v1/locale/en_GB/intents.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@
"what we are listening to in the {Player}"
]
},
{
"name": "PlayIntent",
"slots": [
{
"name": "Player",
"type": "PLAYER"
}
],
"samples": [
"Play on {Player}"
]
},
{
"name": "SelectPlayerIntent",
"slots": [
Expand Down Expand Up @@ -174,7 +186,10 @@
"turn {Player} player on",
"switch {Player} on",
"switch on {Player}",
"switch {Player} player on"
"switch {Player} player on",
"power {Player} on",
"power on {Player}",
"power {Player} player on"
]
},
{
Expand All @@ -191,7 +206,10 @@
"turn {Player} player off",
"switch {Player} off",
"switch off {Player}",
"switch {Player} player off"
"switch {Player} player off",
"power {Player} off",
"power off {Player}",
"power {Player} player off"
]
},
{
Expand All @@ -215,12 +233,12 @@
}
],
"samples": [
"play some {Genre}",
"play some {Genre} and {SecondaryGenre}",
"play some {Genre} and some {SecondaryGenre}",
"play some {Genre} some {SecondaryGenre} and some {TertiaryGenre}",
"play a mix of {Genre} and {SecondaryGenre}",
"play a mix of {Genre} {SecondaryGenre} and {TertiaryGenre}"
"play some {Genre} on {Player}",
"play some {Genre} and {SecondaryGenre} on {Player}",
"play some {Genre} and some {SecondaryGenre} on {Player}",
"play some {Genre} some {SecondaryGenre} and some {TertiaryGenre} on {Player}",
"play a mix of {Genre} and {SecondaryGenre} on {Player}",
"play a mix of {Genre} {SecondaryGenre} and {TertiaryGenre} on {Player}"
]
},
{
Expand All @@ -241,6 +259,51 @@
"play my {Playlist} playlist in {Player}"
]
},
{
"name": "SetEchoMapIntent",
"slots": [
{
"name": "Player",
"type": "PLAYER"
}
],
"samples": [
"Set the default player to {Player}",
"I'm using {Player}",
"I'm in {Player}",
"Set the player in this room to {Player}",
"I am using {Player}",
"I am in {Player}"
]
},
{
"name": "DelEchoMapPlayerIntent",
"slots": [
{
"name": "Player",
"type": "PLAYER"
}
],
"samples": [
"Delete the default echo device for player {Player}",
"Delete the default echo device for {Player}",
"Delete the default echo for player {Player}",
"Delete the default echo for {Player}",
"Delete the default association for player {Player}",
"Delete the default association for {Player}"
]
},
{
"name": "DelEchoMapDeviceIntent",
"slots": [],
"samples": [
"Delete the default player here",
"Delete the default player for this room",
"Delete the default player for this echo device",
"Delete the default player for this echo",
"Delete the default player"
]
},
{
"name": "AllOnIntent",
"samples": [
Expand Down
3 changes: 3 additions & 0 deletions squeezealexa/alexa/intents.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ class Custom(object):
for s in ["SetVolume", "SetVolumePercent"])
NOW_PLAYING, SELECT_PLAYER = ("%sIntent" % s
for s in ["NowPlaying", "SelectPlayer"])
(SET_ECHOMAP, DEL_ECHOMAP_PLAYER,
DEL_ECHOMAP_DEVICE) = ("%sIntent" % s for s in ["SetEchoMap",
"DelEchoMapPlayer", "DelEchoMapDevice"])
62 changes: 62 additions & 0 deletions squeezealexa/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,68 @@ def on_select_player(self, intent, session, pid=None):
return speech_response(title, speech, reprompt_text=reprompt,
end=False)

@handler.handle(Custom.SET_ECHOMAP)
def on_set_echomap(self, intent, session, pid=None):
srv = self._server
srv.set_echomap(pid, srv.deviceId)
if pid:
player = srv.players[pid]
return self.smart_response(
text=(_("Set the default player for current Echo "
"to {player}").format(player=player.name)),
speech=(_("I have set the default player for "
"current Echo to {player}")
.format(player=player.name)))
speech = (_("I only found these players: {players}. "
"Could you try again?")
.format(players=human_join(srv.player_names)))
reprompt = (_("You can select a player by saying \"{utterance}\" "
"and then the player name.")
.format(utterance=Utterances.SELECT_PLAYER))
try:
player = intent['slots']['Player']['value']
title = (_("No player called \"{name}\"").format(name=player))
except KeyError:
title = "Didn't recognise a player name"
return speech_response(title, speech, reprompt_text=reprompt,
end=False)

@handler.handle(Custom.DEL_ECHOMAP_PLAYER)
def on_del_echomap_player(self, intent, session, pid=None):
srv = self._server
srv.del_echomap(pid)
if pid:
player = srv.players[pid]
text = (_("Removed the default associations for player {player}")
.format(player=player.name))
speech = (_("I have removed the default associations "
"for player {player}")
.format(player=player.name))
return self.smart_response(text, speech)
speech = (_("I only found these players: {players}. "
"Could you try again?")
.format(players=human_join(srv.player_names)))
reprompt = (_("You can select a player by saying \"{utterance}\" "
"and then the player name.")
.format(utterance=Utterances.SELECT_PLAYER))
try:
player = intent['slots']['Player']['value']
title = (_("No player called \"{name}\"").format(name=player))
except KeyError:
title = "Didn't recognise a player name"
return speech_response(title, speech, reprompt_text=reprompt,
end=False)

@handler.handle(Custom.DEL_ECHOMAP_DEVICE)
def on_del_echomap_device(self, intent, session, pid=None):
srv = self._server
srv.del_echomap(False, srv.deviceId)
return self.smart_response(
text=_("Removed the default player for "
"the current Echo device"),
speech=_("I have removed the default player for "
"the current Echo device"))

@handler.handle(Audio.SHUFFLE_ON)
@handler.handle(CustomAudio.SHUFFLE_ON)
def on_shuffle_on(self, intent, session, pid=None):
Expand Down
83 changes: 82 additions & 1 deletion squeezealexa/squeezebox/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,13 @@ class Server(object):
_MAX_FAILURES = 3

def __init__(self, transport, user=None, password=None,
cur_player_id=None, debug=False):
cur_player_id=None, deviceId=None, debug=False):

self.transport = transport
self._debug = debug
self.user = user
self.password = password
self.deviceId = deviceId
if user and password:
self.log_in()
print_d("Authenticated with %s!" % self)
Expand All @@ -121,6 +122,7 @@ def __init__(self, transport, user=None, password=None,
self.__genres = []
self.__playlists = []
self.__favorites = []
self.__echomaps = {}

@property
def connected(self):
Expand Down Expand Up @@ -388,6 +390,85 @@ def __str__(self):
def __del__(self):
self.disconnect()

def get_squeezealexa_favorite_id(self):
resp = self.__a_request("favorites items 0 255",
raw=True)
favorites = {d['name']: d
for d in self._groups(resp, 'id')
if d['hasitems']}
if 'squeeze-alexa' not in favorites:
print_d("Creating squeeze-alexa container in Favorites...")
resp = self.__a_request("favorites addlevel title:squeeze-alexa",
raw=True)
resp = self.__a_request("favorites items 0 255",
raw=True)
favorites = {d['name']: d
for d in self._groups(resp, 'id')
if d['hasitems']}
if 'squeeze-alexa' in favorites:
id = favorites['squeeze-alexa']['id']
print_d("Found squeeze-alexa favorite id={id}", id=id)
return id
else:
return False

def get_echomaps(self):
""" Updates the list of the Squeezebox players available and other
server metadata."""
id = self.get_squeezealexa_favorite_id()
resp = self.__a_request("favorites items 0 255 item_id:%s want_url:1" %
id, raw=True)
self.__echomaps = {d['url']: d for d in self._groups(resp, 'id')
if d['isaudio']}
print_d(with_example("Loaded {num} Echo Maps", self.__echomaps))
return self.__echomaps

def set_echomap(self, player, deviceId):
self.__echomaps = self.get_echomaps()
id = self.get_squeezealexa_favorite_id()
if deviceId not in self.__echomaps:
print_d("Setting new Echo Map from {player} to {deviceId}",
player=player, deviceId=deviceId)
self.__a_request("favorites add item_id:%s.0 title:%s"
"url:%s" % (id, player, deviceId),
raw=True)
elif self.__echomaps[deviceId]['name'] != player:
print_d("Deleting outdated Echo Map with {id}",
id=self.__echomaps[deviceId]['id'])
self.__a_request("favorites delete item_id:%s" %
self.__echomaps[deviceId]['id'],
raw=True)
print_d("Setting new Echo Map from {player} to {deviceId}",
player=player, deviceId=deviceId)
self.__a_request("favorites add item_id:%s.0 title:%s "
"url:%s" % (id, player, deviceId),
raw=True)

def del_echomap(self, player=False, deviceId=False):
self.__echomaps = self.get_echomaps()
if deviceId:
print_d("Trying to remove default player for {deviceId}",
deviceId=deviceId)
while deviceId in self.__echomaps:
print_d("Deleting Echo Map with ID {id}",
id=self.__echomaps[deviceId]['id'])
self.__a_request("favorites delete item_id:%s" %
self.__echomaps[deviceId]['id'],
raw=True)
self.__echomaps = self.get_echomaps()
if player:
inverted_echomaps = {v['name']: {'deviceId': k, 'id': v['id']}
for k, v in self.__echomaps.items()}
while player in inverted_echomaps:
print_d("Deleting Echo Map with ID {id}",
id=inverted_echomaps[player]['id'])
self.__a_request("favorites delete item_id:%s" %
inverted_echomaps[player]['id'],
raw=True)
self.__echomaps = self.get_echomaps()
inverted_echomaps = {v['name']: {'deviceId': k, 'id': v['id']}
for k, v in self.__echomaps.items()}


def people_from(details: Dict, default=None) -> Union[str, None]:
genres = {g.lower() for g in details.get('genre', [])}
Expand Down