Particle device plugin for Homebridge


#1

As you all know in the new version of Homebridge, the plugin architecture is changed. In new Homebridge, plugins are published through NPM with name starts with homebridge-. Users can install the plugin using NPM.

My previous example of Particle and Homebridge uses old plugin architecture. I have been thinking for a long time to upgrade my previous plugin to the new architecture. But because of many reasons it is delayed. Luckily last week I was able to complete and publish to NPM.

You can install it using NPM like all other modules, you can install using:

npm install -g homebridge-particle.

In this version, I have made some changes from the older version. Mainly the plugin is now a Homebridge Platform. Also in this version accessories are defined in config.json file. The plugin loads the accessories from the config.json file and create accessory dynamically. A sample configuration file is like:

{
    "bridge": {
        "name": "Homebridge",
        "username": "CC:22:3D:E3:CE:39",
        "port": 51826,
        "pin": "031-45-154"
    },
    
    "description": "This is an example configuration file with one Particle platform and 3 accessories, two lights and a temperature sensor. You should replace the access token and device id placeholder with your access token and device id",

    "platforms": [
        {
            "platform": "Particle",
            "name": "Particle Devices",
			"access_token": "<<access token>>",
			"cloudurl": "https://api.spark.io/v1/devices/",
			"devices": [
				{
					"accessory": "BedroomLight",
					"name": "Bedroom Light",
				    "deviceid": "<<device id>>",
					"type": "LIGHT",
					"function_name": "onoff",
					"args": "0={STATE}"
				},
				{
					"accessory": "KitchenLight",
					"name": "Kitchen Light",
					"deviceid": "<<device id>>",
					"type": "LIGHT",
					"function_name": "onoff",
					"args": "1={STATE}"
				},
				{
					"accessory": "KitchenTemperature",
					"name": "Kitchen Temperature",
					"deviceid": "<<device id>>",
					"type": "SENSOR",
					"sensorType": "temperature",
					"key": "temperature",
					"event_name": "tvalue"
				}
			]
        }
    ]
}

A sample firmware to test this config.json file is:

#define MAX_ARGS 64

int lightPins[] = {
    A5,A4
};

int onoffLight(String args){
    int index = args.toInt();
    int value;
    char szArgs[MAX_ARGS];
    int count = sizeof(lightPins) / sizeof(int);
    
    args.toCharArray(szArgs, MAX_ARGS);
    
    sscanf(szArgs, "%d=%d", &index, &value);
    
    Serial.println();
    Serial.print("On/Off");
    Serial.println();
    Serial.print("Arguments: ");
    Serial.print(args);
    Serial.println();
    Serial.print("Index: ");
    Serial.print(index);
    Serial.println();
    Serial.print("Value: ");
    Serial.print(value);
    Serial.println();
    
    if(index >= 0 && index<count){
        analogWrite(lightPins[index], value == 1 ? 255 : 0);
    }
    
    return 0;
}

void setup() {
    Serial.begin(115200);
    
    int count = sizeof(lightPins) / sizeof(int);
    
    for(int index=0; index<count; ++index){
        pinMode(lightPins[index], OUTPUT);
    }
    
    Particle.function("onoff", onoffLight);
}

void loop() {

}

As you can see from the above example this config.json file defines 3 accessories. 2 Lights and one Temperature Sensor. The access_token defines the Particle Access Token and cloudurl defines the base Particle API url. If you are using the Particle Cloud, then the value of cloudurl should be https://api.spark.io/v1/devices/. If you are using local cloud, then replace with your sensor address.

The devices array contains all the accessories. You can see the accessory object defines following string objects:

  • accessory - Accessory name, this is the name of the accessory.
  • name - Display name, this is the name to be displayed on the HomeKit app.
  • deviceid - Device ID of the Particle Device (Core, Photon or Electron). It is defined in accessory so that you can use different Particle Devices for different accessory.
  • type - Type of the accessoy. As of now, the plugin supports 2 type, LIGHT and SENSOR. Type LIGHT represents a light, such as bedroom light, kitchen light, living room light, etc… Type SENSOR represents sensor accessory such as Temperature sensor, Humidity sensor, Light sensor, etc…
  • sensorType - Optional Sensor Type, this string object is optional. This is only valid when the accessory type is SENSOR. As of now the plugin supports 3 types of sensors, Temperature Sensor, Humidity Sensor and Light Sensor. More sensor will be supports in future versions.
  • function_name - name of the Particle Function declared in the firmware. This function will be called when the light is turned on/off with the arguments as per the args property.
  • args - defines the argument to be passed to the Particle Function defined by the function_name. Whatever is declared here will be passed to the Particle Function. You can define a special token {STATE} in the argument. This token will be replaced by 1 or 0 depending on whether user turns on or off the light. In this example the Particle Function is named as “onoff” and the argument is defined as ***0={STATE}***. So when the user turns on the light by saying “Hey Siri, turn on bedroom light”, the Particle plugin will call the function onoff with argument 0=1. In case the user turns off the light by saying Hey Siri, turn off bedroom light, the onoff function will be called with argument 0=0. A sample firmware to test this config is given above.
  • event_name - The name of the event to listen for sensor value update. This is only valid if the accessory type is SENSOR. If the accessory is a type of SENSOR, then the plugin listens for events published from Particle Device (using Particle.publish). The device firmware should publish the sensor values in the format key=value. The key identifies the sensor value. For a temperature sensor the key should be temperature. For a humidity sensor the key should be humidity. For light sensor it should be light.
  • key - Name of the key, this is not used in this version of the plugin. This is included for future purpose.

Mac OS sierra Siri / Windows 10 Cortana on Particle core/photon
Homebridge troubleshooting
#2

Wow good work. Thanks


#3

How does the args function work? What does the 0={STATE} or 1={STATE} mean? Is this the arguments that get passed to the onoff cloud function. And if it is can you maybe post an example of the onoff cloud function?

Thanks @krvarma


#4

@johandk, I have modified the article to clear your points. Basically args defines the argument passed to the Particle Function defined in the firmware. The token {STATE} is a special token you can define in the args and this token will be replaced with 1 or 0 depending on whether the user turns on/off the light. The {STATE} will be 1 when the use turns on the light and 0 when turn off. I have updated the article with a sample firmware to test the sample config.json file given.

Hope this makes sense.


#5

Thanks this clarifies it


#6

Hi @krvarma

Have you made any progress in adding the Garage Accessory?


#7

I didn’t work on the these days, I have completed some more accessories. I should restart it this weekend.


#8

This is awesome. While I’m enjoying connecting multiple pieces of software I’m stuck - I’m not seeing my accessories populate the Eve iphone app but I do see my Homebridge server.

So far I have:

  1. ran “sudo npm install -g homebridge” in terminal on my imac.
  2. ran “sudo npm install -g homebridge-particle” (i believe this is the particle plugin)
  3. changed the config.json file inside “usr/local/lib/node_modules/homebridge-particle” with my particle access token and photon device id
  4. flashed my photon with the code above
  5. ran “homebridge” in terminal (it provided a pin)
  6. downloaded “Eve” and entered the pin from terminal running homebridge
    Now the accessories are not there.

Maybe I’m not editing the correct json file. I notice there is a config.json in:

  1. user/local/lib/config.json
  2. user/local/lib//node_modules/homebridge/config-sample.json
  3. user/local/lib//node_modules/homebridge-particle/config-sample.json

I have been editing number 3 but stubbing in my access token and device id.

    "bridge": {
        "name": "Homebridge",
        "username": "CC:22:3D:E3:CE:39",
        "port": 51826,
        "pin": "031-45-154"
    },
    
    "description": "This is an example configuration file with one Particle platform. It contians 3 accessories, two lights and a temperature sensor. You should replace the access token and device id placeholder with your access token and device id",

    "platforms": [
        {
            "platform": "Particle",
            "name": "Particle Devices",
            "access_token": "<<access token>>",
            "cloudurl": "https://api.spark.io/v1/devices/",
            "devices": [
                {
                    "accessory": "ParticleLight",
                    "name": "Bedroom Light",
                    "deviceid": "<<device id>>",
                    "type": "LIGHT",
                    "function_name": "onoff",
                    "args": "0={STATE}"
                },
                {
                    "accessory": "ParticleLight",
                    "name": "Kitchen Light",
                    "deviceid": "<<device id>>",
                    "type": "LIGHT",
                    "function_name": "onoff",
                    "args": "1={STATE}"
                },
                {
                    "accessory": "ParticleTemperature",
                    "name": "Kitchen Temperature",
                    "deviceid": "<<device id>>",
                    "type": "SENSOR",
                    "sensorType": "temperature",
                    "key": "temperature",
                    "event_name": "tvalue"
                }
            ]
        }
    ]
}


#9

I have noticed that when the homebridge server is started, I can see:

imac:~ jaysettle$ homebridge
[7/7/2016, 11:08:19 PM] Loaded plugin: homebridge-particle
[7/7/2016, 11:08:19 PM] Registering platform 'homebridge-particle.Particle'
[7/7/2016, 11:08:19 PM] ---
Scan this code with your HomeKit App on your iOS device to pair with Homebridge:
                       
    ┌────────────┐     
    │ 031-45-154 │     
    └────────────┘     
                       
[7/7/2016, 11:08:19 PM] Homebridge is running on port 51826.

Looking at another post (https://github.com/nfarina/homebridge) I see that terminal is supposed to show that the config.json has been loaded, I don’t receive this information on my setup and not sure why.

$ homebridge
Loaded plugin: homebridge-lockitron
Registering accessory 'Lockitron'
---
Loaded config.json with 1 accessories and 0 platforms.
---
Loading 0 platforms...
Loading 1 accessories...
[Back Door] Initializing Lockitron accessory...


#10

@jaysettle, did you change the device id and access token placeholder with you actual ones. From log it seems no platform accessory is loaded. It should display 3 accessories loaded.


#11

I did. I’ve also deleted the config.json file completely and upon starting homebridge, terminal does not complain it’s not there. I believe it should say:

$ homebridge
Couldn’t find a config.json file

The config.json file I’ve created does need to sit inside the homebridge folder, correct?


#12

Sorry for late reply. Yes it should be in the homebridge folder.


#13

Still not seeing the accessories.


#14

I think I figured it out. This is my fault. I didn’t have a good understanding of the mac file structure to recognize that

“Create and edit the Homebridge configuration file ~/.homebridge/config.json”

meant that there was another directory other than /usr/local/lib/node_modules/homebridge where the config.json should end up. I know now that the config.json file should end up in the user’s home directory.


#15

I’m probably missing something, but is there a reason why you need to include “temperature=” in the actual data published for a sensor when it’s in the event name already? Aside from making it a bit hard to throw in the actual value via a float (it doesn’t like String alongside fixed text etc.), it also makes doing other things with the data a bit harder because I need to yank the fixed text from it.


#16

@hopkapi, it is because the Particle Plugin for Homebridge expects the events published in the format key=value. The plugin splits the event data and look for predefined sensors. Look at the line https://github.com/krvarma/homebridge-particle/blob/master/index.js#L169.

I agree that this can be less complicated and it will be changed for sure in an updated version.


#17

I did eventually figure that out, though was mostly just confused as to why it worked that way. Pleased to hear it’ll be changed :).


#18

@krvarma Thanks for your great post! I got homebridge running with your example config.json and photon firmware files. All three accessories show up in the iOS 10 Home app and both light switches are working as expected.

However, when I try to publish a temperature value the “20” in the iOS app isn’t updating. The data shows up on the particle dashboard so maybe something is wrong with my formatting:
Particle.publish(“tvalue”,“temperature=23”,60,PRIVATE);

Thanks for your help!

Edit: No idea why but it it works now :sweat_smile:


#19

Hi,
I have Homebridge setup and successfully reading a temperature value from my Photon through homebridge on my phone. Works great!!

I am trying to get the Switch Function working so I can turn on my AC Unit.

I have cloud function on my Photon called" ACStatus" The firmware is all working well and the function can be called with a single 0 or 1 to turn off/on the switch, I have also added the option to have 1=0 as per the example. I have tested this through Particle Dev and it all works fine.

HomeBridge detects the Accessory and it successfully displayed on my iphone as a switch. My config.json is the following

"platform": "Particle",
            "name": "Particle Devices",
            "access_token": "XXXXXXXXXXXXXXXXX", //omitted for privacy
            "cloudurl": "https://api.spark.io/v1/devices/",
            "devices": [

                {
                    "accessory": "Airconditioner",
                    "name": "Air Conditioner",
                    "deviceid": "XXXXXXX",   //omitted for privacy
                    "type": "LIGHT",
                    "function_name": "setACStatus",
                    "args": "0={STATE}"
                },

I have tried this with just {STATE} and as above but no differrence in the result. The function in my Photon firmware is

int controlAC (String command) {
//function called by Cloud Function to turn the AC on or OFF
  if (command=="on" || command=="1=1" || command == "1") {
          turnOnAirCon();
          return 1;
      }
      else if (command=="off" || command=="1=0" || command =="0") {
          turnOffAirCon ();
          return 0;
      }

}

When I select the Switch button on my iphone the following is shown on my screen running homebridge. and the Photon function is not called and the relay is not energised. I have omitted Photon device ID from post.

What am I doing wrong here? Thanks in advance

[12/20/2016, 10:26:22 PM] [Particle Devices] Getting current state...
[12/20/2016, 10:26:22 PM] [Particle Devices] URL: https://api.spark.io/v1/devices/
[12/20/2016, 10:26:22 PM] [Particle Devices] Device ID: xxxxxxxxxxx
[12/20/2016, 10:26:22 PM] [Particle Devices] Calling function: https://api.spark.io/v1/devices/xxxxxxxxxx/setACStatus
IncomingMessage {
  _readableState: 
   ReadableState {
     objectMode: false,
     highWaterMark: 16384,
     buffer: BufferList { head: null, tail: null, length: 0 },
     length: 0,
     pipes: null,
     pipesCount: 0,
     flowing: true,
     ended: true,
     endEmitted: true,
     reading: false,
     sync: true,
     needReadable: false,
     emittedReadable: false,
     readableListening: false,
     resumeScheduled: false,
     defaultEncoding: 'utf8',
     ranOut: false,
     awaitDrain: 0,
     readingMore: false,
     decoder: null,
     encoding: null },
  readable: false,
  domain: null,
  _events: 
   { end: [ [Function: responseOnEnd], [Function], [Function], [Function] ],
     close: [ [Function], [Function] ],
     data: [Function],
     error: [Function] },
  _eventsCount: 4,
  _maxListeners: undefined,
  socket: 
   TLSSocket {
     _tlsOptions: 
      { pipe: null,
        secureContext: [Object],
        isServer: false,
        requestCert: true,
        rejectUnauthorized: true,
        session: <Buffer 30 82 06 f6 02 01 01 02 02 03 03 04 02 c0 2f 04 20 ba cb 52 c4 4f 69 b6 43 19 4d e1 da fd ee 8f fa 4b 56 06 61 32 fb 83 94 c1 d5 54 ff 3d e6 a1 92 04 ... >,
        NPNProtocols: undefined,
        ALPNProtocols: undefined,
        requestOCSP: undefined },
     _secureEstablished: true,
     _securePending: false,
     _newSessionPending: false,
     _controlReleased: true,
     _SNICallback: null,
     servername: null,
     npnProtocol: undefined,
     alpnProtocol: false,
     authorized: true,
     authorizationError: null,
     encrypted: true,
     _events: 
      { close: [Object],
        end: [Object],
        finish: [Function: onSocketFinish],
        _socketEnd: [Function: onSocketEnd],
        secure: [Function],
        free: [Function: onFree],
        agentRemove: [Function: onRemove],
        drain: [Function: ondrain],
        error: [Function: socketErrorListener] },
     _eventsCount: 9,
     connecting: false,
     _hadError: false,
     _handle: null,
     _parent: null,
     _host: 'api.spark.io',
     _readableState: 
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [Object],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: false,
     domain: null,
     _maxListeners: undefined,
     _writableState: 
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        needDrain: false,
        ending: true,
        ended: true,
        finished: true,
        decodeStrings: false,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 0,
        prefinished: true,
        errorEmitted: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: false,
     allowHalfOpen: false,
     destroyed: true,
     _bytesDispatched: 237,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server: undefined,
     _server: null,
     ssl: null,
     _requestCert: true,
     _rejectUnauthorized: true,
     parser: null,
     _httpMessage: 
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 5,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: true,
        upgrading: false,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedHeader: [Object],
        _contentLength: null,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Circular],
        connection: [Circular],
        _header: 'POST /v1/devices/3xxxxxxxxxx/setACStatus HTTP/1.1\r\nhost: api.spark.io\r\ncontent-type: application/x-www-form-urlencoded\r\ncontent-length: 63\r\nConnection: close\r\n\r\n',
        _headers: [Object],
        _headerNames: [Object],
        _onPendingData: null,
        agent: [Object],
        socketPath: undefined,
        timeout: undefined,
        method: 'POST',
        path: '/v1/devices/xxxxxxxxxxx/setACStatus',
        _ended: true,
        parser: null,
        res: [Circular] },
     read: [Function],
     _consuming: true,
     _idleNext: null,
     _idlePrev: null,
     _idleTimeout: -1 },
  connection: 
   TLSSocket {
     _tlsOptions: 
      { pipe: null,
        secureContext: [Object],
        isServer: false,
        requestCert: true,
        rejectUnauthorized: true,
        session: <Buffer 30 82 06 f6 02 01 01 02 02 03 03 04 02 c0 2f 04 20 ba cb 52 c4 4f 69 b6 43 19 4d e1 da fd ee 8f fa 4b 56 06 61 32 fb 83 94 c1 d5 54 ff 3d e6 a1 92 04 ... >,
        NPNProtocols: undefined,
        ALPNProtocols: undefined,
        requestOCSP: undefined },
     _secureEstablished: true,
     _securePending: false,
     _newSessionPending: false,
     _controlReleased: true,
     _SNICallback: null,
     servername: null,
     npnProtocol: undefined,
     alpnProtocol: false,
     authorized: true,
     authorizationError: null,
     encrypted: true,
     _events: 
      { close: [Object],
        end: [Object],
        finish: [Function: onSocketFinish],
        _socketEnd: [Function: onSocketEnd],
        secure: [Function],
        free: [Function: onFree],
        agentRemove: [Function: onRemove],
        drain: [Function: ondrain],
        error: [Function: socketErrorListener] },
     _eventsCount: 9,
     connecting: false,
     _hadError: false,
     _handle: null,
     _parent: null,
     _host: 'api.spark.io',
     _readableState: 
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [Object],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: false,
     domain: null,
     _maxListeners: undefined,
     _writableState: 
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        needDrain: false,
        ending: true,
        ended: true,
        finished: true,
        decodeStrings: false,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 0,
        prefinished: true,
        errorEmitted: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: false,
     allowHalfOpen: false,
     destroyed: true,
     _bytesDispatched: 237,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server: undefined,
     _server: null,
     ssl: null,
     _requestCert: true,
     _rejectUnauthorized: true,
     parser: null,
     _httpMessage: 
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 5,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: true,
        upgrading: false,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedHeader: [Object],
        _contentLength: null,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Circular],
        connection: [Circular],
        _header: 'POST /v1/devices/xxxxxxxxxxxx/setACStatus HTTP/1.1\r\nhost: api.spark.io\r\ncontent-type: application/x-www-form-urlencoded\r\ncontent-length: 63\r\nConnection: close\r\n\r\n',
        _headers: [Object],
        _headerNames: [Object],
        _onPendingData: null,
        agent: [Object],
        socketPath: undefined,
        timeout: undefined,
        method: 'POST',
        path: '/v1/devices/xxxxxxxxxxxx/setACStatus',
        _ended: true,
        parser: null,
        res: [Circular] },
     read: [Function],
     _consuming: true,
     _idleNext: null,
     _idlePrev: null,
     _idleTimeout: -1 },
  httpVersionMajor: 1,
  httpVersionMinor: 1,
  httpVersion: '1.1',
  complete: true,
  headers: 
   { server: 'nginx',
     date: 'Tue, 20 Dec 2016 11:26:25 GMT',
     'content-type': 'application/json; charset=utf-8',
     'content-length': '98',
     connection: 'close',
     'access-control-allow-origin': '*' },
  rawHeaders: 
   [ 'Server',
     'nginx',
     'Date',
     'Tue, 20 Dec 2016 11:26:25 GMT',
     'Content-Type',
     'application/json; charset=utf-8',
     'Content-Length',
     '98',
     'Connection',
     'close',
     'Access-Control-Allow-Origin',
     '*' ],
  trailers: {},
  rawTrailers: [],
  upgrade: false,
  url: '',
  method: null,
  statusCode: 200,
  statusMessage: 'OK',
  client: 
   TLSSocket {
     _tlsOptions: 
      { pipe: null,
        secureContext: [Object],
        isServer: false,
        requestCert: true,
        rejectUnauthorized: true,
        session: <Buffer 30 82 06 f6 02 01 01 02 02 03 03 04 02 c0 2f 04 20 ba cb 52 c4 4f 69 b6 43 19 4d e1 da fd ee 8f fa 4b 56 06 61 32 fb 83 94 c1 d5 54 ff 3d e6 a1 92 04 ... >,
        NPNProtocols: undefined,
        ALPNProtocols: undefined,
        requestOCSP: undefined },
     _secureEstablished: true,
     _securePending: false,
     _newSessionPending: false,
     _controlReleased: true,
     _SNICallback: null,
     servername: null,
     npnProtocol: undefined,
     alpnProtocol: false,
     authorized: true,
     authorizationError: null,
     encrypted: true,
     _events: 
      { close: [Object],
        end: [Object],
        finish: [Function: onSocketFinish],
        _socketEnd: [Function: onSocketEnd],
        secure: [Function],
        free: [Function: onFree],
        agentRemove: [Function: onRemove],
        drain: [Function: ondrain],
        error: [Function: socketErrorListener] },
     _eventsCount: 9,
     connecting: false,
     _hadError: false,
     _handle: null,
     _parent: null,
     _host: 'api.spark.io',
     _readableState: 
      ReadableState {
        objectMode: false,
        highWaterMark: 16384,
        buffer: [Object],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: false,
        endEmitted: false,
        reading: true,
        sync: false,
        needReadable: true,
        emittedReadable: false,
        readableListening: false,
        resumeScheduled: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null },
     readable: false,
     domain: null,
     _maxListeners: undefined,
     _writableState: 
      WritableState {
        objectMode: false,
        highWaterMark: 16384,
        needDrain: false,
        ending: true,
        ended: true,
        finished: true,
        decodeStrings: false,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        corked: 0,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        bufferedRequest: null,
        lastBufferedRequest: null,
        pendingcb: 0,
        prefinished: true,
        errorEmitted: false,
        bufferedRequestCount: 0,
        corkedRequestsFree: [Object] },
     writable: false,
     allowHalfOpen: false,
     destroyed: true,
     _bytesDispatched: 237,
     _sockname: null,
     _pendingData: null,
     _pendingEncoding: '',
     server: undefined,
     _server: null,
     ssl: null,
     _requestCert: true,
     _rejectUnauthorized: true,
     parser: null,
     _httpMessage: 
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 5,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: true,
        upgrading: false,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedHeader: [Object],
        _contentLength: null,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Circular],
        connection: [Circular],
        _header: 'POST /v1/devices/xxxxxxxxxxxx/setACStatus HTTP/1.1\r\nhost: api.spark.io\r\ncontent-type: application/x-www-form-urlencoded\r\ncontent-length: 63\r\nConnection: close\r\n\r\n',
        _headers: [Object],
        _headerNames: [Object],
        _onPendingData: null,
        agent: [Object],
        socketPath: undefined,
        timeout: undefined,
        method: 'POST',
        path: '/v1/devices/xxxxxxxxxxxx/setACStatus',
        _ended: true,
        parser: null,
        res: [Circular] },
     read: [Function],
     _consuming: true,
     _idleNext: null,
     _idlePrev: null,
     _idleTimeout: -1 },
  _consuming: true,
  _dumped: false,
  req: 
   ClientRequest {
     domain: null,
     _events: 
      { socket: [Object],
        response: [Function: bound ],
        error: [Function: bound ],
        drain: [Function],
        prefinish: [Function: requestOnPrefinish] },
     _eventsCount: 5,
     _maxListeners: undefined,
     output: [],
     outputEncodings: [],
     outputCallbacks: [],
     outputSize: 0,
     writable: true,
     _last: true,
     upgrading: false,
     chunkedEncoding: false,
     shouldKeepAlive: false,
     useChunkedEncodingByDefault: true,
     sendDate: false,
     _removedHeader: { 'content-length': false },
     _contentLength: null,
     _hasBody: true,
     _trailer: '',
     finished: true,
     _headerSent: true,
     socket: 
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: null,
        npnProtocol: undefined,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 9,
        connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'api.spark.io',
        _readableState: [Object],
        readable: false,
        domain: null,
        _maxListeners: undefined,
        _writableState: [Object],
        writable: false,
        allowHalfOpen: false,
        destroyed: true,
        _bytesDispatched: 237,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: [Circular],
        read: [Function],
        _consuming: true,
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1 },
     connection: 
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: null,
        npnProtocol: undefined,
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 9,
        connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'api.spark.io',
        _readableState: [Object],
        readable: false,
        domain: null,
        _maxListeners: undefined,
        _writableState: [Object],
        writable: false,
        allowHalfOpen: false,
        destroyed: true,
        _bytesDispatched: 237,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: [Circular],
        read: [Function],
        _consuming: true,
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1 },
     _header: 'POST /v1/devices/xxxxxxxxxxxx/setACStatus HTTP/1.1\r\nhost: api.spark.io\r\ncontent-type: application/x-www-form-urlencoded\r\ncontent-length: 63\r\nConnection: close\r\n\r\n',
     _headers: 
      { host: 'api.spark.io',
        'content-type': 'application/x-www-form-urlencoded',
        'content-length': 63 },
     _headerNames: 
      { host: 'host',
        'content-type': 'content-type',
        'content-length': 'content-length' },
     _onPendingData: null,
     agent: 
      Agent {
        domain: null,
        _events: [Object],
        _eventsCount: 1,
        _maxListeners: undefined,
        defaultPort: 443,
        protocol: 'https:',
        options: [Object],
        requests: {},
        sockets: [Object],
        freeSockets: {},
        keepAliveMsecs: 1000,
        keepAlive: false,
        maxSockets: Infinity,
        maxFreeSockets: 256,
        maxCachedSessions: 100,
        _sessionCache: [Object] },
     socketPath: undefined,
     timeout: undefined,
     method: 'POST',
     path: '/v1/devices/xxxxxxxxxxxx/setACStatus',
     _ended: true,
     parser: null,
     res: [Circular] },
  request: 
   Request {
     domain: null,
     _events: 
      { error: [Function: bound ],
        complete: [Function: bound ],
        pipe: [Function],
        data: [Function],
        end: [Function] },
     _eventsCount: 5,
     _maxListeners: undefined,
     uri: 
      Url {
        protocol: 'https:',
        slashes: true,
        auth: null,
        host: 'api.spark.io',
        port: 443,
        hostname: 'api.spark.io',
        hash: null,
        search: null,
        query: null,
        pathname: '/v1/devices/xxxxxxxxxxxx/setACStatus',
        path: '/v1/devices/xxxxxxxxxxxx/setACStatus',
        href: 'https://api.spark.io/v1/devices/xxxxxxxxxxxx/setACStatus' },
     callback: [Function],
     method: 'POST',
     readable: true,
     writable: true,
     explicitMethod: true,
     _qs: 
      Querystring {
        request: [Circular],
        lib: [Object],
        useQuerystring: undefined,
        parseOptions: {},
        stringifyOptions: {} },
     _auth: 
      Auth {
        request: [Circular],
        hasAuth: false,
        sentAuth: false,
        bearerToken: null,
        user: null,
        pass: null },
     _oauth: OAuth { request: [Circular], params: null },
     _multipart: 
      Multipart {
        request: [Circular],
        boundary: 'db0c9677-6dbb-4cd0-8684-f78ff6545cf5',
        chunked: false,
        body: null },
     _redirect: 
      Redirect {
        request: [Circular],
        followRedirect: true,
        followRedirects: true,
        followAllRedirects: false,
        allowRedirect: [Function],
        maxRedirects: 10,
        redirects: [],
        redirectsFollowed: 0,
        removeRefererHeader: false },
     _tunnel: 
      Tunnel {
        request: [Circular],
        proxyHeaderWhiteList: [Object],
        proxyHeaderExclusiveList: [] },
     headers: 
      { 'content-type': 'application/x-www-form-urlencoded',
        'content-length': 63 },
     setHeader: [Function],
     hasHeader: [Function],
     getHeader: [Function],
     removeHeader: [Function],
     localAddress: undefined,
     pool: {},
     dests: [],
     __isRequestRequest: true,
     _callback: [Function],
     proxy: null,
     tunnel: true,
     setHost: true,
     originalCookieHeader: undefined,
     _disableCookies: true,
     _jar: undefined,
     port: 443,
     host: 'api.spark.io',
     body: 'access_token=xxxxxxxxxxxx&args=1%7D',
     path: '/v1/devices/xxxxxxxxxxxx7/setACStatus',
     httpModule: 
      { Server: [Object],
        createServer: [Function],
        globalAgent: [Object],
        Agent: [Object],
        request: [Function],
        get: [Function] },
     agentClass: { [Function: Agent] super_: [Object] },
     agent: 
      Agent {
        domain: null,
        _events: [Object],
        _eventsCount: 1,
        _maxListeners: undefined,
        defaultPort: 443,
        protocol: 'https:',
        options: [Object],
        requests: {},
        sockets: [Object],
        freeSockets: {},
        keepAliveMsecs: 1000,
        keepAlive: false,
        maxSockets: Infinity,
        maxFreeSockets: 256,
        maxCachedSessions: 100,
        _sessionCache: [Object] },
     _started: true,
     href: 'https://api.spark.io/v1/devices/xxxxxxxxxxxx/setACStatus',
     req: 
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 5,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: true,
        _last: true,
        upgrading: false,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedHeader: [Object],
        _contentLength: null,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Object],
        connection: [Object],
        _header: 'POST /v1/devices/xxxxxxxxxxxx/setACStatus HTTP/1.1\r\nhost: api.spark.io\r\ncontent-type: application/x-www-form-urlencoded\r\ncontent-length: 63\r\nConnection: close\r\n\r\n',
        _headers: [Object],
        _headerNames: [Object],
        _onPendingData: null,
        agent: [Object],
        socketPath: undefined,
        timeout: undefined,
        method: 'POST',
        path: '/v1/devices/xxxxxxxxxxxx/setACStatus',
        _ended: true,
        parser: null,
        res: [Circular] },
     ntick: true,
     response: [Circular],
     originalHost: 'api.spark.io',
     originalHostHeaderName: 'host',
     responseContent: [Circular],
     _destdata: true,
     _ended: true,
     _callbackCalled: true },
  toJSON: [Function: responseToJSON],
  caseless: 
   Caseless {
     dict: 
      { server: 'nginx',
        date: 'Tue, 20 Dec 2016 11:26:25 GMT',
        'content-type': 'application/json; charset=utf-8',
        'content-length': '98',
        connection: 'close',
        'access-control-allow-origin': '*' } },
  read: [Function],
  body: '{\n  "id": "xxxxxxxxxxxx",\n  "last_app": "",\n  "connected": true,\n  "return_value": 0\n}' }

#20

@scottsss, I am really sorry for the late reply. I was busy with some important assignment and I didn’t check the forum last week.

From the above code I can see that you are checking “1=1” or “1=0” to turn on/off the AC. But you argument definition is “0={STATE}”, this will pass to your function as “0=1” or “0=0”. Could this be the problem? Also can you put some Serial log to find what is the parameter coming?