Skip to content

vnovick/smarthome-manager

Repository files navigation

SmartHome Manager

styled with prettier License: MIT PRs Welcome

Travis

Project consolidates several SmartHome devices brands into one manager

Built for Node environment since one of the brands requires to actually run your own server to parse requests

Supported Brands

PhilipsHue

Philips Hue is a high end smart home supplier supplying huge variety of devices from simple dimmable leds to complex sensors etc.

Philips Hue devices use Zigbee protocol to communicate between themselves and the bridge. Bridge is connected to local network. Communication between devices and the bridge is done solely through the bridge by using Philips hue REST API.

SmartHome Manager abstracts this api to be available through async calls on HueController object. for example:

Wemo is home automation branch by Belkin which works over local Wifi by sending SOAP requests from and to device. Wemo also has a Bridge that gives an ability to interact with various devices

Wemo part of smarthome-manager is an abstraction and syntactic sugar over wemo-client library that enables more easier API

MagicHome is a series of Chinese manufactured low end light bulbs that connects to your local wifi through setup process and then is available through tcp socket. library uses under the hood magic-home library and abstrats things on top of it

Setup & Discovery

Before setting everything up it's important to understand which devices you want to consolidate inside a bridge. If you have PhilipsHue and Wemo, you can choose to use smarthome-manager autoDiscovery mode. If you have magicHome too unfortunately you have to find out to which IP it's connected. Read about this in Discovery section

Install

# npm
npm install smarthome-manager --save

#yarn

yarn add smarthome-manager

Bring in smarthome-manager into your project

// in babel transpiled Node Environment or Typescript
import { SmartHomeManager } from 'smarthome-manager'

// in Node without traspilation

const { SmartHomeManager } = require('smarthome-manager')

Configuration

SmartHomeManager Object receives an array of DeviceOptions with Devices type and special options for each device.

❗ Currently Wemo supports only one device

const manager = new SmartHomeManager(DeviceType[], Config)

And the example:

import { SmartHomeManager, Devices } from 'smarthome-manager'


const manager = new SmartHomeManager([{
  type: Devices.philipsHue,
  options: {
    userName: "n3fnnJQzwEqeFtJ1J4cRIo1O98bztFp5R8TT109y",
    ip: "10.0.0.1"
  }
}, {
  type: Devices.wemo,
  options: {
    setupUrl: 'http://10.0.0.10:49154/setup.xml',
  }
}, {
  type: Devices.magicHome,
  options: {
    ip: "10.0.0.3"
  }
}],{
  onReady: (state, controllers) => {
    
  }
})

In this example IPs of all devices are already known. smarthome-manager constructs SmartHomeControllers object available as a second argument in onReady function. Then you are free to access the following controllers and call methods on them:

Discovery

In order to discover Ips of all your devices you need to basically sniff your network for packet data. It can be done by enabling proxy between your mobile phone with controlling app for each device. For MagicHome it's the most complicated process since you cannot use regular tools like CharlesProxy and need to use WireShark to sniff actual TCP packet transfer data.

Below are steps if you use MacOS and have an Iphone. If you are on Windows and use Android you will use different steps that will be added here later on.

  • Create new interface for your device UUID
instruments -s devices
# Known Devices:
# MyPhone(11.2.6) [c93402350920923402342094]
rvictl -s c93402350920923402342094
# Starting device c93402350920923402342094 [SUCCEEDED] with interface rvi0
ifconfig rvi0

Wireshark Image

MagicHome uses port 5577 so in order to sniff packet data and get the actual device Ip we can enable the following filter in Wireshark: tcp.port == 5577 || udp.port === 5577

Wireshark Image

:celebration: Congratulations: You got your MagicHome IP

Philips Hue and Wemo Devices

Both Philips Hue and Wemo Devices are discoverable by sniffing UPNP services in your local network. Philips Hue also has Broker Service you can use to discover Hue Bridge IP

AutoDiscovery

smarthome-manager has autoDiscovery mode for both PhilipsHue and Wemo devices. it uses external SSDP Client that should be passed into SmartHomeManager so basically you can switch the client to the one of your choice.

export type ManagerOptions = {
  onReady?: Function
  SSDPClient?: any
  ssdpOptions?: any
  autoDiscovery?: Boolean
}

Examples and Usage

For convenience all examples are divided by smarthome device and looks as it is written in onReady function after destructuring controllers from second arguments.

//...
onReady: (state, controllers) => {
  const {
    philipsHueController,
    wemoController,
    magicHomeController
  } = controllers
}
//...

PhilipsHue

Philips Hue API is available here

Library abstracts only basic API calls. rest api calls are available by calling hueAuthorizedApiCall or hueRawApiCall methods to access any Philips Hue functionality

export interface IHueController extends Controller {
  generateUser(devicetype: String): Promise<any>
  getHueState(): Promise<any>
  toggleHueState(lightId: number, state: HueStateObject): Promise<any>
  hueAuthorizedApiCall(endpoint: String, options: RequestInit): Promise<any>
  hueRawApiCall(endpoint: String, options: RequestInit): Promise<any>
}

Examples

Generate new user for api (initially there is no user unless it's passed in DeviceOptions) :exclamation: It's important to physically press link button on Hue bridge as mentioned here

    // By using promises

    philipsHueController.generateUser("test-app").then(user => {
      console.log(user)
    })

    //Async await

    const response = await philipsHueController.generateUser("test-app")

Toggle state {on: true }(see HueState for available options) of lightbulb with id=2 for available options

    // By using promises

    philipsHueController.toggleHueState(2, {
      on: true
    }).then(response => {
      console.log(response)
    })


    //Async await

    const response = await 
      philipsHueController.toggleHueState(2, { state: on })

Use for any authorized Philips Hue call. Accepts regular fetch request options for second argument

    // By using promises

    philipsHueController.hueAuthorizedApiCall('groups').then(response => {
      console.log(response)
    })
    

    //Async await

    const response = await philipsHueController.hueAuthorizedApiCall("test-app")

Use for unauthorized Philips Hue call. Accepts regular fetch request options for second argument

    // By using promises

    philipsHueController.hueRawApiCall("username").then(user => {
      console.log(user)
    })

    //Async await

    const response = await philipsHueController.hueRawApiCall("username")

Magic Home

MagicHomeControllers implements the following interface and abstracts usage of magic-home library

export interface IMagicHomeController extends Controller, Control {
  turnOn(): Promise<any>
  turnOff(): Promise<any>
  setColor(red: number, green: number, blue: number): Promise<any>
  setColorWithBrightness(red: number, green: number, blue: number, brightness: number): Promise<any>
  setPattern(pattern: PatternList, speed: number): Promise<any>
  queryState(): Promise<any>
  startEffectMode(): Promise<any>
}

Examples

Turn light Off by accessing raw magic-home library using callbacks

  magicHomeController.control.turnOff(function(err,success){
    console.log(err, success)
  })

Promise abstraction on top

  magicHomeController.turnOn().then(response => {
    console.log(response)
  })

Set Light Color

  magicHomeController.setColor(255,255,255).then((response) => {
    console.log(response)
  })

Set both color and brightness

  magicHomeController.setColorWithBrightness(255,255,255, 100).then((response) => {
    console.log(response)
  })

Set pattern out of PatternList

  magicHomeController.setPattern(PatternList.blue_gradual_change, 100).then(response => {
    console.log(response)
  })

Pattern List you can use:

export enum PatternList {
  seven_color_cross_fade = 'seven_color_cross_fade',
  red_gradual_change = 'red_gradual_change',
  green_gradual_change = 'green_gradual_change',
  blue_gradual_change = 'blue_gradual_change',
  yellow_gradual_change = 'yellow_gradual_change',
  cyan_gradual_change = 'cyan_gradual_change',
  purple_gradual_change = 'purple_gradual_change',
  white_gradual_change = 'white_gradual_change',
  red_green_cross_fade = 'red_green_cross_fade',
  red_blue_cross_fade = 'red_blue_cross_fade',
  green_blue_cross_fade = 'green_blue_cross_fade',
  seven_color_strobe_flash = 'seven_color_strobe_flash',
  red_strobe_flash = 'red_strobe_flash',
  green_strobe_flash = 'green_strobe_flash',
  blue_stobe_flash = 'blue_stobe_flash',
  yellow_strobe_flash = 'yellow_strobe_flash',
  cyan_strobe_flash = 'cyan_strobe_flash',
  purple_strobe_flash = 'purple_strobe_flash',
  white_strobe_flash = 'white_strobe_flash',
  seven_color_jumping = 'seven_color_jumping'
}

Query light state

  magicHomeController.queryState().then(response => {
    console.log(response)
  })

Wemo

library use under the hoode wemo-client library

export interface IWemoController extends Controller {
  connect(): Promise<any>
  getEndDevices(): Promise<any>
  getBrightness(): Promise<any>
  setBrightness(brightness: number): Promise<any>
  getAttributes(): Promise<any>
  getDeviceStatus(deviceId: String): Promise<any>
  setDeviceStatus(deviceId: String, capability: String, value: String): Promise<any>
  setLightColor(deviceId: String, red: String, green: String, blue: String): Promise<any>
  getInsightParams(): Promise<any>
  setAttributes(attributes: Object): Promise<any>
  getBinaryState(client: any): Promise<any>
}

Examples

Connect and set binary state

  wemoController.connect().then(result => {
      console.log(result)
      wemoController.setBinaryState(0)
    }
  )

__

Subscriptions

All controllers inherit from StateFull class and hence have subscription behavior

subscribeToState
removeStateListener
    // Subscribe to state
    const listenerId = wemoController.subscribeToState((binaryState) => {
      console.log(binaryState)
    }, 'binaryState')

    //Remove Listener
    wemoController.removeStateListener(listenerId)

❗ Subscriptions for all state changes are not yet finished

RoadMap

  • Finish subscriptions for all state changes
  • Add full test Coverage
  • Perform physical tests on all available Philips Hue, Wemo and Magic Home available devices
  • Wrap additional Philips Hue Apis
  • Add GraphQL Helpers to simplify building GraphQL server
  • Improve Documentation

Contributing

Contributing is really welcomed since I don't have all smarthome devices to physically test. Also library lacks basic tests so tests are really welcomed.

Library is based on the package typescript-library-starter so all testing, coverage reporting guidelines are listed there.

Read more

Resources

Presentation at ReactAmsterdam:

Credits

Made with ❤️ by @vnovick

This project follows the all-contributors specification. Contributions of any kind are welcome!

About

SmartHome manager for various smart appliances brands

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published