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 🙏

© 2024 – Pkg Stats / Ryan Hefner

lwnoodle

v2.4.0

Published

A lightweight client and server implementation for Lightware 3 protocol

Downloads

19

Readme

License Automated tests TSLint code coverage lines code coverage functions code coverage statements code coverage branches

LwNoodle

LwNoodle is a server-client architecture based library where the server represents different status, settings and callable functions in a tree-organized structure and arbitrary number of clients can read / modify / call these items. Instead of synchronizing the whole tree, LwNoodle clients read only the requested information. If the client is interested of a change of some values, listeners can be added instead of polling values. LwNoodle provide a nice dynamic object-relational-model syntax, while supporting typescript enables nice code-completion in your favorite IDE.

Example

Install the module from npm:

$ npm install lwnoodle

Server side:

const lwnoodle = require('lwnoodle');

const server = lwnoodle.noodleServer({host:'127.0.0.1'});  

server.APPLICATION.Name = 'My Application';
server.LED.setRGB = (r,g,b) => { /* do something with r,g,b values */}
server.AUDIO.SoundLevel = 50;
server.AUDIO.on('SoundLevel', (path, prop, value)=>{ /* do something with the new value when it has changed*/})
setInterval(()=>{ server.APPLICATION.STATUS.Time = Date.now(); }, 1000);    // update APPLICATION.STATUS.Time in every second

And on client side:

const lwnoodle = require('lwnoodle');

const noodle = lwnoodle.noodleClient('192.168.0.1');  

await noodle.__connect__();  // you might want to wait while the connection created. You need to make an await

console.log(await noodle.APPLICATION.Name); // 'My Application'
noodle.LED.setRGB(10,20,30); // call the function on the server
noodle.AUDIO.SoundLevel = 50;   // Update sound level. Server-side callback listener will be called
noodle.APPLICATION.STATUS.on('Time', (path, prop, value)=>{ console.log(value); }); // will print the new values of timestamp

Features

  • Tree like structure, freely extendable by custom nodes and leafs. Leafs are properties and methods.
  • Properties can be read-only or readable/writable for the clients
  • Methods can have arbitrary number of parameters
  • Data types for properties can be strings, numbers, boolean or lists
  • Arbitrary number of clients can connect
  • A client can listen for arbitrary number of nodes / properties*
  • All methods / properties can have optionally a description (manual), which can be queried by the clients.
  • Symbolic links can be created within the tree
  • Supports raw TCP socket connection and WebSocket (incl. secure websockets)
  • Typescript support

Protocol

By default, LW3 protocol is used for client-server communication, so it can be used to communicate with devices / softwares supporting LW3 protocol. In the future, other protocols might be added.

More about LW3 Protocol: https://lightware.com/pub/media/lightware/filedownloader/file/White-Paper/Lightware_s_Open_API_Environment_v3.pdf

DISCLAIMER: This project is not officially supported by Lightware. There are no guarantee that it will work in your production environment

Client reference

noodleClient will create a client connection:


const noodle = noodleClient('192.168.0.10');        

const noodle = noodleClient({host: '192.168.0.10', port: '1010'});        

const noodle = noodleClient({host: '192.168.0.10', waitresponses:true});  // will wait response before sending the next request to the server. Used for debugging.

Getting / setting properties

//Getting properties. Return values are casted to string, boolean, number or Array<string> based on the value

console.log('Part number is ',await noodle.PartNumber);

if (await noodle.MEDIA.GPIO.P7.InputState) {
    :
}

Setting properties:

noodle.MEDIA.GPIO.P1.State = 'High';

//setting is done in async manner in the background. If you need to wait while it completes, please use:

await noodle.__sync__();

Methods

Call methods:

const result = await noodle.MEDIA.GPIO.P1.toggle();  

await noodle.MEDIA.XP.switch('I1','O1);

You can watch for changes:

// Add listener functions

noodle.MEDIA.PORTS.I1.on('SignalPresent', (path,property,value)=>{
    // will be called when 'SignalPresent' property has been changed
});

noodle.MEDIA.PORTS.I1.on('SignalPresent=true', (path,property,value)=>{
    // will be called when 'SignalPresent' property has been changed to true
});

noodle.MEDIA.PORTS.I1.on('*', (path,property,value)=>{
    // will called on any changes on MEDIA.PORTS.I1
});

noodle.MEDIA.PORTS.I1.on((path,property,value)=>{
    // same as above, you can just omit the first parameter
});

Or just add a one time listener:


noodle.MEDIA.PORTS.I1.once('SignalPresent', (path,property,value)=>{
    // will be called only once
});

You can wait for an event with waitFor

await noodle.MEDIA.PORTS.I1.waitFor('SignalPresent=true');

Synchronize a node to the client

When you request for a property, it needs to be async as it will trigger communication between client and server. Sometimes you need a faster, real-time response. By creating live object, you will have a local copy of the node. Please note, that subnodes will be not synchronized.

const mynode = lwnoodle.live(noodle.APPLICATION.STATUS);

// then use it anytime later

console.log(mynode.Time); //Time property will hold the actual value, kept updated automatically

// also you can get anytime a copy of the actual snapshot of the node:

const snapshot = mynode.getSnapshot();

console.log(snapshot.Time); //This is a regular static object, Time will not be updated in the background

Server reference

noodleServer will create a server object which starts listening immediately:


const server = noodleServer();          // default port number is 6107

const server = noodleServer(6107);      // port number

const server = noodleServer({port: 6107}); // port number

const server = noodleServer({port: 6107, type:'tcp'}); // same as above

const server = noodleServer({port: 6107, type:'ws'}); // use websocket instead of raw TCP socket

const server = noodleServer([{port: 6107},{port: 6108, type:'ws'}]); // use port 6107 for TCP and 6108 for websocket, use two server backend at same time.

const server = noodleServer({port: 6107, type:'wss', key: key_in_pem_format, cert: cert_in_pem_format}); 
// use port 6107 for secure websocket, key and cert has to be in PEM format
// of course, this kind of server can be used with other types, like raw TCP socket clients as well

Defining nodes, properties, methods

Just define them:


server.PATH.TO.MY.NODE.MyProperty = 'something';

server.PATH.TO.MY.NODE.myMethod = (a,b) => { return a+b; };

Methods can have arbitrary number of parameters. The return value will be sent back to the client.

You can define the methods and properties via a json:


server.PATH.TO.MY.NODE.MyProperty = {value:'something', manual:'this is my property', rw: false};

server.PATH.TO.MY.NODE.myMethod = {func:myFunction, manual:'this is my property'};

Or you can modify read-write flag / description this way:

server.PATH.TO.MY.NODE.MyProperty = 'something';
server.PATH.TO.MY.NODE.MyProperty__rw__ = false;
server.PATH.TO.MY.NODE.MyProperty__manual_ = 'this is my property';

When use the rw after the property name, then you can set the read-write flag. When you use manual after the property name, then you can set the description.

Callbacks

You can set up callback listeners, which are called when the property is modified either by server or client:


server.PATH.TO.MY.NODE.on('MyProperty', (path, property, value)=>{ /* */});
server.PATH.TO.MY.NODE.on('MyProperty=watchedValue', (path, property, value)=>{ /* */});
server.PATH.TO.MY.NODE.on('*', (path, property, value)=>{ /* */});  // will trigger on any changes
server.PATH.TO.MY.NODE.on((path, property, value)=>{ /* */});  // same as above

When calling once, the callback will be removed after the first call:

server.PATH.TO.MY.NODE.once('MyProperty', (path, property, value)=>{ /* */});  // will trigger once

If you call waitFor, it will wait for a change and return the new value. It is an async function, so you need to use await. This is useful for waiting for a change in a property:

await server.PATH.TO.MY.NODE.waitFor('MyProperty=watchedValue');    // wait for a change

JSON conversions

Getting / modifying a single property might be inefficient as lots of magic stuff is involved in the background. If you need to read / write multiple properties, it is better to convert it to/from JSON.


server.MY.SETTINGS = {
    Led1: True,
    Led2: False,
    Volume: 12,    
    LCD: {
        Brightness: 50,
        Title: 'Hello'
    }
}

server.MY.SETTINGS.toJSON()    // will return the same thing

Creating symbolic links

You can create any number of symbolic links within the tree. Example:

server.DEVICES.DEV1.Name = 'Some important device';

server.BUILDINGS.MAIN.ROOM1.DISPLAY = server.DEVICES.DEV1;  

// server.BUILDINGS.MAIN.ROOM1.DISPLAY.Name and server.DEVICES.DEV1.Name refers to the same object.  

Naming conventions

The library relies on name conventions: nodes shall be UPPERCASE, Property names shall be CamelCase, methods are lowerCamelCase.

Also you can force casting if needed:

console.log(noodle.DATE.time__property__)           //by adding __property__, this will behave as a property
noodle.DATE.Apply__method__();                      //cast to method
noodle.MANAGEMENT.Settings__node__.Enabled=true;    //cast to node

Use with typescript

Type definition is included in the package, so you will have nice code completion with your IDE.

Because of the tricks involved about ORM and ES6 Proxies, when using tpyescript a casting to any is needed while setting a property:


noodle.PATH.TO.MY.NODE.PropertyName = 'something';    // will raise a compilation-time error

//They will work as expected:

noodle.PATH.TO.MY.NODE.PropertyName = 'something' as any;  
noodle.PATH.TO.MY.NODE.PropertyName = 42 as any;
noodle.PATH.TO.MY.NODE.PropertyName = true as any;