Skip to content

NEEOInc/neeo-sdk

Repository files navigation

NEEO SDK Build Status Greenkeeper badge

This is the source code of the NEEO SDK to interact with the NEEO Brain.

If you're looking for examples, take a look at the example repository at https://github.com/NEEOInc/neeo-sdk-examples

Table of Contents

Prerequisite

Windows OS

  • If you use the SDK on Windows we suggest to install Apple iTunes (or the Bonjour SDK for Windows, available from the https://developer.apple.com/bonjour/ site). Windows versions < 10 seems to miss multicast DNS support (aka. Bonjour/Zeroconf). This means the Brain discovery find's a NEEO Brain but you cannot connect to the NEEO Brain (as the discovery fails). You can test if multicast DNS works on your machine when you try to ping NEEO-xxxxxxxx.local (replace xxxxxxxx with the unique hostname of your NEEO Brain).

NEEO-CLI

The NEEO-CLI now lives in a dedicated repository: @neeo/cli

The neeo-sdk cli is deprecated and will be removed in a future version to make the neeo-sdk smaller. We recommend switching to the new CLI as soon as possible.

Why the NEEO-CLI?

One goal of the CLI is to make it easy and simple to run SDK devices without writing any code for non developers. A NEEO-SDK user should be able to run multiple drivers easily without writing code (unlike development that focuses on a single driver at a time). That's where the CLI comes in:

# Example setup:
sdk-drivers/
  node_modules/
    # NEEO SDK drivers installed via npm:
    neeo-driver-lifx/
    neeo-driver-ps4/
    neeo-driver-coffee/
    # ... other modules as well
  package.json

Running neeo-cli start will find and load all 3 drivers (lifx, ps4, coffee). This means you don't have to worry about the folders, the ports or changing any code.

deprecated NEEO-sdk cli

The neeo-sdk cli will be removed in a future version. We recommend using the @neeo/cli.

To use it for your own driver, either update the NPM script:

"scripts": {
  [...]
  "start": neeo-sdk start"
}

This allows you to start your driver with npm start.

By default, the neeo-sdk cli will connect to the first discovered brain within the network. You can change this behaviour by creating a "neeoSdkOptions" property in your project's package.json, and set the "brainHost" property to the Brain IP address. Following parameters are available:

"neeoSdkOptions": {
  "brainHost": "10.0.0.131", // If configured, connect to this IP. Else connect to the first NEEO Brain discovered
}

The Brain lookup uses a default timeout of 5000ms. To change this behaviour, change the environment variable NEEO_BRAIN_LOOKUP_DURATION_MS

SDK Documentation

See https://neeoinc.github.io/neeo-sdk/ for the functions documentation.

Driver export best practices

The NEEO CLI will look for drivers installed in the node_module folder (installed via npm) which export their devices using the following format:

  1. A single file exporting all devices for example index.js:
    module.exports = {
      devices: [
        // Exports listed here will be detected,
        // these are the objects returned by neeo-sdk.buildDevice()
        exampleDriver,
      ],
    };
    
  2. Your package.json main property points to that file:
      ...
      "main": "index.js",
      ...
    
  3. In your package.json use the neeo-driver- prefix for the name property.*

This allows you to place your export where you please as long as you document it in package.json. The CLI will read the main property of the package.json for drivers installed via npm.

This is the recommended setup for exporting devices:

  • one device per driver module**
  • Set the path to your main export in your package.json: main: "index.js"

* Previously we looked for the neeo- and neeo_ prefixes, these are still detected but we recommend neeo-driver-

** In some cases like 2 devices of the same model but different series with slightly different features might share common code, they could then be part of the same module.

You can find an example driver: neeo-driver-example in the neeo-sdk-examples.

See SDK Driver Migration to 0.51.0 for migrating to the @neeo/cli

Running a single driver locally in development mode

When developing a driver it is more convient to directly run a single driver rather than install or link the driver in another folder to use the regular CLI. This also allows attaching a debugger to the process more easiler.

One way to start a driver with the server is to create for example a devStart.js file that looks like this:

"use strict";

const neeoapi = require("neeo-sdk");
const driver = require("./lib/DeviceController");

// IP of your NEEO Brain
const BRAIN_IP = '10.0.0.10';

neeoapi
  .startServer({
    brain: BRAIN_IP,
    port: 6336,
    name: 'debug-server',
    devices: [
      driver,
    ]
  })
  .then(() => console.log('Server Ready'))
  .catch((error) => {
    console.error('ERROR:', error);
    process.exit(1);
});

You can then start your driver with node devStart.js or by adding it to "start": "node devStart.js" in your package.json scripts npm start.

SDK Driver Migration to 0.50.0

To migrate a driver to the driver device export format, follow: Driver migration guide to 0.50.0. Note: We have updated the example code too, take a look there if something is unclear.

SDK Driver Migration to 0.51.0

After 0.50.0 we've decided to move the CLI to a separate repository.

To migrate a driver to the new @neeo/cli, follow: Driver migration guide to @neeo/cli.

Updating device drivers

To tell the NEEO Brain about changes to your device's components you can simply change the driver version (.setDriverVersion). If you for example add new buttons to a device, you can increase the version and this will let the Brain know to fetch the new components.

You do not need to update the version if you do not change the components. When adding the version to a device that was previously not versioned, start with 1. The NEEO Brain will assume it was previously 0 and update. The check for updates happens as soon as the SDK driver is started and registers with the NEEO Brain

### NOTE

  • the NEEO Brain will only add new components, updating or removing old components is not supported
  • the name of a component is used as an identifier for that component, changing it will result in it being added as a "new" component instead of the old one being updated

Hints

A collection of hints if you create a device driver.

  • If possible provide a device discovery to find a device to support (.enableDiscovery) - this is the most user friendly way for the user to add a device.
  • Make sure that your driver handles if the device reboots. It's possible that the device IP changed after the reboot.
  • Make sure to handle the case when the user deleted the device from the NEEO Brain, so the driver won't send notifications anymore and shut down any running services (registerDeviceSubscriptionHandler).
  • Make sure your driver allocate resources only if needed - use the registerInitialiseFunction to initialise your driver.
  • List the minimal firmware version of the device you use - this might help if a driver does not work as expected (use .enableDiscovery to show information to the user).
  • The .registerSubscriptionFunction function is used to inject the notification function to your driver. So the driver can send updates to the NEEO Brain.
  • The .registerDeviceSubscriptionHandler function is used to inform your driver how many devices are in use on the NEEO Brain and when a device is added or removed from the NEEO Brain.

Dynamic Device Builder

If you need to build different devices dynamically (for example to write an SDK driver for a controller that controls lights and switches) you can do that thanks to the updated .enableDiscovery function introduced in the v0.52 release. It static defined device contains the information that it's able to build dynamic devices as soon the discovery function is called.

  • you must set enableDynamicDeviceBuilder to true when you configure the .enableDiscovery options
  • your discovery function needs to return the dynamically build devices as .device attribute (see dynamicDeviceBuilder example)
  • Make sure the name (neeoapi.buildDevice('THIS-IS-THE-NAME')) of ALL SDK devices of your driver (the initial device and the dynamically build devices) are identical - else your device won't be found.
  • Don't forget to call the .addCapability('dynamicDevice’) to the dynamic build devices.
  • Use the .registerDeviceSubscriptionHandler function:
    • The initializeDeviceList can be used to build the dynamic devices in use as soon as the driver starts.
    • This is important so the NEEO Brain won't miss a state update.
  • Update the discovery function to handle the optional optionalDeviceId - this optionalDeviceId is set when your driver needs to rebuild the dynamic device (for example after a restart of your SDK driver)

Quirks

There are some issues on the SDK we will address in the future. To avoid breaking changes we collect them and bulk fix them in a non breaking way in the future.

  • If you added a sensor with the name "MYSENSOR" and want to update a sensor value, you cannot use the name "MYSENSOR" but you need to add the suffix "_SENSOR" (e.g. "MYSENSOR_SENSOR").

NEEO Macro Names

The view for a device in the recipe is generated automatically depending on the device capabilities.

See widget documentation for more details on the way default views are generated.

Power Control Capability

If your device supports Power control (power on device, power off device), add this capability - the generated recipe will power on and off your device automatically.

You need to add support for the following buttons (addButton({..):

  • POWER ON
  • POWER OFF

or just use the shortcut function .addButtonGroup('POWER')

Volume Control Capability

If your device supports Volume control (volume up and down, optional mute toggle), add this capability - the generated recipe will automatically use the volume capability of your device.

You need to add support for the following buttons (addButton({..):

  • VOLUME UP
  • VOLUME DOWN
  • optionally MUTE TOGGLE

or just use the shortcut function .addButtonGroup('VOLUME')

Favorites View Capability

If you want support for a custom Favorite view, you need to add support for the following buttons (addButton({..):

  • DIGIT 0
  • DIGIT 1
  • DIGIT 2
  • DIGIT 3
  • DIGIT 4
  • DIGIT 5
  • DIGIT 6
  • DIGIT 7
  • DIGIT 8
  • DIGIT 9

or just use the helper function .addButtonGroup('Numpad'). The device must be one of the following types:

  • TV
  • DVB (aka Satellite box, digital receiver)
  • TUNER (audio tuner)

Numpad Capability

If you want to add a numpad widget to your view, make sure to implement all the DIGIT buttons of the "Favorites View Capability". Supported for TVand DVB devices.

Controlpad Capability

To create a Controlpad capability you need to implement the following buttons (addButton({..):

  • CURSOR ENTER
  • CURSOR UP
  • CURSOR DOWN
  • CURSOR LEFT
  • CURSOR RIGHT

or just use the helper function .addButtonGroup('Controlpad'). The devicetype must be TV, DVB, GAMECONSOLE, MEDIAPLAYER, VOD, DVD or PROJECTOR.

Color Buttons Capability

To create a Controlpad capability you need to implement the following buttons (addButton({..):

  • FUNCTION RED
  • FUNCTION GREEN
  • FUNCTION YELLOW
  • FUNCTION BLUE

or just use the helper function .addButtonGroup('Color Buttons'). The devicetype must be TV, DVB, GAMECONSOLE, MEDIAPLAYER or PROJECTOR.

MENU Capability

To create a MENU (navigation) capability you need to implement the following buttons (addButton({..):

  • MENU
  • BACK

or just use the helper function .addButtonGroup('Menu and Back'). In most cases it make sense to include the Controlpad capability aswell.

Channel Zapper Capability

To create a Channel Zapper capability (Channel Up/Down) you need to implement the following buttons (addButton({..):

  • CHANNEL UP
  • CHANNEL DOWN

or just use the helper function .addButtonGroup('Channel Zapper').

Transport Capability

If you want to support different transport features (like skip, forward, next) you can include the following buttons (addButton({..):

  • PLAY, PAUSE, STOP (helper function: .addButtonGroup('Transport'))
  • REVERSE, FORWARD (helper function: .addButtonGroup('Transport Search'))
  • PREVIOUS, NEXT (helper function: .addButtonGroup('Transport Scan'))
  • SKIP SECONDS BACKWARD, SKIP SECONDS FORWARD (helper function: .addButtonGroup('Transport Skip'))

This works for the devices TV, DVB, GAMECONSOLE, MEDIAPLAYER, VOD, DVD or PROJECTOR.

Record Capability

To create a Record capability you need to implement the following buttons (addButton({..):

  • MY RECORDINGS
  • RECORD
  • LIVE

or just use the helper function .addButtonGroup('Record'). The devicetype must be TV, DVB. Please note, if you don't have all 3 buttons you can implement only the buttons your device provides.

Input Capability

If you add support for a devicetype TV, PROJECTOR or AVRECIEVER you should provide discrete input change commands depending of your devices features, for example:

  • INPUT HDMI 1
  • INPUT HDMI 2
  • INPUT VGA 1
  • INPUT SCART 1

Other SDK implementations

Thanks to the contributors for porting the SDK to other platforms.