# LIFX LAN Node.js Library

[![NPM Version](https://img.shields.io/npm/v/lifx-lan-client.svg)](https://www.npmjs.com/package/lifx-lan-client)

A Node.js implementation of the [LIFX protocol](https://github.com/LIFX/lifx-protocol-docs). Developed to work with a minimum firmware version of 2.0.

This library is not, in any way, affiliated or related to LiFi Labs, Inc.. Use it at your own risk.

## Installation

```sh
$ npm install lifx-lan-client --save
```

```sh
$ yarn add lifx-lan-client
```

## Compatibility

Node LTS and current versions are tested and supported on Mac, Linux and Windows.
Older versions may work but are not supported or tested.

## Usage
The file `cli.js` contains a working example.

### Client
The library uses a client for network communication. This client handles communication with all lights in the network.
```js
var LifxClient = require('lifx-lan-client').Client;
var client = new LifxClient();

client.init();
```
The `Client` object is an EventEmitter and emmits events whenever any changes occur. This can be a new light discovery, a light sending a message or similar.
The client starts discovery of lights right after it is initialized with the `init` method. If a new light is found the client emmits a `light-new` event. This event contains the light as an object on which methods can be called then:

```js
var LifxClient = require('lifx-lan-client').Client;
var client = new LifxClient();

client.on('light-new', function(light) {
  // Change light state here
});

client.init();
```

### Changing light state
The states of a light can be changed with different methods:

#### `light.on([duration], [callback])`
This turns a light on.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`duration` | int | 0 | Turning on will be faded over the time (in milliseconds).
`callback` | function | null | `function(error) {}` Called after the command has reached the light or after `client.resendMaxTimes` with `client.resendPacketDelay` in case it has not. `error` is `null` in case of success and given if the sending has failed.
_Note: Using callback multiplies network load for this command by two or more times._

Usage examples:
```js
light.on(); // Turns the light on instantly
light.on(2000); // Fading the light on over two seconds
```

#### `light.off([duration], [callback])`
This turns a light off.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`duration` | int | 0 | Turning off will be faded over the time (in milliseconds).
`callback` | function | null | `function(error) {}` Called after the command has reached the light or after `client.resendMaxTimes` with `client.resendPacketDelay` in case it has not. `error` is `null` in case of success and given if the sending has failed.
_Note: Using callback multiplies network load for this command by two or more times._

Usage examples:
```js
light.off(); // Turns the light off instantly
light.off(2000); // Fading the light off over two seconds
```

#### `light.color(hue, saturation, brightness, [kelvin], [duration], [callback])`
Changes the color of a light to an HSB color value. This is the preferred method to change the color of a light.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`hue` | int | | Between 0 and 360, representing the color hue in degree which changes the color.
`saturation` | int | | Between 0 and 100, representing the color intensity from 0% to 100%.
`brightness` | int | | Between 0 and 100, representing the light brightness from 0% to 100%.
`kelvin` | int | 3500 | Between 2500 and 9000, representing the color temperature.
`duration` | int | 0 | Fade the color to the new value over time (in milliseconds).
`callback` | function | null | `function(error) {}` Called after the command has reached the light or after `client.resendMaxTimes` with `client.resendPacketDelay` in case it has not. `error` is `null` in case of success and given if the sending has failed.
_Note: Using callback multiplies network load for this command by two or more times._

Usage examples:
```js
light.color(0, 100, 50); // Set to red at 50% brightness
light.color(50, 50, 80, 3500, 2000); // Set to a light green at 80% brightness over next two seconds
```

#### `light.colorRgbHex(hexString, [duration], [callback])`
Changes the color of a light to an RGB color value given in Hex Format. Note that RGB poorly represents color of light,
prefer HSBK values given via the `color` method.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`hexString` | string | | A hex RGB string starting with `#`
`duration` | int | 0 | Fade the color to the new value over time (in milliseconds).
`callback` | function | null | `function(error) {}` Called after the command has reached the light or after `client.resendMaxTimes` with `client.resendPacketDelay` in case it has not. `error` is `null` in case of success and given if the sending has failed.
_Note: Using callback multiplies network load for this command by two or more times._


Usage examples:
```js
light.colorRgbHex('#F00'); // Set to red
light.colorRgbHex('#FFFF00'); // Set to yellow
```

#### `light.colorRgb(red, green, blue, [duration], [callback])`
Changes the color of a light to an RGB color value. Note that RGB poorly represents color of light,
prefer HSBK values given via the `color` method.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`red` | int | | Amout of red in color from 0 to 255
`green` | int | | Amout of green in color from 0 to 255
`blue` | int | | Amout of blue in color from 0 to 255
`duration` | int | 0 | Fade the color to the new value over time (in milliseconds).
`callback` | function | null | `function(error) {}` Called after the command has reached the light or after `client.resendMaxTimes` with `client.resendPacketDelay` in case it has not. `error` is `null` in case of success and given if the sending has failed.
_Note: Using callback multiplies network load for this command by two or more times._


Usage examples:
```js
light.colorRgb(255, 0, 0); // Set to red
light.colorRgb(255, 255, 0); // Set to yellow
```

#### `light.setMultiZoneEffect(effectName, speed, direction, [callback])`
Changes a color zone range to the given HSBK value

Option | Type | Default | Description
------ | ---- | ------- | -----------
`effectName` | string | | Desired effect, currently available options are: `'MOVE'`, `'OFF'`
`speed` | number | | Duration of one cycle of the effect, the higher the value the slower the effect animation
`direction` | string | | Animate from or towards the controller, available options are: `'TOWARDS'`, `'AWAY'`
`callback` | function | null | Called after command has reached the light

#### `light.maxIR(brightness, callback)`
Set's the maximum infrared brightness of the light (only for lights that support infrared light)

Option | Type | Default | Description
------ | ---- | ------- | -----------
`brightness` | int | | Between 0 and 100, representing the light brightness from 0% to 100%.
`callback` | function | | `function(error, data) {}`

Usage examples:
```js
light.maxIR(0); // Set's a maximum infrared brightness of 0
light.maxIR(25); // Set's a maximum infrared brightness of 25
```

#### `light.waveform(hue, saturation, brightness, [kelvin], [transient], [period], [cycles], [skewRatio], [waveform], [callback])`
Apply a waveform effect to the bulb.

Also see the [LIFX waveform docs](https://lan.developer.lifx.com/docs/waveforms)

Option | Type | Default | Description
------ | ---- | ------- | -----------
`hue` | int | | Between 0 and 360, representing the color hue in degree which changes the color.
`saturation` | int | | Between 0 and 100, representing the color intensity from 0% to 100%.
`brightness` | int | | Between 0 and 100, representing the light brightness from 0% to 100%.
`kelvin` | int | 3500 | Between 2500 and 9000, representing the color temperature.
`transient` | boolean | false | Whether to return to the previous color after the effect.
`period` | int | 500 | Duration of a cycle in miliseconds.
`cycles` | float | 10e30 | Number of wave cycles to stop after. Default value is effectively never. Total duration of the effect will be period * cycles.
`skewRatio` | float | 0.5 | The skew ratio to use for pulse waveforms (percentage of the time the old color is visible per period), between 0 and 1.
`waveform` | int | 0 | The [waveform to use](https://lan.developer.lifx.com/docs/waveforms), between 0 and 4. 0 = SAW, 1 = SINE, 2 = HALF_SINE, 3 = TRIANGLE, 4 = PULSE.
`callback` | function | null | `function(error) {}` Called after the command has reached the light or after `client.resendMaxTimes` with `client.resendPacketDelay` in case it has not. `error` is `null` in case of success and given if the sending has failed.
_Note: Using callback multiplies network load for this command by two or more times._

Usage examples:
```js
light.waveform(0, 100, 50); // Set to a basic red wave at 50% brightness
light.waveform(50, 50, 80, 3500, true, 200, 5); // Set to a light green wave at 80% brightness over next two seconds, which will return to the previous color after five 200ms cycles
```

#### `light.getMaxIR(callback)`
Requests the maximum infrared brightness of the light (only for lights that support infrared light)

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
{
  brightness: 25
}
```


### Requesting light state and info
Infos of the state and spec of the light can be requested with the following methods:

#### `light.getState(callback)`
Requests general info from a light, this includes color, label and power state. This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
{
  color: { hue: 120, saturation: 0, brightness: 100, kelvin: 8994 },
  power: 0,
  label: 'Kitchen'
}
```

#### `light.getPower(callback)`
Requests current power state (on or off). This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
0 // off
```

#### `light.getFirmwareVersion(callback)`
Requests the firmware version from a light (minor and major version). This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
{
  majorVersion: 2,
  minorVersion: 1
}
```

#### `light.getHardwareVersion(callback)`
Requests the hardware version from a light (vendor, product and version). This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
{
  vendorId: 1,
  vendorName: 'LIFX',
  productId: 1,
  productName: 'Original 1000',
  version: 6,
  productFeatures: {
    color: true,
    infrared: false,
    multizone: false
  }
}
```

#### `light.getFirmwareInfo(callback)`
Requests info from the micro controller unit of a light (signal, tx and rx). This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
{
  signal: 0,
  tx: 0,
  rx: 0
}
```

#### `light.getWifiInfo(callback)`
Requests wifi info from a light (signal, tx and rx). This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
{
  signal: 0.000009999999747378752,
  tx: 16584,
  rx: 12580
}
```

#### `light.getWifiVersion(callback)`
Requests the wifi firmware version from the light (minor and major version). This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`

Example result:
```js
null,
{
  majorVersion: 2,
  minorVersion: 1
}
```

#### `light.getAmbientLight(callback)`
Requests the ambient light value in flux from the light. This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`


Example result:
```js
null,
10
```

### Switch
The following methods are used to control a Switch device with relays:

#### `light.hasRelays(callback)`
Checks to see if the device is a switch with relays. This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(result) {}`


Example result:
```js
true
```

#### `light.getRelayPower(relayIndex, callback)`
Gets the value of the relay at zero-based index i (0-3). This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`relayIndex` | int | | The zero-based index of the relay (from 0 to 3)
`callback` | function | | `function(error, level) {}`


Example result:
```js
null,
65535
```

#### `light.relayOn(relayIndex, callback)`
Turns the relay on. This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`relayIndex` | int | | The zero-based index of the relay (from 0 to 3)
`callback` | function | | `function(error, data) {}`


Example result:
```js
null,
65535
```

#### `light.relayOff(relayIndex, callback)`
Turns the relay off. This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`relayIndex` | int | | The zero-based index of the relay (from 0 to 3)
`callback` | function | | `function(error, data) {}`


Example result:
```js
null,
0
```

### Labels
Labels of lights can be requested and set using the following methods:

#### `light.getLabel(callback, [cache])`
Requests the label of a light. This function is asynchronous.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`callback` | function | | `function(error, data) {}`
`cache`    | boolean  | false | Use the last known value for the label and and do not request from the light again

Example result:
```js
null,
'Kitchen'
```

#### `light.setLabel(label, [callback])`
Sets a new label for a light.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`label` | string | | New Label with 32 bit size maximum (which is a length of 32 with non unicode chars).
`callback` | function | null | `function(error) {}` Called after the command has reached the light or after `client.resendMaxTimes` with `client.resendPacketDelay` in case it has not. `error` is `null` in case of success and given if the sending has failed.
_Note: Using callback multiplies network load for this command by two or more times._


Usage examples:
```js
light.setLabel('Bedroom Light');
light.setLabel('Kitchen Light 4', function(err) {
  if (err) { throw err; }
  console.log('New light label has been set');
});
```

### Get a light
#### `client.light(identifier)`
Find a light in the list off all lights by ip, label or id.

Option | Type | Default | Description
------ | ---- | ------- | -----------
`identifier` | string | | Light label (case sensitive) `client.light('Kitchen')`, the ip address `client.light('192.168.2.102')` or the light id `client.light('0123456789012')`

Returns a light object that can then be used to call methods on it. For example `client.light('192.168.2.102').on()`.

### Get all lights

#### `client.lights([filter])`
Get a list of all known lights

Option | Type | Default | Description
------ | ---- | ------- | -----------
`filter` | string | null | Filter list of lights to return only active (`null` or `'on'`), inactive (`'off'`) or all (`''`)

### Client events
The following events might be thrown by the client.

#### `light-new`
This event is thrown when there is a new light discovery that has not been seen at runtime before. This event is provided with the new light object.
`client.on('light-new', function(light) {});`

#### `light-offline`
This event is thrown when a light hasn't been discovered for a time. The light given is no longer expected to be reachable.
`client.on('light-offline', function(light) {});`

#### `light-online`
This event is thrown when a light is discovered again after being offline.
`client.on('light-online', function(light) {});`

### Start / Stop discovery
The discovery for each client can be started and stopped at runtime using these commands:

#### `client.startDiscovery()`
Starts the discovery process.

#### `client.stopDiscovery()`
Stops the discovery process.

### Client settings
For the initialization of the client different settings can be provided. This is an example with the default options:

```js
var LifxClient = require('lifx-lan-client').Client;
var client = new LifxClient();

// ...

client.init({
  lightOfflineTolerance: 3, // A light is offline if not seen for the given amount of discoveries
  messageHandlerTimeout: 45000, // in ms, if not answer in time an error is provided to get methods
  startDiscovery: true, // start discovery after initialization
  resendPacketDelay: 150, // delay between packages if light did not receive a packet (for setting methods with callback)
  resendMaxTimes: 3, // resend packages x times if light did not receive a packet (for setting methods with callback)
  debug: false, // logs all messages in console if turned on
  address: '0.0.0.0', // the IPv4 address to bind the udp connection to
  broadcast: '255.255.255.255', // set's the IPv4 broadcast address which is addressed to discover bulbs
  lights: [], // Can be used provide a list of known light IPv4 ip addresses if broadcast packets in network are not allowed
              // For example: ['192.168.0.112', '192.168.0.114'], this will then be addressed directly
  stopAfterDiscovery: false, // stops discovery process after discovering all known lights (requires list
                             // of addresses provided with "lights" setting)
  discoveryInterval: 5000, // Interval (in ms) between discovery operations
  messageRateLimit: 50, // The delay (in ms) between sending any two packets to a single light
});
```
