JavaScript API

From MiCasaVerde
Jump to: navigation, search

Contents

Usage

"Tabs": [
       {
           "Label": {
               "lang_tag": "users",
               "text": "Users" 
           },
           "Position": "0",
           "TabType": "javascript",
           "ScriptName": "I_HomeCare.js",
           "Function": "showUsers"
       }, 

To generate a tab using your JavaScript functions you need to specify this by setting TabType to javascript. Then, you need to specify the script file's name and which function in this file will be used to draw the tab's content. That function will have one argument, which is the device number.

<functionName> (deviceNumber)

This is the tab rendering function. The set_panel_html function must be called at the end.

Input:

  • deviceNumber (number)

Output:

  • nothing


Variables

jsonp.ud

This is the user data.

  • Type: object
  • Members:
    • devices (array)
    • scenes (array)
    • users (array)
    • rooms (array)
    • eventList (array)
    • etc.


jsonp.ud.devices

  • Type: array
  • Members:
    • id (number)
    • device_type (string)
    • room (number)
    • name (string)
    • category_num (number)
    • etc.


jsonp.ud.scenes

  • Type: array
  • Members:
    • id (number)
    • name (string)
    • room (number)
    • Timer_ids (array)
    • Timer (object)
    • etc.


Functions

get_device_state (deviceId, serviceId, variable, dynamic)

Input:

  • deviceId (number)
  • serviceId (string)
  • variable (string)
  • dynamic (number): if 1, the variable's value is taken from lu_status, else, the variable's value is taken from user_data. user_data is read at Luup startup, so for variables that change their value frequently (like Watts, Temperature, etc.) dynamic should be 1.

Output:

  • variable value (string or undefined)


set_device_state (deviceId, serviceId, variable, value, dynamic)

Input:

  • deviceId (number)
  • serviceId (string)
  • variable (string)
  • value (string)
  • dynamic (number): if 1, the variable's value is saved to lu_status (the value is lost after Luup is restarted), else, the variable's value is saved to a copy of user_data; the real user_data is updated when LuaUPnP is restarted.

Output:

  • true if the operation succeeded, false otherwise


_console (str)

Input:

  • str (string): text to be written in the console. You need to have FireBug installed in order to have a console.

Output:

  • nothing


set_panel_html (html)

Used in the tab rendering functions.

Input:

  • html (string): The html to be used for generating the tab content.

Output:

  • nothing

JavaScript API for UI7

Introduction

One of the new features of UI7 is the Javascript API for writing plugins. This is a free programming interface which is robust, well documented and comes with a guarantee that it will be compatible with the following UI releases.

What is this “API” ?

Simply said: it is a layer between the UI and the plugin developer. This way, the developer can use the features available in the UI by interacting only with the API. By using the new features available in the API, the overall user experience can be visibly improved.

How does it work ?

The API comes in the form of a library which exposes to the developer the objects and methods/functions needed to implement functionality in their plugins.

How is this different from the previous way of writing plugins ?

The developers were writing Javascript code and they had full access to the core of the application. There were a couple of objects and functions written by the UI team which were easing the work for the developer. By using the new API, the developer can write code without interfering with the core of the application.

What is new ?

  • The developer can write the plugin without interfering with any other code except the API. This means there are a great bundle of methods like setters and getters which help writing good code.
  • UI team can make any changes to the core of the application, but as long as the API stays the same, the plugins will work. This way, the application can be improved and the developer won’t have to fix the code in order for it to work. New versions of the UI can be launched and the plugins will be compatible.
  • Event handling - the application will notify the plugin (via the API) about the events that happen inside. Consider, for example, the addition of a new device to the unit. The developer will know, by simply listening to an event, that a device was added and so, (s)he will write the code in such a way that it will take into account the event. The user experience will be greatly improved.
  • Asynchronous getters and setters - because most of the actions that happen inside the UI is asynchronous, so are the API’s method written. Most of them have a signature like this: function getXXX(obj, onSuccess, onFailure), where ‘onSuccess’ and ‘onFailure’ are callback functions which are called based on the result of the call.
  • No more global variables to manipulate. Everything is handled through getters/setters via API.
  • An API reference documentation: every method and its parameters is carefully documented so that the developer would know what the function does, how to call it and what it will return.
  • A couple of guidelines will be given to help developers write better code in such a way that two different plugins won’t “harm” each other. The module pattern will be used as a design pattern in order to put a plugin code inside a namespace. Also, this module helps in writing code which is easier to read and use.
  • It will be easier for the developers to write the code by using a new feature in the UI which generates the skeleton for the plugin.

What is the API reference documentation ?

This is the part which makes the API usable. Every aspect of the API, no matter how trivial, will be stated explicitly. It contains:

  • a description of all the data structures it depends upon
  • a description of all the function signatures:
    • function names
    • function parameters (names and types)
    • return type of the function
    • if it throws any exceptions or not

Where is the list of functions which were replaced ?

In the API reference documentation the developer can find out if a function replaces an old one.

Why should a developer write plugins using the API when it can do some “reverse engineering” and directly access any object/functionality of the application ?

It is not mandatory to write the code for the plugin this way. Probably the plugin will work as long as changes are not made in the core of the application, but the API comes also with a guarantee that after application changes, the plugins will continue to work. The UI team and plugin developers have the same goal: to deliver the best experience to the user. This is where the API comes into place - an interface between the two.

Will the API be rewritten with each new UI release ?

No. It will be improved. The goal of the API is to provide a stable interface to the developer so that when changing the core of the application it won’t be necessary to rewrite the plugins.

Is this API going to change in the near future ?

It can be changed. There will be parts of it which are going to be marked as ‘deprecated’. This means that the parts will be candidates for being removed, or modified in a backward incompatible way. The developers will be advised on how are they to change their code in order to use the features which replace the deprecated parts.

Is the API complete ?

Almost. Most of the functionality is there, but developers are encouraged to request new features from the API which will be added in future releases.

Guidelines on writing Javascript code for plugins in UI7

  • Use the module pattern to organize your code inside a namespace. Choose a well defined name for your namespace.
  • Generate an UUID for your plugin. This way you can use the events engine.
  • Use the provided API plugins to perform most of your stuff. If you can’t find an implemented method inside the API you can ASK for an implementation to it.
  • NEVER USE variables/functions which are not accessible through API ! They can be changed or removed at any time.
  • If you need to do a background job (ex: start a timer), use the events engine and register a ‘cleanup’ function in which you finish the job (clear the timer).
  • We encourage the use of try { … } catch { … } blocks everywhere in your functions so that errors don’t get propagated to the top of the application.
  • Start from the skeleton provided and expand it:
var MyPlugin = (function (api) {
// example of unique identifier for this plugin...
var uuid = 'B971F6C4-F315-4E2E-AD08-F029A9777517';
 
var myModule = {};
 
function doSomething() {
        // register to event ‘on_ui_cpanel_before_close’...
        api.registerEventHandler('on_ui_cpanel_before_close', myModule, ‘cleanUp’);
        // and do something... 
    }
 
    function cleanUp() {
        // perform clean up...
    }
 
    myModule = {
        uuid: uuid,
        doSomething: doSomething,
        cleanUp: cleanUp
    };
    return myModule;
})(api);

Your plugin functions will be accessible via MyPlugin.doSomething() construction. When the user will leave the cpanel, your plugin will be noticed via event ‘on_ui_cpanel_before_close’; this way you can perform the necessary cleanup.

JavaScript API

Defined in: api.js

Class Summary


API(config, application, ui)

- API class for plugins

Parameters:

  • {object} config - holds configuration data
  • {object} application - application layer
  • {object} ui - interface

Method Summary


{Object} cloneObject(obj)

- Clones a given object

- Replaces: cloneObject()

Parameters:

  • {object} obj - object to be cloned

Returns:

  • {Object}

{*} getCommandURL()

- Retrieves command URL

- Replaces: command_url

Returns:

  • {*}

{string} getCpanelContent()

- Retrieves cpanel content

- Replaces: get_panel_html()

Returns:

  • {string} HTML code set in cpanel

{number} getCpanelDeviceId()

- Returns device id for current opened cpanel or 0 if not found

Returns:

  • {number} - device ID

getCurrentHouseMode(onSuccess, onFailure, context)

- Performs a data request to retrieve the current house mode

Parameters:

  • {*} onSuccess Optional - function to be called if the call is successfull
  • {*} onFailure Optional - function to be called if the call fails
  • {object} context Optional - context in which to execute the functions of success and/or failure

{*} getDataRequestURL()

- Retrieves data request URL

- Replaces: data_request_url

Returns:

  • {*}

{number} getDeviceIndex(deviceId)

- Retrieves device index for given device id

- Replaces: get_device_index()

Parameters:

  • {number} deviceId - id of the device

Returns:

  • {number}

{*} getDeviceObject(deviceId)

- Retrieves device object given its id

- Replaces: get_device_obj()

Parameters:

  • {number} deviceId - id of the device

Returns:

  • {number}

{object} getDeviceState(deviceId, service, variable, options)

- Retrieves device state for a device (it can be either read from user data or lu status, depending on the options set)

Parameters:

  • {number} deviceId - id of the device
  • {string} service - name of the service
  • {string} variable - name of the variable
  • {object} options Optional - list of options like: 'dynamic': (true - lu_status will be used [default], false - user_data will be used)

Returns:

  • {object} value for a given variable

{object} getDeviceStateVariable(deviceId, service, variable, options)

- Retrieves device state for a device (it can be either read from user data or lu status, depending on the options set)(same as api.getDeviceState())

Parameters:

  • {number} deviceId - id of the device
  • {string} service - name of the service
  • {string} variable - name of the variable
  • {object} options Optional - list of options like: 'dynamic': (true - lu_status will be used [default], false - user_data will be used)

Returns:

  • {object} value for a given variable


{Object|Boolean} getDeviceTemplate(deviceId)

- Returns device template for a given device id

Parameters:

  • {number} deviceId - id of the device

Returns:

  • {Object|Boolean}

{*} getDisplayedDeviceName(deviceId)

- Retrieves displayed device name or 'unnamed device' if device doesn't have a name or undefined if device not found

Parameters:

  • {number} deviceId - id of the device

Returns:

  • {*} if device is found a string which contains the device name or 'Unnamed device' if device name is not set, or undefined if device wasn't found

{*} getEventDefinition(deviceType)

- Retrieves event list for a given device type

- Replaces: get_event_definition()

Parameters:

  • {string} deviceType - type of device

Returns:

  • {*}

{Array} getListOfDevices()

- Returns the list of devices from userdata

Returns:

  • {*}

{Array} getListOfSupportedEvents()

- Returns the list of supported events

Returns:

  • {Array} - list containing each event a plugin can subscribe to

getLuSdata(onSuccess, onFailure, context)

- Performs a data request to retrieve lu_sdata

Parameters:

  • {*} onSuccess Optional - function to be called if the call is successfull
  • {*} onFailure Optional - function to be called if the call fails
  • {object} context Optional - context in which to execute the functions of success and/or failure

{*|Object} getRoomObject(roomId)

- Returns room object given a room id

- Replaces: get_room_by_id()

Parameters:

  • {number} roomId - id of the room

Returns:

  • {*|Object}

{string} getSceneDescription(sceneId, options)

- Retrieves scene description

Parameters:

  • {number} sceneId - id of the scene
  • {object} options Optional

- Options can be :

{Boolean} hideTriggers: hides triggers description (default: false)

{Boolean} hideSchedules: hides schedules description (default: false)

{Boolean} hideActions: hides actions description (default: false)

{Boolean} hideNotifications: hides notifications description (default: false)

Returns:

  • {string}

{*} getSendCommandURL()

- Retrieves send command URL

Returns:

  • {*}

{*} getSysinfo()

- Retrieves sysinfo JSON

- Replaces: sysinfoJson

Returns:

  • {*}

{*} getUserData()

- Retrieves userData or null if error

- Replaces: jsonp.ud

Returns:

  • {*} object containing userdata

performActionOnDevice(deviceId, service, action, options)

- Same as 'performLuActionOnDevice' Performs an action on a device

Parameters:

  • {number} deviceId
  • {string} service
  • {string} action
  • {object} options Optional

- Ex: options can be: { actionArguments: {arg1: 'val1', arg2: 'val2'}, onSuccess: function() {}, onFailure: function() {}, context: that }

performLuActionOnDevice(deviceId, service, action, options)

- Same as 'performActionOnDevice' Performs a lu_action on a device

Parameters:

  • {number} deviceId
  • {string} service
  • {string} action
  • {object} options Optional

- Ex: options can be: { actionArguments: {arg1: 'val1', arg2: 'val2'}, onSuccess: function() {}, onFailure: function() {}, context: that }

registerEventHandler(eventName, object, functionName)

- Register an event handler.

Parameters:

  • {string} eventName - name of the event to register to
  • {object} object - the object which registers to the event
  • {string} functionName - the name of the function to be executed if event triggers

runUpnpCode(code, options, onSuccess, onFailure, context)

- Runs given Upnp code

Parameters:

  • {string} code - code to execute
  • {object} options Optional - optionally options to be supplied (will be documented later)
  • {*} onSuccess Optional - function to be called if the call is successfull
  • {*} onFailure Optional - function to be called if the call fails
  • {object} context Optional - context in which to execute the functions of success and/or failure

setCpanelContent(html)

- Sets content for the cpanel

- Replaces: set_panel_html()

Parameters:

  • {string} html - the new content of the cpanel

setCurrentHouseMode(modeValue, onSuccess, onFailure, context)

- Sets current house mode

Parameters:

  • {number} modeValue - new value for the house mode
  • {*} onSuccess Optional - function to be called if the call is successfull
  • {*} onFailure Optional - function to be called if the call fails
  • {object} context Optional - context in which to execute the functions of success and/or failure

setDeviceState(deviceId, service, variable, value, options)

- Sets state for a device variable (same as API.setDeviceStateVariable())

- Replaces: set_device_state()

Parameters:

  • {number} deviceId - id of the device
  • {string} service - name of the service
  • {string} variable - name of the variable
  • {*} value - new value for the variable
  • {object} options Optional - a set of options which can be:

- onSuccess: {function} to be called if the call succedes

- onFailure: {function} to be called if the call fails

- context: {object} represents context of execution for onSuccess and onFailure functions

- dynamic: {Boolean} true - state variable will be set in lu_status (default); false - state variable will be set in user_data

setDeviceStatePersistent(deviceId, service, variable, value, options)

- Sets a persistent state for a device variable using a variableset request (same as API.setDeviceStateVariablePersistent())

Parameters:

  • {number} deviceId - id of the device
  • {string} service - name of the service
  • {string} variable - name of the variable
  • {*} value - new value for the variable
  • {object} options Optional - a set of options which can be:

- onSuccess: {function} to be called if the call succedes

- onFailure: {function} to be called if the call fails

- context: {object} represents context of execution for onSuccess and onFailure functions

setDeviceStateVariable(deviceId, service, variable, value, onSuccess, onFailure, context)

- Sets state for a device variable using variableset request.

- Replaces: set_device_state()

Parameters:

  • {number} deviceId - id of the device
  • {string} service - name of the service
  • {string} variable - name of the variable
  • {*} value - new value for the variable
  • {*} onSuccess Optional - function to be called if the call is successfull
  • {*} onFailure Optional - function to be called if the call fails
  • {object} options Optional - a set of options which can be:

- onSuccess: {function} to be called if the call succedes

- onFailure: {function} to be called if the call fails

- context: {object} represents context of execution for onSuccess and onFailure functions

- dynamic: {Boolean} true - state variable will be set in lu_status (default); false - state variable will be set in user_data

setDeviceStateVariablePersistent(deviceId, service, variable, value, onSuccess, onFailure, context)

- Sets a persistent state for a device variable using a variableset request

Parameters:

  • {number} deviceId - id of the device
  • {string} service - name of the service
  • {string} variable - name of the variable
  • {*} value - new value for the variable
  • {*} onSuccess Optional - function to be called if the call is successfull
  • {*} onFailure Optional - function to be called if the call fails
  • {object} options Optional - a set of options which can be:

- onSuccess: {function} to be called if the call succedes

- onFailure: {function} to be called if the call fails

- context: {object} represents context of execution for onSuccess and onFailure functions

Events


The list of available events can be obtained with the following command:

api.getListOfSupportedEvents()

  • on_startup_showModalLoading
    • triggered before loading resources in the browser
  • on_startup_luStatusLoaded
    • triggered after luStatus was successfully parsed
    • parameter: luStatusObject
  • on_startup_show2gInterface
    • triggered after requesting account devices only if UI is not in ‘local mode’ and ‘slowConnection’ flag is set
  • on_ui_requireSave
    • triggered if user data was changed and save wasn’t issued
  • on_ui_userDataFirstLoaded
    • fired if user data was loaded for the first time
  • on_ui_staticDataAdded
    • fired if static_data was added to user data
    • parameter: staticDataObject
  • on_ui_staticDataChanged
    • fired if static_data was changed after parsing user data
    • parameter: changedStaticDataObject
  • on_ui_staticDataRemoved
    • fired if static_data was removed after parsing user data
    • parameter: idForRemovedStaticDataObject
  • on_ui_setupDeviceAdded
    • fired if new setup device (menu item) is added
    • parameter: setupDeviceObject
  • on_ui_setupDeviceChanged
    • fired if setup device (menu item) is changed
    • parameter: changedSetupDeviceObject
  • on_ui_setupDeviceRemoved
    • fired if setup device (menu item) is removed
    • parameter: idForRemovedSetupDeviceObject
  • on_ui_sectionAdded
    • triggered when new section is added
    • parameter: sectionObject
  • on_ui_sectionChanged
    • fired after section was changed
    • parameter: changedSectionObject
  • on_ui_sectionRemoved
    • fired after section was removed
    • parameter: idForRemovedSectionObject
  • on_ui_roomAdded
    • fired when new room was added to user data
    • parameter: roomObject
  • on_ui_roomChanged
    • fired after room was changed
    • parameter: changedRoomObject
  • on_ui_roomRemoved
    • fired after room was removed from user data
    • parameter: idForRemovedRoomObject
  • on_ui_deviceAdded
    • fired after device was added to user data
    • parameter: deviceObject
      • wrapper object for
        • device: this is the actual object representing the device
        • deviceTemplate: object with the static data associated to this device type
  • on_ui_deviceChanged
    • fired after device was found changed in user data
    • parameter: changedDeviceObject
      • wrapper object for
        • device: this is the actual object representing the device
        • deviceTemplate: object with the static data associated to this device type
  • on_ui_deviceRemoved
    • fired after device was found removed from user data
    • parameter: idForRemovedDeviceObject
  • on_ui_deviceJobsReceived
    • fired when jobs are found for a device in lu_status
    • parameter: deviceObjectFromLuStatus
  • on_ui_sceneAdded
    • fired after scene was added
    • parameter: sceneObject
  • on_ui_sceneChanged
    • fired after scene was changed
    • parameter: changedSceneObject
  • on_ui_sceneChanged
    • fired when scene is removed
    • parameter: idForRemovedSceneObject
  • on_ui_pluginAdded
    • triggered when plugin is added to user data
    • parameter: pluginObject
  • on_ui_pluginChanged
    • triggered when plugin is changed
    • parameter: changedPluginObject
  • on_ui_pluginRemoved
    • triggered when plugin is removed
    • parameter: idForRemovedPluginObject
  • on_ui_extraSubmenuItemAdded
    • triggered when extra submenu item is added
    • parameter: extraSubmenuItemObject
      • wrapper for:
        • submenuItem: object containing submenu item
        • deviceType: string which contains the type of device for which the submenu item is added
  • on_ui_extraSubmenuItemChanged
    • triggered when extra submenu item is changed
    • parameter: changedExtraSubmenuItemObject
      • wrapper for:
        • oldObj: contains previous submenu item
        • newObj: wrapper for:
          • submenuItem: object containing submenu item
          • deviceType: string which contains the type of device for which the submenu item is added
  • on_ui_extraSubmenuItemRemoved:
    • triggered when an extra submenu item is removed
    • parameter: idForRemovedExtraSubmenuItemObject
  • on_ui_cardAdded
    • triggered when card is added to user data
    • parameter: cardObject
  • on_ui_cardChanged
    • triggered when card is found changed in user data
    • parameter: changedCardObject
  • on_ui_cardRemoved
    • triggered when card is removed from user data
    • parameter: idForRemovedCardObject
  • on_ui_jobsUpdated
    • triggered when job is updated in lu_status
    • parameter: luStatusJobsObject
  • on_ui_deviceStatusChanged
    • triggered when device status is changed
    • parameter: deviceObjectFromLuStatus
  • on_ui_syncClock
    • triggered after lu_status is parsed
    • parameter: luStatusLocalTime
  • on_ui_initFinished
    • fired when init has finished (UI should be ready now)
  • on_ui_setUserAuthenticated
    • fired after login is successful
  • on_ui_saveFinished
    • fired after save is finished
  • on_ui_initNotLogged
    • triggered if user is not logged in
  • on_ui_alertsUpdated
    • fired after alert list was updated
  • on_ui_refreshDashboardCards
    • triggered when dashboard cards are refreshed
  • on_ui_engineAvailable
    • triggered if engine is not busy
  • on_ui_2GEnabled
    • triggered when parsing lu_status if 2G is enabled
  • on_ui_userDataSetTemperatureFormat
    • triggered after temperature format is set in user data
    • parameter: userDataObject
  • on_ui_userDataLoaded
    • triggered after user data parsing is completed
  • on_parseUserData
    • triggered when user data parsing is started
  • on_ui_accountUnitsLoaded
    • fired after account lis from MMS was successfully loaded
  • accountInformationLoaded
    • triggered after account information is loaded
    • parameter: accountInfoObject
  • on_ui_finishDrawingMenu
    • triggered after parsing of user data setup devices has finished
  • on_ui_deviceTypeChanged
    • triggered if device type for a device has changed
    • parameter: deviceObject
      • wrapper object for
        • device: this is the actual object representing the device
        • deviceTemplate: object with the static data associated to this device type
  • on_ui_hideModalLoading
    • triggered each time a time consuming job finishes (so that UI can hide modal loading)
  • on_ui_showUserDataSaveFailure
    • triggered if user data save has failed
  • on_ui_showErrorMessage
    • fired if application has error message to send to the UI
  • on_ui_cpanel_before_close
    • fired before closing the cpanel to allow plugins to cleanup their data
    • parameters: idForDeviceWhichOpenedTheCpanel

Migration Walkthrough for plugins with JS

With the introduction of our new JavaScript API, plugin mechanism slightly changed. In order to keep a backward compatibility with the UI5 version, there are a couple of thing a developer should do :

  • Update the JS file using the JavaScript API
  • Update the JSON file with proper calls for JavaScript functions

For UI5 version, the JavaScript functions were called in JSON file like this :

"ScriptName": "Your_JS_File.js",

"Function": "Your_Function_Name"

Example: Home Care Plugin - JSON file:

{
	"Label": {
		"lang_tag": "users",
		"text": "Users"
		},
	"TopNavigationTab": "1",
	"Position": "0",
	"TabType": "javascript",
	"ScriptName": "J_HomeCare.js",
	"Function": "ShowUsers"
},

For UI7, the JavaScript functions are called like this :

"ScriptName": "Your_JS_File_for_UI7.js",

"Function": "Your_Class_Name.Your_Function_Name"

Example:

{
	"Label": {
		"lang_tag": "users",
		"text": "Users"
	},
	"TopNavigationTab": "1",
	"Position": "0",
	"TabType": "javascript",
	"ScriptName": "J_HomeCare_UI7.js",
	"Function": "HomeCare.ShowUsers"
},

By now, you should be asking yourself: "How plugin will work on UI5 if I update these files?". If you answer by yourself and the answer is : "It won't work", you are right. For this you will need to do an additional step:

  • Create different JSON and JS files and perform a check in your Luup code
    • As you can see in the above example for JSON file, our choice was to add "_UI7" for our JS file : "J_HomeCare_UI7.js". We suggest you to do the same, but this is not a requirement. We did the same with JSON file : "D_HomeCare_UI7.json"
    • In order to use the new JSON and JS files in UI7 instead of UI5 versions, "device_json" attribute of your device must set with the JSON file for UI7. For this, you can use the following function:
 
local function checkVersion()
	local ui7Check = luup.variable_get(SID, "UI7Check", lug_device) or ""
	if ui7Check == "" then
		luup.variable_set(SID, "UI7Check", "false", lug_device)
		ui7Check = "false"
	end
	if( luup.version_branch == 1 and luup.version_major == 7 and ui7Check == "false") then
		luup.variable_set(SID, "UI7Check", "true", lug_device)
		luup.attr_set("device_json", "Your_UI7_JSON_File", lug_device)
		luup.reload()
	end
end

SID - your Service ID; lug_device - your device ID;

    • Call checkVersion() in your initialization function, before doing something else.

HomeCare Plugin - updated for UI7

We use the HomeCare plugin as example for what need to be added/updated for UI7. Below you can download plugin files:

File:HomeCare.zip

This is the published version of HomeCare plugin that can be downloaded from our appstore. This version is compatible with both UI5 and UI7.

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox