Particle device plugin for Homebridge


#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?


#21

Hi thanks for your reply. I think I sorted this out. I changed to use just {STATE} in the config.json and changed my sketch to convert to in first before I check using my if statement. It seems to work now.


#22

Scotsss don;t know if this is still a problem for you, but I have found the plugin adds a trailing “}” to the args when you use the “{STATE}” keyword.

I suspect this is not a problem for the example firmware because the trailing “}” gets dropped when the arguments are read in.

This line >> sscanf(szArgs, “%d=%d”, &index, &value);

In my config.json I dropped the trailing "} " and it appear to work, although I am unsure why. Suspect this is not intended behavior but it works for the moment.


#23

Great! Homebridge works very stable and reliable now,
Switching lights ON and OFF with Siri has no secrets for me anymore.

But now I’m trying to explore the communication possibilities in the other direction:

Reading sensors

From @krvarma’s sample “config.json” file, I want to make this “device” (or “accessory”?) work:

               {
                    "accessory": "ParticleTemperature",
                    "name": "Room Temperature",
                    "deviceid": "222042000b47348882313031999",
                    "type": "SENSOR",
                    "sensorType": "temperature",
                    "key": "temperature",
                    "event_name": "tvalue"
                }

And I read @krvarma’s explanation:

Based on this, I tried various combinations of Particle.publish commands, such as:

  Particle.publish("tvalue", "key=" + String(temperature,1));


But the temperature value shown in the Apple HOME App does not change: It shows 20° continuously…

Any experience with this somebody?

:wave::older_man:

Edit: I now found the correct syntax:

  Particle.publish("tvalue", "temperature=" + String(temperature,1));

All works now!
Thanks
:hugs:


#24

Strange! I was experiencing the opposite:
The switch function worked quite simply but I couldn’t make the temperature reading work.
See my previous post.

I found now that my syntax for the Particle.publish command was wrong…

Thanks for the help!
:wave::older_man:


#25

any updates on adding a garage door for the particle relay shield ?


#26

I recently forked this code, and have begun adding support for GarageDoorOpener service. In the process, I’m refactoring a bit to use the particle-api-js library, which I think makes some of the Particle cloud API bits more clear.

One minor roadblock I’ve run into is that the current code assumes that there will only be one Characteristic related to a given service, and its value is tracked via the ParticleAccessory::value property. However, the GarageDoorOpener service has three required characteristics: CurrentDoorState, TargetDoorState, and ObstructionDetected. So in my current work-in-progress (which followed the example of the existing SENSOR services), the setting the initial default values and the event handling of state updates winds up stomping all over itself.

So my next step is going to be to create some sort of generic way to register each service’s characteristics, and track each value along with that that characteristic.

Once I have that working, it should lay pretty good groundwork for being able to handle just about any kind of service supported by HomeKit (I hope).

I’ll post updates when I make some more progress. And once I have the GarageDoorOpener bit working, I’ll link to my repo, even if the code is still a bit messy.


#27

After thinking a little more about how to support service types and characteristics in a more general way, I’m realizing that this will probably end up being a pretty heavy rewrite rather than just a refactoring… The way things are now, each new service type that you want to support has to be explicitly added to the code in a few different places. But I feel like it should be possible to handle any service if we can just get the right information mapped between the homebridge platform config and the Particle device functions/vars/events.

I feel like all the pieces are there, we just need to find a way to balance ease-of-configuration with open-endedness. This will probably end up with a slightly more complicated config on the homebridge side, but I think the flexibility will be worth it.

Right now, I think the config might end up looking a little more like this:

{
    "platform": "ParticleServices",
    "name": "Particle Services",
    "access_token": "<<access token>>",
    "devices": [
        {
            "device_id": "<<device id>>",
            "service_key": "GarageDoorOpener",
            "display_name": "Left Garage Door",
            "characteristics": [
                    "Current Door State": {
                        "alias": "cds",
                        "default": 1
                        },
                    "Target Door State": {
                        "alias": "tds",
                        "function": "toggleRelay"
                    }
                ],
            "event_name": "left_g",
        }
    ]
}
  • service_key: This should match a service property name from HAP-NodeJS HomeKitTypes.js (AKA, homebridge.hap.Service). E.g., Lightbulb, Doorbell, HumiditySensor, etc.
  • display_name: The name that will be displayed in the HomeKit app. I’ll probably let this be optional, and create a normalized name from the service_key, if necessary.
  • characteristics: An array of service characteristic info. For read-only characteristics, this will probably be optional, though an alias can be provided.
    • alias: The alias should be useful to provide shorter names, since memory can sometimes be at a premium on microcontrollers.
    • default: Lets you set a default value that a characteristic should have at startup.
    • function: For writable characteristics, the function property specifies the Particle.function() name to call when HomeKit needs to give the device a state update for that characteristic.
  • event_name: This will probably default to a normalized version of display_name, but this property will let you override that. The main reason for an override is the same as for the alias names of characteristics. (maybe I should name it event_alias?)

The Particle device will send events notifying value changes for characteristics. The value of these events will be a string that will probably look like {Characteristic Name}=somevalue, where {Characteristic Name} could be the full name of a characteristic or an alias. E.g., the Left Garage Door device could send a left_g event with value "cds=2" to inform HomeKit that the Current Door State value has changed to 2 (which indicates a “closing” state, according to the HomeKitTypes definitions).

I’m not positive if I’m going to stick with this completely. For example, I’ve debated about whether to use events in both directions (PubSub using Particle.publish() and Particle.subscribe()) rather than using Particle.function() for sending state changes from HomeKit to the device. I’ll just have to see if that would actually make anything easier or not.

Anyways, these are my current thoughts. Feedback welcome.


#28

Just an update for anyone interested. I’ve been working on this off-and-on, here-and-there as time allows. But I’ve been running into a problem where when I trigger my garage door relay function, it works the first time, but the second time, my homebridge plugin throws an error saying that the remote function doesn’t exist (“but you just called it successfully a minute ago, you stupid machine!”). I think this might be due to the node-hap module which underlies homebridge using once() on the callback function when setting a characteristic value. But I haven’t had time to properly narrow that down yet.

On the bright side, I think some of my ideas here for being able to handle arbitrary services defined by HAP, with just a little supporting info in the config file, does appear to be doable, based on what I’ve done so far.

I’ll share more as time allows.


#29

Good news: We have a breakthrough!

See this new thread: LINK

It works beautifully: You compile a sketch and flash it to any Particle device and then it can be paired with Homekit on any iPad, iPhone… Currently, you can only control the Particle’s RGB LED ON/OFF state.