npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

lavva.exalushome

v2.0.176

Published

Library implementing communication and abstraction layers for ExalusHome system

Downloads

1,842

Readme

ExalusHome library for Lavva

Library installation:

If you want to use this library in any JavaScript or TypeScript project you can use npm to install it as package from npm repository. For that you will have to use command:

npm install lavva.exalushome

or

yarn add lavva.exalushome

Initialization:

If you have installed this package then you have to initialize this library first before usage. To do this, you have to call:

import { Api } from 'lavva.exalushome';
const api = Api.Init();

Getting services:

This library implements controller APIs as a group of services that implement different functionalities. Currently we have services as:

IExalusConnectionService
IDevicesService
ILocalStorageService
IWebApiCacheService
IUsersService
ISessionService
ILoggerService
IControllerConfigurationService
IChannelsGroupsService
IRemoteStorageService
...

To get any service that is registered in dependency container you can use:

const api = Api.Get<TInterface>(ServiceClassName);

//ex:
import { LoggerService } from 'lavva.exalushome/build/js/Services/Logging/LoggerService';
import { ILoggerService } from 'lavva.exalushome/build/js/Services/Logging/ILoggerService';
const logger = Api.Get<ILoggerService>(LoggerService.ServiceName);

Where as generic T parameter you have to put the iterface that is used by given service and as the (ServiceClassName) parameter you have to put the reference (name) of service that you want to use.

IExalusConnectionService

This service allows us to connect to any controller using cloud connection (in future it will implement local communication too) and call controller APIs. All calls to APIs are asynchronous and can be done concurently. They can be executed out of order depending on the time when given API was called and how fast it can be handled by controller and devices. It is a good practice to wait asynchronously for api call execution.

If you want to connect to controller you have to take service from dependency container and call method with controller serial number and PIN.

import { Api } from 'lavva.exalushome';
import { ExalusConnectionService } from 'lavva.exalushome/build/js/ExalusConnectionService';
import { AuthorizationInfo, IExalusConnectionService } from 'lavva.exalushome/build/js/IExalusConnectionService';
import { ILoggerService } from 'lavva.exalushome/build/js/Services/Logging/ILoggerService';
import { LoggerService } from 'lavva.exalushome/build/js/Services/Logging/LoggerService';

/// get logger service
const log = Api.Get<ILoggerService>(LoggerService.ServiceName);

/// get exalus connection service
const connection = Api.Get<IExalusConnectionService>(ExalusConnectionService.ServiceName);

/// subscribe to connection state changed event
connection.OnConnectionStateChangedEvent().Subscribe(state => log.Info(`Connection state has changed to: ${state}`));

/// connnect to controller and authorize
const result = await connection.ConnectAndAuthorizeAsync(new AuthorizationInfo(serial, pin));
log.Info(`Connection and authorization result: ${result}`);

/// check connection and authorization result
if (result != ConnectionResult.Connected) {
   log.Error("Failed to connect to controller!");
   return;
}

log.Info("Connected to controller!");

ISessionService

This service represents the current app session and has reference to currently logged in user (if any is currently logged in) and can login or logout user. There are two events that you can subsribe to if you want to know when user is logged in or logged out. User can be logged out by controller so it should be handled.

import { ISessionService, LoginError } from 'lavva.exalushome/build/js/Services/Session/ISessionService';
import { SessionService } from 'lavva.exalushome/build/js/Services/Session/SessionService';

/// get session service
const session = Api.Get<ISessionService>(SessionService.ServiceName);
session.OnUserLoggedOutEvent().Subscribe(user => log.Info(`User logged out from controller!`));
session.OnUserLoggedInEvent().Subscribe(user => log.Info(`User logged in to controller`));

/// login user
const loginResult = await session.UserLogInAsync("installator@installator", "QT54K96X3P9299");

/// check login result
if (loginResult instanceof User) {
  log.Info(SessionService, `logged in as ${loginResult.Name} ${loginResult.Surname} email: ${loginResult.Email}`);
}
  else
    log.Error(SessionService, `Failed to login, error code: ${loginResult}`);

IUsersService

This service allows us to manage users registered in controller. You can take users list, delete them, create or update.

 /// get users
 const usersApi = Api.Get<IUsersService>(UsersService.ServiceName);
 const users = await usersApi.GetUsersAsync();
 users.forEach(user => log.Info(`User: ${user.Email}`));

IDevicesService

This service allows us to manage and control devices.

Getting DevicesService:

const devicesApi = Api.Get<IDevicesService>(DevicesService.ServiceName);
await devicesApi.WaitForSynchronizationAsync();

Getting devices list:

const devices = await devicesApi.GetDevicesAsync();
devices.forEach(device => {
   log.Info(`[DEVICE] Name: ${device.Name} Guid: ${device.Guid}`);
   device.AvailableTaskTypes.forEach(task => log.Info(`    Task type: ${task.InterfaceType} enum type: ${task.Type}`));
   device.AvailableResponseTypes.forEach(resp => log.Info(`    Response type: ${resp.InterfaceType} enum type: ${resp.Type}`));
   device.Channels.forEach(ch => {
     log.Info(`      Channel: ${ch.Name} num: ${ch.Number}`);
     ch.AvailableTaskTypes.forEach(task => log.Info(`        Task type: ${task.InterfaceType} enum type: ${task.Type}`));
     ch.AvailableResponseTypes.forEach(resp => log.Info(`        Response type: ${resp.InterfaceType} enum type: ${resp.Type}`));
   });
 });

Subscribing to device state changes:

let onStateChange = (stateChange: IDeviceState<any>) => log.Info(`[DEVICE][STATE CHANGED] changed DeviceGuid: ${device.Guid} Name: ${device.Name} \nstate: ${stateChange.TypeAsEnum}"`);
device.OnDeviceStateChangedEvent().Subscribe(onStateChange);

Canceling subscription to device state changes:

device.OnDeviceStateChangedEvent().Unsubscribe(onStateChange);

Monitoring tasks execution on devices:

 devicesApi.OnDevicesTasksExecutionChangeEvent().Subscribe(tasks => {
   tasks.forEach(task => log.Info(`Executing tasks on device: ${task.DeviceGuid} channel: ${task.Channel} status: ${task.Status}`));
 });

Searching for devices:

devicesApi.OnDeviceFoundEvent().Subscribe(device => log.Info(`Found new device ${device.Name}`));
await devicesApi.FindDevicesAsync();
const foundDevices = await devicesApi.GetFoundDevicesAsync();

Registering found devices:

foundDevices.forEach(async device => {
    log.Warning(`Found device: ${device.Name}`);
    switch (await devicesApi.RegisterDeviceAsync(device)) {
      case DeviceTaskExecutionResult.Executed:
        log.Warning(`Device ${device.Name} has been successfully registered!`);
        break;

      case DeviceTaskExecutionResult.Failed:
        log.Error(`Failed to register device: ${device.Name}`);
        break;

      case DeviceTaskExecutionResult.ControllerResponseTimeout:
        log.Error(`Controller response timeout, unknown status of device: ${device.Name}`);
        break;

      case DeviceTaskExecutionResult.DeviceResponseTimeout:
        log.Error(`Device response timeout, unknown status of device: ${device.Name}`);
        break;
    }
  });

Turning on light on first channel on first device that can be turned on:

(await devicesApi.GetDevicesAsync()).first(device => device.Channels.any(channel => channel.AvailableTaskTypes.any(a => a.Type === DeviceTaskType.TurnOn && channel.Number === 1)))
   .Channels.first(channel => channel.Number === 1)
   .ExecuteTaskAsync(new TurnOn());
   
/// alternatively you can use
let task = new TurnOn();
task.Channel = 1;
devicesApi.ExecuteDeviceTaskAsync(device, task);

Turning off light on first channel on first device that can be turned on:

(await devicesApi.GetDevicesAsync()).first(device => device.Channels.any(channel => channel.AvailableTaskTypes.any(a => a.Type === DeviceTaskType.TurnOff && channel.Number === 1)))
   .Channels.first(channel => channel.Number === 1)
   .ExecuteTaskAsync(new TurnOff());

Turning on all channels on devices that can be turned on:

  (await devicesApi.GetDevicesAsync()).forEach(device => {
    device.Channels.forEach(async channel => {
      if (channel.AvailableTaskTypes.any(a => a.Type === DeviceTaskType.TurnOn)) {
        let task = new TurnOn();
        task.Channel = channel.Number;
        tasks.push([device, task]);
      }
    });
  });
  
  let result = await devicesApi.ExecuteDevicesTasksAsync(tasks);
  result.forEach(response => {
    switch (response.Result) {
      case DeviceTaskExecutionResult.Executed:
        log.Info(`Turned on device: ${response.Device?.Name} on channel: ${response.Task?.Channel}`);
        break;

      case DeviceTaskExecutionResult.DeviceResponseTimeout:
        log.Warning(`Failed to turn on device: ${response.Device?.Name} on channel: ${response.Task?.Channel}, device response timeout occurred`);
        break;

      case DeviceTaskExecutionResult.ControllerResponseTimeout:
        log.Warning(`Unknown device state: ${response.Device?.Name} on channel: ${response.Task?.Channel}, controller response timeout occurred`);
        break;

      case DeviceTaskExecutionResult.Failed:
        log.Error(`Error uccured when tried to turn on device: ${response.Device?.Name} on channel: ${response.Task?.Channel}`);
        break;
    }
  });

Enabling fast devices states synchronization:

await devices.EnableFastStatesSyncAsync();

Disabling fast devices states synchronization:

await devices.DisableFastStatesSyncAsync();

Checking if fast states synchronization is enabled: await devices.IsFastStatesSyncEnabledAsync();

IControllerConfigurationService

This service allows us to manage, control and monitor controller configuration.

Getting IControllerConfigurationService:

const controllerConfigurationApi = Api.Get<IControllerConfigurationService>(ControllerConfigurationService.ServiceName);

Monitoring controller configuration changes:

controllerConfigurationApi.OnConfigurationTimeCheckedEvent().Subscribe(date => log.Warning(`Controller configuration time changed: ${date}`));
controllerConfigurationApi.OnEnteredConfigurationEvent().Subscribe(() => log.Warning(`Controller entered configuration mode`));
controllerConfigurationApi.OnExitedConfigurationEvent().Subscribe(() => log.Warning(`Controller exited configuration mode`));

Entering configuration mode:

await controllerConfigurationApi.EnterConfigurationModeAsync();

Exiting configuration mode:

await controllerConfigurationApi.ExitConfigurationModeAsync();

Check if configuration has changed in comparison to the last known konfiguration by this app instance:

if (await controllerConfigurationApi.DidCofigurationChangeAsync())
   log.Warning(`Controller configuration has changed! ${await controllerConfigurationApi.GetLastConfigurationChangeTimeAsync()}`);\
else
   log.Debug("Controller configuration did not change!");

IChannelsGroupsService

This service allows us to control devices channels groups.

Getting IChannelsGroupsService:

const groupsApi = Api.Get<IChannelsGroupsService>(ChannelsGroupsService.ServiceName);
await groupsApi.WaitForSynchronizationAsync(); // good to use one time after user logs in.

Getting groups:

let groups = await groupsApi.GetGroupsAsync();
groups.forEach(group => log.Info(`Group: ${group.Name} guid: ${group.Guid}`));

Removing group:

await groupsApi.RemoveGroupAsync(groups.first(a => a.Name === "TEST"));

Creating new group:

let gr = new ChannelsGroup();
gr.Name = "TEST";
await groupsApi.AddNewGroupAsync(gr);

Adding device channel to group:

 await groupsApi.AddDeviceChannelToGroupAsync((await devicesApi.GetDevicesAsync()).first().Channels.first(), gr);

IRemoteStorageService

This service allows application to store data (whatever it want's to store) in similar way like Local Storage in JavaScript but this data is not stored locally in browser but it is stored on Exalus Home controller. It can be then synchronized between app instances and retrieved or updated by app. It allows to store data in context of given logged in user or as global database that can be retrieved and modified by anyone. If it is stored in context of user then only the user that created given database can access it. You can have similar databases with same name for each user in the system and given user will be able to modify only it's own database.

Getting service instance:

const remoteStorage = Api.Get<IRemoteStorageService>(RemoteStorageService.ServiceName);

Saving data globally accessible by every user:

await remoteStorage.SaveAsync("data_unique_id", true, "some data that can be an object or array too");

Saving data for user that is logged in:

await remoteStorage.SaveAsync("data_unique_id", false, "some data that can be an object or array too; in context of user");

Retrieving data that has global access:

await remoteStorage.ReadAsync<boolean>("data_unique_id", true, false)

Retrieving data for user that is logged in:

await remoteStorage.ReadAsync<boolean>("data_unique_id", false, false)

Removing global data (it only works on the newest controller software, older version don't have that capability but all will have it after update):

await remoteStorage.RemoveAsync("data_unique_id", true)

Removing user data (it only works on the newest controller software, older version don't have that capability but all will have it after update):

await remoteStorage.RemoveAsync("data_unique_id", false)