2022-12-29, 23:54
Here's how to integrate Alexa with Kodi (and whatever else), in response to https://forum.kodi.tv/showthread.php?tid=309723 and possibly more.
This how-to assumes knowledge of Android, command line interfacing and programmatic procedures. Only general Examples provided.
So there's four key components needed to integrate.
1) Event detection so you know when there has been an Alexa request.
2) Retrieve the request
3) Perform an action for the request.
4) Respond to Alexa that an action has been performed.
There are any number of ways to trigger an event that there has been an Alexa request, for my use case I am using a FireTV cube so the easiest way is to watch logcat entries from com.amazon.vizzini (the alexa process).
You can do this many ways, for this example we're watching logcat from a python subprocess call to adb.
This generates a new trigger each time a new line is added to logcat from the system acting as a pseudo device event trigger.
What we're going to watch for is CSMSIM#directive_received, these are added from the vizzini process when a request has been received.
DeviceEvent: CSMSIM#directive_received#1#NameSpace#Directive#null#EventID
Specifically we care about NameSpace "Alexa.RemoteVideoPlayer" so we can filter by and trigger from that.
Other systems without this trigger you're not out of luck because the actual call is never in logcat so you can do whatever you like to create a trigger.
What now? We have a notification of an Alexa event but we don't know what it was. Well, good old amazon logs Alexa events to our account and we will use this.
You can see the events from your account at https://www.amazon.com/alexa-privacy/apd/rvh and even better it's backed by a JSON request so we can pull events, even better still these are updated near instantly.
JSON Backend: https://www.amazon.com/alexa-privacy/apd...lNav=false
Yes, those are timestamps in the request url and yes you can filter up to the last 60 seconds. We will pull records with a startTime 60 seconds before and after now.
You need to do a url request with whatever you like, add your amazon cookies to the headers.
With some customization you can decide what to do from here, for this we only want the most recent record.
Now alexaRequest will be something like "alexa play a random movie" or "alexa play a random movie from firetv", whatever you asked it to do.
Great now we have successfully detected and received an Alexa request, by now something has already happened on your TV, most likely prime video opened or it's showed search results or whatever.
Before we proceed it would be best to eliminate competing apps so we can handle the request ourselves, there's 2 methods I found each with merit, it's a preference.
Option 1
adb shell am force-stop com.amazon.firebat
adb shell am force-stop com.amazon.tv.launcher
adb shell am force-stop com.amazon.avod
This will kill the apps that are handling or have handled the request, messy, murderous, makes Kodi do weird things after the fact such as video playing without the video window visible.
Option 2
adb shell pm disable-user --user 0 com.amazon.firebat
This disables firebat which likes to handle these requests as a result no other competing apps try to process the request, minimal impact and doesn't break the system. If you want to re-enable it's adb shell pm enable com.amazon.firebat
Great now we can do things with the request, our example "alexa play a random movie" is an easy one you just need to call Kodi with some JSONRPC calls.
Ask Kodi for a movies list, limit 1 record, sorted by random with a playcount of 0 (unwatched).
{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "properties": [ "title", "lastplayed", "studio", "cast", "plot", "writer", "director", "fanart", "runtime", "mpaa", "thumbnail", "file","year", "genre", "tag", "trailer" ], "sort": { "order": "ascending", "method": "random" }, "limits":{"end":1},"filter":{"field":"playcount","operator":"is", "value":"0"} }, "id": 1 }
Get movieid, data["result"]["movies"][0]["movieid"]
Ask Kodi to play it, resume if in progress.
{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"movieid":'+str(movieid)+'},"options":{"resume":true}}}
Now Kodi should be playing the movie, success you have now started a media playback from an Alexa command.
There's the first 3 of the 4 components for an Alexa request, as far as responding I have not found a way but I'm not bothered once Kodi starts playing Alexa informs me the it's "playing (something) from prime video" which it's not, it'll also respond "Here's what I found".
Other notes.
Obviously you need to enable "Control From Other Applications" in Kodi JSONRPC settings.
I recommend not abusing the history url just in case Amazon decides they don't like Skills being sidestepped.
While you're watching the logcat anyway you can find some other useful triggers to do things:
An event of "ActivityManager: Displayed" will have the current active activity name.
You can track active activity and if it's "com.amazon.tv.launcher" you can do "adb shell am start org.xbmc.kodi/.Main" to kick Kodi back on screen (sort of makes Kodi the Home launcher)
If you are unable to:
Work out how to perform url requests with cookies.
Work out how adb works over tcp
Generally script something.
Then this is not the guide for you.
I could never decide the best implementation for this, on one hand it would be easy to put into a kodi service addon but then you couldn't process requests when Kodi isn't running so for my application I'm running it as a background service on a server in the same network.
Hope this helps.
Have a great new year Kodi fans and I look forward to seeing some responses noting either integrations on other systems or just telling me how this is a terrible idea.
This how-to assumes knowledge of Android, command line interfacing and programmatic procedures. Only general Examples provided.
So there's four key components needed to integrate.
1) Event detection so you know when there has been an Alexa request.
2) Retrieve the request
3) Perform an action for the request.
4) Respond to Alexa that an action has been performed.
There are any number of ways to trigger an event that there has been an Alexa request, for my use case I am using a FireTV cube so the easiest way is to watch logcat entries from com.amazon.vizzini (the alexa process).
You can do this many ways, for this example we're watching logcat from a python subprocess call to adb.
python:
p = subprocess.Popen(["adb", "logcat"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
inline = p.stdout.readline().decode("utf-8")
This generates a new trigger each time a new line is added to logcat from the system acting as a pseudo device event trigger.
What we're going to watch for is CSMSIM#directive_received, these are added from the vizzini process when a request has been received.
DeviceEvent: CSMSIM#directive_received#1#NameSpace#Directive#null#EventID
Specifically we care about NameSpace "Alexa.RemoteVideoPlayer" so we can filter by and trigger from that.
Other systems without this trigger you're not out of luck because the actual call is never in logcat so you can do whatever you like to create a trigger.
What now? We have a notification of an Alexa event but we don't know what it was. Well, good old amazon logs Alexa events to our account and we will use this.
You can see the events from your account at https://www.amazon.com/alexa-privacy/apd/rvh and even better it's backed by a JSON request so we can pull events, even better still these are updated near instantly.
JSON Backend: https://www.amazon.com/alexa-privacy/apd...lNav=false
Yes, those are timestamps in the request url and yes you can filter up to the last 60 seconds. We will pull records with a startTime 60 seconds before and after now.
python:
import time
ns = time.time_ns()
startTime = int(str(ns)[:-6]) # Get ms from ns, as good as any math function
remoteurl="https://www.amazon.com/alexa-privacy/apd/rvh/customer-history-records?startTime={}&endTime={}&disableGlobalNav=false".format(startTime-60000,startTime+60000) # Events 1 minute before and after now
You need to do a url request with whatever you like, add your amazon cookies to the headers.
With some customization you can decide what to do from here, for this we only want the most recent record.
python:
events = json.loads(data)
if len(events['customerHistoryRecords'])>0:
alexaRequest = events['customerHistoryRecords'][0]['voiceHistoryRecordItems'][0]['transcriptText']
Now alexaRequest will be something like "alexa play a random movie" or "alexa play a random movie from firetv", whatever you asked it to do.
Great now we have successfully detected and received an Alexa request, by now something has already happened on your TV, most likely prime video opened or it's showed search results or whatever.
Before we proceed it would be best to eliminate competing apps so we can handle the request ourselves, there's 2 methods I found each with merit, it's a preference.
Option 1
adb shell am force-stop com.amazon.firebat
adb shell am force-stop com.amazon.tv.launcher
adb shell am force-stop com.amazon.avod
This will kill the apps that are handling or have handled the request, messy, murderous, makes Kodi do weird things after the fact such as video playing without the video window visible.
Option 2
adb shell pm disable-user --user 0 com.amazon.firebat
This disables firebat which likes to handle these requests as a result no other competing apps try to process the request, minimal impact and doesn't break the system. If you want to re-enable it's adb shell pm enable com.amazon.firebat
Great now we can do things with the request, our example "alexa play a random movie" is an easy one you just need to call Kodi with some JSONRPC calls.
Ask Kodi for a movies list, limit 1 record, sorted by random with a playcount of 0 (unwatched).
{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "properties": [ "title", "lastplayed", "studio", "cast", "plot", "writer", "director", "fanart", "runtime", "mpaa", "thumbnail", "file","year", "genre", "tag", "trailer" ], "sort": { "order": "ascending", "method": "random" }, "limits":{"end":1},"filter":{"field":"playcount","operator":"is", "value":"0"} }, "id": 1 }
Get movieid, data["result"]["movies"][0]["movieid"]
Ask Kodi to play it, resume if in progress.
{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"movieid":'+str(movieid)+'},"options":{"resume":true}}}
Now Kodi should be playing the movie, success you have now started a media playback from an Alexa command.
There's the first 3 of the 4 components for an Alexa request, as far as responding I have not found a way but I'm not bothered once Kodi starts playing Alexa informs me the it's "playing (something) from prime video" which it's not, it'll also respond "Here's what I found".
Other notes.
Obviously you need to enable "Control From Other Applications" in Kodi JSONRPC settings.
I recommend not abusing the history url just in case Amazon decides they don't like Skills being sidestepped.
While you're watching the logcat anyway you can find some other useful triggers to do things:
An event of "ActivityManager: Displayed" will have the current active activity name.
You can track active activity and if it's "com.amazon.tv.launcher" you can do "adb shell am start org.xbmc.kodi/.Main" to kick Kodi back on screen (sort of makes Kodi the Home launcher)
If you are unable to:
Work out how to perform url requests with cookies.
Work out how adb works over tcp
Generally script something.
Then this is not the guide for you.
I could never decide the best implementation for this, on one hand it would be easy to put into a kodi service addon but then you couldn't process requests when Kodi isn't running so for my application I'm running it as a background service on a server in the same network.
Hope this helps.
Have a great new year Kodi fans and I look forward to seeing some responses noting either integrations on other systems or just telling me how this is a terrible idea.