Litexa HTML Web API Support
The Alexa Web API for Games allows you to use existing web technologies and tools to create visually rich and interactive voice-controlled game experiences. You can build for multimodal devices using HTML and web technologies like Canvas 2D, WebAudio, WebGL, JavaScript, and CSS, starting with Echo Show devices and Fire TVs.
For more information, see the Alexa developer portal documentation
There are two broad activities to creating a Web API capable skill: starting a web page and
communicating with it. In this extension, new statements and an HTML
API object
give you tools that help tie these to your Litexa data. Note that the HTML
API
object is available as if it were in your inline JavaScript context, so you can refer
to it anywhere in the inline context, or from expressions in your Litexa code.
Installation
The module can be installed globally, which makes it available to any of your Litexa projects:
npm install -g @litexa/html
If you alternatively prefer installing the extension locally inside your Litexa project, for the sake of tracking the dependency, just run the following inside of your Litexa project directory:
npm install --save @litexa/html
This should result in the following directory structure:
project_dir
├── litexa
└── node_modules
└── @litexa
└── html
HTML
statement
This injects an Alexa.Presentation.HTML.Start
directive into the current response,
which causes a device to load the given URL. This statement will do nothing in the
absence of the HTML interface, so it's safe to let it silently fail.
The presence of this statement will also automatically add the ALEXA_PRESENTATION_HTML
to your skill manifest.
launch
HTML "app.html"
timeout: 200
initialData: @appData
transformers: makeStartTransformers(@appData)
-> idle
You can either use an absolute URL or a relative one, in which case Litexa will append the current assets root to the URL, letting you easily deploy your related website from the Litexa assets folder.
Note, as with the directive, the timeout given is in seconds.
Whatever you pass to initialData, you can expect to arrive in your web page as the data payload to successful initialization of the Alexa API object. See the API documentation for details.
The transformers
key expects an array value that will be added into the appropriate
location of the start directive.
For more information on what these values do, see the directive documentation
HTML.isHTMLPresent()
HTML.isHTMLPresent
will return true at runtime if the skill is currently running on
a device that supports the Alexa Web API. You can use this to write conditional
code that branches on whether the user will see your HTML.
launch
if HTML.isHTMLPresent()
HTML "app.html"
-> startApp
else
say "Ah, I see your device doesn't support HTML. Let's try this instead."
-> alternativeExperience
If at any time you need to shortcircuit HTML, you can call HTML.setEnabled(false)
to make isHTMLPresent()
subsequently report false regardless of the device capabilities.
Note that by default Litexa test runs as if it were running
on an Echo Show-like device, and will behave as if HTML is present.
You can specify an Echo Dot like device instead by calling litexa test --device=dot
You can conditionally disable HTML in your Litexa tests by using the DisableHTML
test statement. This lets you create a single test suite that tests both conditions,
as long as you test with a device that initially supports HTML.
TEST "with HTML"
DisableHTML false
launch
TEST "without HTML"
DisableHTML true
launch
HTML.start(url, timeout, initialData, transformers)
If you'd prefer to manipulate the directive directly, this function will
create an Alexa.Presentation.HTML.Start
directive you can edit further.
You may want to do this if you need to add additional headers, for instance.
IMPORTANT! If you go this route, Litexa cannot automatically add
the ALEXA_PRESENTATION_HTML
to your skill manifest, so you'll need to
add that manually.
You can use the directive as is:
launch
directive HTML.start("hello.html")
-> startApp
Or perhaps pass it through your own adaptor function:
launch
directive myStartDirective()
-> startApp
// utils.js
function myStartDirective(){
let directive = HTML.start("hello.html");
directive.request.headers = {
"Authorization" : "token"
};
return directive;
}
HTML.mark(name)
and <mark name>
The HTML.mark()
function appends a formatted SSML tag to the current
say string.
The <mark>
shorthand tag does the same thing inline in a say string.
When sent to your web page via a transformer, the mark will appear as an ssml
tag type in the speechmarks list, at the appropriate timestamp.
launch
HTML.mark("begin")
say "Hello <mark wave> there player!"
This will produce the SSML: <mark name='begin'/>Hello <mark name='wave'/> there player!
.
When rendered through a transformer, you'll find the first mark at the
start of the speechmark, and the second one following the last
viseme for the word "Hello". Both will take the form { type: "ssml", value: "wave" }
,
substituting each's respective names for the value property, and also
include with the usual start
, end
, and time
speechmark properties.
Events
To handle data that is sent from your web page, add a when
listener for the
Alexa.Presentation.HTML.Message
event in an appropriate state. Note you can
add it to the global
state to handle any incoming message irrespective of
the current skill state.
waitForTransmission
when Alexa.Presentation.HTML.Message
say "I received a message from the web page!"
switch $request.message # payload from web page
...
Tips
Working with both speaker only devices and devices with screens
If you're interested in trying out transformers in your directives,
but want to maintain a skill that can switch between an HTML device
and a speaker only device, try using the usual Litexa say
statements,
then write a post processor
to conditionally move your outputSpeech into a directive instead.
Project structure
There are a lot of ways to build a webapp, so there are a lot of ways you can build one to go along with your Litexa skill. One very convenient single page application structure we've experimented with, is as follows:
- project
|-- webpack.config.js
|-- app // all app development happens here
|-- code
|-- styles
|-- etc
|-- skill // this is your litexa root
|-- litexa
|-- inline-code.js // webpack target
|-- assets
|-- app-code.js // also webpack target!
|-- app.html
|-- etc
This structure relies on webpack to bundle your app, defining two separate entry points, with two different outputs in the same config file:
-
inline-code.js
, exposing your business logic function calls to your Litexa code. -
app-code.js
and others, placing your webapp inside the Litexa assets directory, to get uploaded in sync with your Litexa deployments, which makes it conveniently available to this extension'sHTML
statement.
This lets you develop your webapp in convenient module format, and easily share references between your webapp and skill code. If you're using TypeScript, you could share interfaces and classes between the two, so that you could for instance use the same type to construct a message in your skill, and to write the receiving function in your webapp.
At development time, you can use webpack's built in development server
to preview and iterate on your webapp in a desktop browser, and then
quickly see it on device by running a litexa deploy
command. If you
do use the webpack server, specify writeToDisk
in the configuration
to make sure you have files on disk for Litexa to upload.
Note: should you find your development slowing down as your webApp gets
larger, both in terms of webpack's build time and Litexa's deploy
time, you may want to think about splitting the bulk of your
assets into a different directory, and then uploading them separately.
Chances are most (music, images, meshes, etc) don't need to be
synchronized as often as your code changes.
Webpack supports the convenient contentBase
property in its server
that will let you serve additional assets in your development server,
letting you continue to use relative asset references.