Living Apps SDK
Every module is accessible from a single entry-point:
window.laSdk;
laSdk.ready()
Resolves when your custom logic is ready to be run.
Don't use other SDK methods until this promise is resolved!
Optionally, you can set a default laName
that would be used as fallback.
laSdk.ready(opts?: ReadyOpts): Promise<void>
interface ReadyOpts {
/**
* Default name of the living app if query param `?laName` is undefined.
*/
laName?: string;
/**
* Keep the Splash screen
* false by default
*
* if set, `laSdk.removeSplash()` can be called later to remove the Splash screen.
* by default, the Splash screen will be automatically removed after 10s with no calls unless this option is on.
*/
keepSplash?: boolean;
}
Example:
window.laSdk
.ready({ laName })
.then(() => {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
})
.catch((err) => {
// error
});
laSdk.removeSplash()
Remove splash screen if keepSplash
option has been used in ready()
method.
Therefore the Living App can decide when to remove the splash screen. It will
be automatically executed after 10s with no calls.
Example:
window.laSdk
.ready({ laName, keepSplash: true })
.then(() => {
return initAppAndLoadData();
})
.then(() => {
window.laSdk.removeSplash();
});
laSdk.close()
Close your Living App.
laSdk.close(): void
laclose
event
There are several ways to close your Living App. Calling laSdk.close()
is just one possibility.
When user press "HOME" key on the remote control, the SDK emits laclose
event. The app has 10ms to perform a clean shutdown before actually end your aura session and closing the browser. Use this delay to save persistence data, unsubscribe from external systems, etc.
React example:
// ...
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
laSdk.ready().then(() => {
root.render(laSdk.channel === 'movistar-home' ? <MhApp /> : <StbApp />);
window.addEventListener('laclose', () => {
root.unmount();
});
});
// ...
Using root.unmount()
will unmount your entire application and run your unmount logic.
laSdk.KEYS
get KEYS map. Therefore you can set lakeypress
listeners based on these key names
laSdk.KEYS: Record<string, number>
Example:
window.addEventListener('lakeypress', (ev: KeyboardEvent) => {
if (ev.keyCode === window.laSdk.KEYS.KEY_BACK) {
// ...
}
});
available keys
laSdk.KEYS.KEY_0;
laSdk.KEYS.KEY_1;
laSdk.KEYS.KEY_2;
laSdk.KEYS.KEY_3;
laSdk.KEYS.KEY_4;
laSdk.KEYS.KEY_5;
laSdk.KEYS.KEY_6;
laSdk.KEYS.KEY_7;
laSdk.KEYS.KEY_8;
laSdk.KEYS.KEY_9;
laSdk.KEYS.KEY_OK;
laSdk.KEYS.KEY_LEFT;
laSdk.KEYS.KEY_UP;
laSdk.KEYS.KEY_RIGHT;
laSdk.KEYS.KEY_DOWN;
laSdk.KEYS.KEY_BACK;
laSdk.KEYS.KEY_PLAY;
laSdk.KEYS.KEY_PAUSE;
laSdk.KEYS.KEY_PLPAUSE;
laSdk.KEYS.KEY_FORWARD;
laSdk.KEYS.KEY_REWIND;
laSdk.KEYS.KEY_STOP;
laSdk.KEYS.KEY_RECORD;
laSdk.KEYS.KEY_PROG_UP;
laSdk.KEYS.KEY_PROG_DN;
laSdk.KEYS.KEY_RED;
laSdk.KEYS.KEY_GREEN;
laSdk.KEYS.KEY_YELLOW;
laSdk.KEYS.KEY_BLUE;
laSdk.KEYS.KEY_MENU;
laSdk.KEYS.KEY_HELP;
laSdk.KEYS.KEY_EPG;
laSdk.KEYS.KEY_TV;
laSdk.KEYS.KEY_WWW;
laSdk.KEYS.KEY_KEYB;
laSdk.KEYS.KEY_TXT;
laSdk.KEYS.KEY_MIC;
laSdk.http
HTTP client. You must use this HTTP client for your HTTP requests.
laSdk.http: HttpClient
interface HttpClient {
get<K>(
url: string,
opts?: {
params?: Record<string, string | number | boolean>;
headers?: Record<string, string>;
proxy?: boolean;
proxyCacheSeconds?: number;
oauth1?: OAuth1;
useLabContentsApi?: boolean;
blob?: boolean; // enables "blob" xhr responsetType
},
): Promise<{ json: K; status: number; blob?: Blob }>;
post<K>(
url: string,
opts?: {
params?: Record<string, string | number | boolean>;
json: Record<string, string | number | boolean>;
headers?: Record<string, string>;
proxy?: boolean;
oauth1?: OAuth1;
useLabContentsApi?: boolean;
},
): Promise<{ json: K; status: number }>;
}
Example
laSdk.http
.get<{ msg: string }>('https://api.livingappsmaker.com/whatever', {
params: { search: 'foo' },
headers: {
Authorization: 'Bearer whatever',
},
})
.then((res) => console.log(res.json.msg));
.catch((err) => console.log(res.json.status));
Content-Type
POST requests use Content-Type: application/json
by default:
laSdk.http.post('/foo/bar', {
json: { param1: 'value1' },
});
will generate:
POST https://api.example.com/foo/bar
Content-Type: application/json
{
"param1": "value1"
}
However, its possible to use Content-Type: application/x-www-form-urlencoded
instead:
laSdk.http.post<>('/foo/bar', {
json: { param1: 'value1' },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
will generate:
POST https://api.example.com/foo/bar
Content-Type: application/x-www-form-urlencoded
param1=value1
useLabContentsApi
Use Contents LAB API host for given path, SDK will inject the right API KEY.
Example
laSdk.http.get<{ msg: string }>('/foo/bar', { useLabContentsApi: true });
it will GET
<contents_lab_url>/foo/bar
with right header x-api-key: ...
.
abortSignal
Use an AbortSignal object created from the AbortController.signal property.
When a fetch request is initiated, we pass abortSignal
as an option. This associates the signal and controller with the fetch request and allows us to abort it by calling AbortController.abort()
.
As seen in the example below, we can use this option to abort a fetch request after a certain amount of time.
const abortController = new AbortController();
const timeout = setTimeout(() => abortController.abort(), 10000); // 10 sec
laSdk.http
.get<{ msg: string }>('/foo/bar', { signal: abortController.signal })
.finally(() => clearTimeout(timeout));
React example
const User = () => {
const [user, setUser] = useState({ name: '' });
useEffect(() => {
const abortController = new AbortController();
laSdk.http
.get('foo/user', { abortSignal: abortController.signal })
.then((response) => response.json())
.then((data) => setUser(data));
return () => {
abortController.abort();
};
}, []);
return <div>{user.name}</div>;
};
HTTP proxy
Sometimes you'll need to avoid CORS problems or inject some secrets into your HTTP requests.
When using { proxy: true }
option, Living Apps SDK HTTP client will POST Living Apps Platform HTTP Proxy endpoint and we'll perform the HTTP request "backend to backend" and inject your environment secrets when using ###CONFIG_NAME### in your params
, headers
or json
.
proxyCacheSeconds
option will make LA Platform HTTP proxy cache GET responses using all params hash as a cache key
Example:
laSdk.http
.get<{ msg: string }>('https://api.livingappsmaker.com/whatever', {
params: { search: 'foo' },
headers: {
Authorization: 'Bearer ###BEARER_TOKEN###',
},
proxy: true
})
.then((res) => console.log(res.json.msg));
.catch((err) => console.log(res.json.status));
HTTP proxy bearerLogin
The bearerLogin
option allows to authenticate an HTTP request using an access token obtained through a previous request. This is useful when the target API requires authentication using a bearer token.
laSdk.http.get<{ msg: string }>('https://api.livingappsmaker.com/whatever', {
method: 'GET'
proxy: true,
bearerLogin: {
url: 'https://auth.livingappsmaker.com/token',
body: {
client_id: '###CLIENT_ID###', // LA platform will inject this secret
client_secret: '###CLIENT_SECRET###', // LA platform will inject this secret
},
tokenPath: 'data.access_token', // token will be extracted from `response.data.access_token`
cacheSeconds: 60,
},
});
The Living Apps Platform HTTP proxy will POST https://auth.livingappsmaker.com/token
with body
(after replacing secret config entries). If the request is successful, the platform will extract the access token from response.data.access_token
, and cache the token for the number of seconds specified in cacheSeconds.
After the access token is obtained and cached, the platform will add Authorization: Bearer <access token>
header to the target request.
bearerLogin
object will be used to generate a cache key, so all requests with the same bearerLogin
parameter will use the same access token until it expires.
Note that the bearerLogin
option only works with the proxy
option set to true.
laSdk.telefonicaApi
Telefonica APIs (aka 4th Platform) HTTP client. You must use this HTTP client for your all requests to Telefonica Kernel.
Living Apps SDK will fetch the Living Apps Platform Telefonica Kernel API proxy using user Living Apps Access token. This HTTP proxy will authenticate user using Delegated Auth (JWT Bearer) and inject user T. Kernel access token into the final request.
Please contact the team in order to provide your Living App Telefonica Kernel configuration:
- client ID
- client secret
- issuer
- private key
- purposes
When your App does not need to speak in the name of a specific user (note this does not mean it can't consume Personal Information resources), then the authorization can be easier and use client credentials
this is the simplest OAuth grant-type. To use it just need to pass {clientCredentials: true}
to the request options.
laSdk.telefonicaApi: TelefonicaApiClient
interface TelefonicaApiClient {
readonly isLoggedIn: boolean;
login(
levelOfAccess: 2 | 3,
queryparamsOnRestore?: Record<string, string>,
): void;
get<K>(
telefonicaApiPath: string,
opts?: {
params?: Record<string, string | number | boolean>;
clientCredentials?: boolean;
},
): Promise<{ json: K; status: number }>;
post<K>(
telefonicaApiPath: string,
opts?: {
params?: Record<string, string | number | boolean>;
json: Record<string, string | number | boolean>;
clientCredentials?: boolean;
},
): Promise<{ json: K; status: number }>;
}
Example
laSdk.telefonicaApi
.get<{ msg: string }>('/userprofile/v3/users/me', {
params: { search: 'foo' },
})
.then((res) => console.log(res.json.msg))
.catch((err) => console.log(err.json.status));
laSdk.telefonicaApi.login()
LoA 2/3
laSdk.telefonicaApi.login(
levelOfAccess: 2 | 3,
queryparamsOnRestore?: Record<string, string>,
pinIDPTransaction?: PinIDPTransaction
): void;
laSdk.telefonicaApi.login()
allows users to authenticate using a higher level of access
- levelOfAccess 2: PIN IDP / Mobile Connect
- levelOfAccess 3: DNI / PASSWORD
Note that the current Living App session will be closed and a new session will be created after user completes authentication. Thanks to queryParamsOnRestore
your Living App could be initialized with custom query params. Use them to initialize your app in a different way.
Example:
laSdk.telefonicaApi.login(3, { entrypoint: 'restart-router' });
PIN IDP Transaction
The pinIDPTransaction
object activates the PIN Identity Provider for Level of Access (LoA) 2, allowing you to configure the data displayed on the PIN screen.
type PinIDPTransaction = {
title?: string;
subtitle?: string;
description?: string;
image?: string;
legalTermsURL?: string;
transactionType?: 'dataAccess' | 'purchase' | 'subcription' | 'activation';
};
laSdk.telefonicaApi.logout()
Log out from Telefonica Kernel. Note that it does not really close session on Telefonica Kernel. It just make the Living App forget about the authorization id.
laSdk.telefonicaApi.isLoggedIn; // true
laSdk.telefonicaApi.logout();
laSdk.telefonicaApi.isLoggedIn; // false
laSdk.telefonicaApi.isLoggedIn
check if logged with LoA 2/3
Example:
if (laSdk.telefonicaApi.isLoggedIn) {
navigate('/wherever');
}
laSdk.telefonicaApi.storeAuth()
Store current Telefonica Kernel auth for the current Living App so it can be restored in a future session. Error will be thrown if user is not authenticated.
laSdk.telefonicaApi.storeAuth(seconds: number): Promise<void>
Example:
// when LA is init with expected query params, via queryParamsOnRestore, meaning user it's comming from a successful authentication:
if (laSdk.isLoggedIn) {
await laSdk.telefonicaApi.storeAuth(60 * 60); // 1h
}
laSdk.telefonicaApi.restoreAuth()
Restore Telefonica Kernel auth for the current Living App
laSdk.telefonicaApi.restoreAuth(): Promise<void>
Example:
try {
await laSdk.telefonicaApi.restoreAuth();
} catch (err) {
if (!laSdk.isLACoreWebError(err)) {
throw err;
}
switch (err.code) {
case 'telefonicaApi.noAuthToRestore':
case 'telefonicaApi.storedAuthExpired':
laSdk.telefonicaApi.login(2, { entrypoint: 'foo' });
break;
default:
throw err;
}
}
laSdk.getMakerContentsXXX
The following methods fetch content stored in Maker CMS.
These are wrapper methods and could potentially be re-implemented using laSdk.http
while setting the correct specifics.
laSdk.getMakerContents()
getMakerContents<K>(id: string): Promise<{
json: K;
status: number
}>;
laSdk.getMakerContentsHome()
getMakerContentsHome<K>(): Promise<{
json: K;
status: number
}>;
laSdk.getMakerContentsSettings()
type LivingAppMakerContentsStyles = {
custom_font?: string | null;
focus_color?: string | null;
focus_font_color?: string | null;
stb_logo?: string | null;
mh_logo?: string | null;
stb_splash?: string | null;
mh_splash?: string | null;
};
getMakerContentsSettings(): Promise<{
json: LivingAppMakerContentsStyles;
status: number;
}>;
laSdk.getMakerContentsSearch()
getMakerContentsSearch<K>(utterance: string): Promise<{
json: K;
status: number
}>;
laSdk.getMakerContentsConfigEntries()
Retrieve the Configuration Entries that are stored in the Maker CMS.
These entries are set using the Maker CMS and can be used as env vars.
getMakerContentsConfigEntries(laName: string): Promise<{
json: Record<string, string>;
status: number
}>;
Example:
const { json: configEntries } = await getMakerContentsConfigEntries(
'my-living-app',
);
console.log(configEntries);
// {
// API_KEY: 'your-api-key',
// API_URL: 'https://api.example.com',
// ...
// }
laSdk.video
Play a HTML5 or HLS.js video on the background.
Use laSdk.player
instead if need to play a video with the default key-management and/or the default visual UI.
💃 Check video-player for more details.
laSdk.video: VideoBackgroundClient
interface VideoBackgroundClient {
readonly duration: number;
readonly isLoading: boolean;
readonly isPaused: boolean;
readonly isPlaying: boolean;
readonly videoSessionId: string | null;
readonly src: string;
readonly timeLeft: number | null;
currentTime: number;
load(url: string, opts?: VideoBackgroundLoadOpts): Promise<void>;
play(): Promise<void>;
pause(): Promise<void>;
plpause(): Promise<void>;
stop(): Promise<void>;
restart(): Promise<void>;
}
Example:
/*
* loads the video on foreground,
* from the beginning,
* as soon as it's loaded,
* remote (and voice controls) are enabled,
* will not loop on end,
* no loading image,
* 16:9
*/
await laSdk.video.load('url');
VideoBackgroundLoadOpts
(load options)
interface VideoBackgroundLoadOpts {
/**
* Jump to given second as soon as the video is loaded.
*
* The video will load from position 0 otherwise.
*
* `0` by default
*/
initialPosition?: number;
/**
* `16/9` by default
*/
aspectRatio?: number;
/**
* Play the video as soon as it's loaded
*
* `true` by default
*/
autoPlay?: boolean;
/**
* Image to show while the video is loading
*
* `undefined` by default
*/
loadingImage?: string;
/**
* Play the video from the begining on end.
*
* `false` by default
*/
playOnLoop?: boolean;
/**
* Video title shown top-left in the control UI.
* It will be send along with `lavideoevent` events
*
* `undefined` by default
*/
title?: string;
/**
* Extra parameters to be send along with `lavideoevent` events
*
* `undefined` by default
*/
analyticsMetadata?: Record<string, string | number | boolean>;
}
To send extra parameters along with lavideoevent
events, use analyticsMetadata
option:
await laSdk.video.load('url', {
analyticsMetadata: {
foo: 'bar',
},
});
video events
subscribe to lavideoevent
to handle pure video events (one from ):
window.addEventListener('lavideoevent', (ev: LACoreWeb.VideoEvent) => {
switch (ev.detail.state) {
case 'error':
// video error
break;
case 'ended':
// video ended
break;
case 'loadeddata':
// video fully loaded
break;
case 'loadstart':
// video starts loading
break;
case 'pause':
// video paused
break;
case 'play':
// video has begun (not paused)
break;
case 'playing':
// Playback is ready to start after having been paused or delayed due to lack of data.
break;
case 'timeupdate':
// video run 1 sec
break;
}
});
Don't forget window.removeEventListener('lavideoevent', f)
.
laSdk.player
HTML5 + HLS.js Video Player (with default key-management and visual UI).
Use laSdk.video
instead if need to override the default key-management or customize the visual UI.
💃 Check video-player for more details.
laSdk.player: PlayerClient
interface PlayerClient {
readonly currentTime: number;
readonly duration: number;
readonly isLoading: boolean;
readonly isPaused: boolean;
readonly isPlaying: boolean;
readonly src: string;
readonly timeLeft: number | null;
init(): Promise<void>;
load(url: string, opts?: PlayerLoadOpts): Promise<void>;
play(): Promise<void>;
pause(): Promise<void>;
plpause(): Promise<void>;
stop(): Promise<void>;
enableRemoteControl(): void
disableRemoteControl(): void
}
Example
/*
* loads the video on foreground,
* from the beginning,
* as soon as it's loaded,
* remote (and voice controls) are enabled,
* will not loop on end,
* no loading image,
* default controller elements UI
* 16/9
*/
await laSdk.player.load('url');
PlayerLoadOpts
interface PlayerLoadOpts {
/**
* `16/9` by default
*/
aspectRatio?: number;
/**
* Play the video as soon as it's loaded
*
* `true` by default
*/
autoPlay?: boolean;
/**
* Jump to given second as soon as the video is loaded.
* We can also send showModal property to show if we want to continue or reproduce from the beginning.
*
* The video will load from position 0 if nothing specified.
*
* `0` by default
*/
initialPosition?:
| number
| {
position: number;
showModal: boolean;
};
/**
* By default, the player shows controller elements UI such as:
*
* - progress bar,
* - play/pause button
* - restart button
* - feedback msgs.
* - title (if enabled)
* - ...
*
* Unless this option is set:
*
* ```js
* {
* hideControlUI: true
* }
* ```
*
* `false` by default
*/
hideControlUI?: boolean;
/**
* By default, the video will listen to remote control events
*
* `true` by default
*/
enabledRemoteControl?: boolean;
/**
* By default, video tag has z-index: "-1". However, it's possible
* to force it to z-index: "9" by using this property
*
* `false` by default
*/
showVideoOverContent?: boolean;
/**
* Restore the previously focused DOM element when video ends
*
* `false` by default
*/
restoreFocusOnStop?: boolean;
/**
* Image to show while the video is loading
*
* `undefined` by default
*/
loadingImage?: string;
/**
* Play the video from the begining on end.
*
* `false` by default
*/
playOnLoop?: boolean;
/**
* Video title shown top-left in the control UI.
*
* `undefined` by default
*/
title?: string;
/**
* Defines the next element to be focused when needs to navigate outside the
* bounds of the controls ui.
*
* `undefined` by default
*/
navigation?: {
down?: string;
up?: string;
};
/**
* Function that returns how much you want to jump with each press of forward keys
*
* by default apply the same movistar video player logic
*
* `undefined` by default
*/
forwardLogic?(): number;
/**
* Function that returns how much you want to jump with each press of backward keys
*
* by default apply the same movistar video player logic
*
* `undefined` by default
*/
backwardLogic?(): number;
}
player events
subscribe to laplayerevent
to handle events related to the video player controls:
window.addEventListener('laplayerevent', (ev: LACoreWeb.PlayerEvent) => {
switch (ev.detail.state) {
case 'showControls':
// show player controls
break;
case 'hideControls':
// hide player controls
break;
case 'showFeedback':
// show feedback message
break;
case 'hideFeedback':
// hide feedback message
break;
}
});
Don't forget window.removeEventListener
.
laSdk.stb
STB wrapper to run some actions on the STB device.
interface Stb {
playMusic(): Promise<void>;
stopMusic(): Promise<void>;
runDetail(params: Record<string, unknown>): Promise<void>;
checkChannelAvailable(channel: number): Promise<boolean>;
}
laSdk.stb: Stb
Play background music (only available on Movistar STB via channel 0)
laSdk.stb.playMusic();
Stop background music (only available on Movistar STB via channel 0)
laSdk.stb.stopMusic();
Run detail action on STB. Eg: show channel info (only available on Movistar STB). It throws an exception otherwise.
laSdk.stb.runDetail({ productType: 0, productID: '1' });
laSdk.stb.tv
Check if a channel is subscribed and can be shown in the STB. The channel param is the dial of the channel. It throws an exception otherwise. It returns a promise that resolves a boolean indicating the availability of the channel.
laSdk.stb.tv.checkChannelAvailable(1);
Get the current channel in the STB, retrieve the sid, status and name of the channel.
laSdk.stb.tv.getCurrentChannel(): Promise<{ sid: number; status: number; name: string }>;
Play channel in the STB in background. The channel param is the dial of the channel.
laSdk.stb.tv.playChannel(1);
Stop live video in the STB.
laSdk.stb.tv.stopLiveVideo();
Set video window mode. The video window is the rectangle where the video is shown. The window param has the following properties:
- left: left position of the window, 0 is te left side of the screen
- top: top position of the window, 0 is the top of the screen
- width: width of the window
- height: height of the window
If no attribute is set, the default value is used, which is the full screen.
laSdk.stb.tv.setVideoWindow(window);
laSdk.sendKey()
Make your Living App receive a lakeypress
event
laSdk.sendKey(code: number): void
Example:
onClick={() => {
laSdk.sendKey(laSdk.KEYS.KEY_BACK)
}}
laSdk.sendText()
Send voice command programmatically
laSdk.sendText(text: string): Promise<void>;
laSdk.setDialogContext()
Define the events your app will receive when user says something using voice remote control or Movistar Home.
laSdk.setDialogContext(commands: Record<string, string>): void
Example:
laSdk.setDialogContext({
'hello living app': 'hello',
'buy product': 'buy',
});
...
window.addEventListener('lavoiceevent', (ev: CustomEvent) => {
if (ev.detail.event === 'hello') {
// user said "hello living app" (or close sentence)
} else if (ev.detail.event === 'buy') {
// user said "buy" (or close sentence)
}
});
synonyms
Provide multiple synonyms for the same event using |
laSdk.setDialogContext({
'hello living app|hello app|hi there': 'hello',
'buy product|purchase|deal': 'buy',
});
click events
Sometimes you'll just need to map voice events to "clicks" on DOM elements:
laSdk.setDialogContext({
'go home': 'click:home-button',
});
By using click:
prefix, Living Apps Core SDK will automatically click on a DOM element with id home-button
with no need to set any lavoiceevent
listener.
key events
Sometimes you will need to trigger some key events using your voice:
laSdk.setDialogContext({
'go back': 'send_key:KEY_BACK',
});
By using send_key:
prefix, Living Apps Core SDK will automatically try to trigger the key that you specified (see laSdk.KEYS
for reference) with no need to set any lavoiceevent
listener.
laSdk.getDialogContext()
Get current dialogContext
laSdk.getDialogContext(): <Record<string, string>>;
laSdk.addToDialogContext()
Add commands to the current dialog context.
laSdk.addToDialogContext(commands: Record<string, string>): Promise<void>;
laSdk.removeFromDialogContext()
Remove commands from the current dialog context.
laSdk.removeFromDialogContext(commands: Record<string, string>): Promise<void>;
laSdk.clearDialogContext()
Clear last dialogContext, same as setDialogContext({})
laSdk.clearDialogContext(): void;
laSdk.createSuggestions()
UI Component: Draw footer Suggestion Pill buttons.
laSdk.createSuggestions(
suggestions: [{ text: string; onClick: () => {}; up: string; aliases?: string[] }],
voice: boolean = true, // set Dialog Context for these entries
opts?: {
className?: string // added to div.la-suggestions
}
)
Example:
laSdk.createSuggestions([
{ text: 'Restart Router', onClick: routerRestar, up: 'home-element' },
{
text: 'Check Wifi',
onClick: checkWifi,
up: 'home-element',
aliases: ['Check internet', 'Check connection'],
},
]);
By using aliases we can create alternative ways to click a suggestion with our voice.
laSdk.removeSuggestions()
UI Component: Remove latest Suggestion Pills from DOM
laSdk.removeSuggestions(): void;
laSdk.persistenceDrop()
Remove all persistence entries for current user and living app
laSdk.persistenceDrop(): Promise<void>;
laSdk.persistenceWrite()
Store encoded string
on a persistent database. This database is unique per user and Living App.
laSdk.persistenceWrite(key: string, value: string): Promise<void>;
laSdk.persistenceRead()
Read value for your previously stored key-value pair
laSdk.persistenceRead(key: string): Promise<unknown>;
Example:
await laSdk.persistenceWrite('user', JSON.stringify({ name: 'John' }));
const data = await laSdk.persistenceRead('user');
const name = JSON.parse(data);
laSdk.sendMh()
💃 Check Movistar Home
laSdk.sendStb()
💃 Check Movistar Home
laSdk.enableNavigable()
Enable Navigation Framework.
If enabled, use laSdk.focus()
and laSdk.focusedElement
to focus DOM elements programatically.
laSdk.enableNavigable(): void
💃 Check Navigation
laSdk.disableNavigable()
Disable Navigation Framework. Useful if you need to block the UI while loading info.
laSdk.disableNavigable(): void
💃 Check Navigation
laSdk.currentFocus
Get current focused DOM element
laSdk.currentFocus: HtmlElement | null
💃 Check Navigation
laSdk.focus()
UI Component: focus DOM element by node (HTMLElement
) or id
laSdk.focus(elem: HtmlElement | string): void
💃 Check Navigation
laSdk.userData
Get info about current user context: user identity and the device where the app is running on:
laSdk.userData: {
userId: string,
deviceId: string,
}
laSdk.channel
Channel where the LA is running on.
laSdk.channel: 'movistar-home' | 'set-top-box-haac'
laSdk.browserName
Browser where the Living App is running on.
type BrowserName =
| 'base'
| 'mh'
| 'gvp'
| 'desktop-stb'
| 'movistar'
| 'desktop-mh';
laSdk.browser: BrowserName
laSdk.locale
Get Living App environment locale: es-ES
or pt-BR
.
laSdk.locale: 'es-ES' | 'pt-BR'
laSdk.labUrl
Living Apps Backend URl
laSdk.labUrl: string
laSdk.env
Environment where the living app is running. Could be used to apply different local config.
laSdk.env:
| 'es-stg'
| 'es-pre'
| 'es-pro'
| 'br-stg'
| 'br-pre'
| 'br-pro';
laSdk.createToast()
UI Component: Display message with an animation from topside
laSdk.createToast(
toast: {
text: string;
type: 'info' | 'error' | 'success' | 'warning' | 'loading';
duration?: number; // in milliseconds
permanent?: boolean;
code?: string;
}
);
Example:
laSdk.createToast({
text: 'Lorem ipsum dolor sit amet.',
type: 'warning',
code: 'general.101',
duration: 10000,
});
laSdk.removeToast()
UI Component: Remove latest Toast from DOM
laSdk.removeToast();
laSdk.createMagicLink()
UI Component: Open Magic Link experience.
Throws error if user is already inside magic link. Promise must be resolved before calling again.
Note that your Living App must have access to these T. Kernel APIs in order to use Magic Link:
Magic Link notifications are not available for non "telco" Movistar users (~1%)
laSdk.createMagicLink(
config: {
flowId: string;
home?: {
title?: string;
description?: string;
};
appAction?: {
title?: string;
description?: string;
};
noAppAction?: {
title?: string;
description?: string;
androidQrUrl?: string;
iosQrUrl?: string;
webQrUrl?: string;
};
}) : Promise<void>
Example:
await laSdk.createMagicLink({
flowId: 'myFlowId',
home: {
title: 'Lorem ipsum dolor sit amet.',
description: 'Lorem ipsum dolor sit amet.',
},
appAction: {
title: 'Lorem ipsum dolor sit amet.',
description: 'Lorem ipsum dolor sit amet.',
},
noAppAction: {
title: 'Lorem ipsum dolor sit amet.',
description: 'Lorem ipsum dolor sit amet.',
androidQrUrl: 'https://example.com',
iosQrUrl: 'https://example.com',
webQrUrl: 'https://example.com',
},
});
laSdk.createQr()
Create QR dataURL image based on url
Example:
const qrImage = await laSdk.createQr('https://example.com');
<img src={qrImage} alt="example.com QR code" />;
Params:
URL
The url for the qr link.
OPTS
Object with the following keys:
useLab
- boolean (false by default) : Enabling this option allows the inclusion of the final URL within the Lab-URL, which redirects to the specified URL (url parameter) while sending the metricla_url_access
, instead of creating the final URL directly using the url parameter.
Example
const qrImage = await laSdk.createQr('https://example.com', { useLab: true });
// URL --> 'LAB_API_URL/s/LA_NAME?url=https://example.com';
<img src={qrImage} alt="example.com QR code with useLab" />;
laSdk.createLogger()
We export a wrapper to console.log()
that "groups" given name and adds some minor feats:
Example:
const logger = laSdk.createLogger('My Super Screen');
logger
object exposes 4 methods:
logger.debug(...data);
logger.info(...data);
logger.warn(...data);
logger.error(...data);
All four methods will work as their built-in counterparts. With the extra feature of mark a topic [[topic]]
in your message if needed.
logger.debug('just an example');
logger.info('[[sendMessage]] >> %o', msg);
logger.info('[[receiveMessage]] << %o', ev);
logger.warn('[[onRender]] something happened: ', error);
laSdk.trackEvent()
Track event on Living Apps monitoring platform.
Living Apps SDK already tracks multiple events. We strongly believe that your app could be fully monitored with only core events: HTTP requests, focus, clicks, etc. Please think twice before adding your custom events.
Contact Living Apps Platform team in order to access to your application events.
Events already tracked by SDK:
la_webapp_load
: app loadedla_start
: app fully startedla_close
: app closedla_send_command
: command sent to Aurala_receive_command
: command received from aurala_navigation_click
: click on navigable HTML element (include id)la_navigation_focus
: focus navigable HTML element (include id)la_video
: video play/error/end/load/pause events (include video url, title, duration, video session id)Ajax HTTP requests
: all HTTP requests performed by the browser.
All events include the following core fields:
userId
: unique user ID on Living Apps ecosystem.sessionId
: session Id shared on the entire Living App navigation.deviceId
: unique device ID (STB)appUrl
: application URLlivingAppName
: your Living App name (AKA laName)
HTTP request events include the following core fields:
- full url
- HTTTP method
- duration
- req/res headers
- status code
Custom events
Your custom events will be registered as la_custom
with your laCustomEventName
field.
laSdk.trackEvent<K>(name: string, data: Record<string, string> | K): void;
Example:
laSdk.trackEvent('show_product', { product_id: 'x234s1' });
will register at our Monitoring platform a la_custom
event with the key laCustomEventName
set to show_product
; all 5 regular parameters - userId
, sessionId
, deviceId
, appUrl
, livingAppName
- plus product_id
laSdk.trackScreen()
trackEvent
shortcut to track when your Living App screen has been rendered.
laSdk.trackScreen(name: string): void;
same as:
laSdk.trackEvent(name: 'la_screen', {}): void;
React Example:
When a new screen component is mounted.
useEffect(() => {
laSdk.trackScreen('home');
}, []);
laSdk.openLivingAppDeepLink()
Opens another Living App. A.K.A. deep-link feature.
It will close the current Living App and open the target one, allowing developers to add specific query parameters so the target Living App could be initialized in a different way.
When the target Living App is closed, the previous one will be opened again, unless skipRestoreLivingAppOnExit is used. It's also possible to add some query parameters (via queryParamsOnRestore) so the first Living App could restore its state.
openLivingAppDeepLink(
laName: string,
opts?: {
queryParams?: Record<string, string>;
skipRestoreLivingAppOnExit?: boolean;
queryParamsOnRestore?: Record<string, string>;
},
): Promise<void>;
Example:
openLivingAppDeepLink('movistar-gaming', {
queryParams: {
entrypoint: 'latest-games',
},
queryParamsOnRestore: {
entrypoint: 'go-to-movistar-gaming',
},
});
laSdk.closeAndNavigateToExternalUrl()
Close current Living App session and navigate to a different URL outside the app.
closeAndNavigateToExternalUrl(url: string): Promise<void>;
Example:
await closeAndNavigateToExternalUrl(
'https://www.livingappsdevelopers.telefonica.com/',
);
laSdk.notifications
Notifications client. Opens a web-socket through which Telefonica kernel sends real-time notifications to the logged-in customer.
subscribe()
Requires as input a callback function to be invoked when a message is received for the customer. Returns the current state of the WebSocket connection, any value other than 1 (OPEN) should be considered an error.
Example:
function callback<T>(data: T, error?: Error) {...}
const socketReadyState = await laSdk.notifications.subscribe( callback );
Check real time notifications for more details