Page 1 of 1

Indigo support for service Webhooks (using HTTPd plugin)

PostPosted: Tue Feb 12, 2019 3:45 pm
by FlyingDiver
I'm posting this here rather than in the HTTPd Plugin Forum because this is mostly about adding webhook support to other plugins or user scripts.

The HTTPd plugin has been out for a while, and is very useful for simple webhooks such as setting trigger variables from IFTTT. Recently a user asked for support for more complex actions to support the Netatmo's API which uses webhooks to push camera events. After developing a simple implementation to meet that user's needs I decided to create a more complete solution.

First, the authentication for the HTTPd plugin has been made more flexible. The plugin supports no authentication (leave the username/password field blank), Basic Authentication (as before), or Digest Authentication. By default both Digest and Basic are enabled, with Digest preferred. The plugin can be set to require Digest (no Basic authentication). For HTTPS connections, single certificate files (as created for self-signed certificates) and separate certificate/ private key files (as generated by Let's Encrypt) are supported. Some webhook clients may not work with self-signed certificates.

The most significant upgrade is that the plugin now supports POST requests with arbitrary payload data. If the payload content-type is "application/json", then the plugin converts the payload to the Python data structure. Other content-types can be supported, just create an enhancement request on GitHub. Requests using the new "/webhook" action provide the data payload, request URL key-value pairs, and the complete request headers.

Unlike the original "/setvar" command, "/webhook" does not create/set Indigo variables. Instead, the plugin broadcasts the complete data set using Indigo's broadcastToSubscribers() function. The payload of the broadcast message will look something like the following, which is a complex IFTTT recipe hook.

Code: Select all
{
    'request':{
        'path':'/webhook',
        'client':'100.24.12.106',
        'command':'POST',
        'headers':{
            'content-length':'43',
            'x-newrelic-id':'VwAOU1RRGwAFUFZUAwQE',
            'connection':'close',
            'x-newrelic-transaction':'PxRRVVRTAVEBVlgGBgADVkYdUFIOFQZOEgQLBw4NUVcLUF9YBgRXUFQUG0MHUwoLBAcDAxVs',
            'host':'vmp.strangled.net:5566',
            'content-type':'application/json',
            'authorization':'Basic aG9va191c2VyOmhvb2tQYXNzMTAx'
        }
    },
    'payload':{
        u'value3':u'44',
        u'value2':u'55',
        u'value1':u'33'
    },
    'vars':{
        'maker':'true'
    }
}


At this time, I've tested the new functionality with IFTTT, Twilio, and Rachio. I am successfully capturing the webhook push notification from Rachio in a modified version of the Rachio plugin. I will be updating that plugin to use the push notifications instead of polling.

Here's how a plugin that wants to support webhooks does its setup. You must have the HTTPd plugin installed and configured first.

In the startup code for you plugin, register for the webhook messages:
Code: Select all
        httpd_plugin = indigo.server.getPlugin("com.flyingdiver.indigoplugin.httpd")
        self.webhook_info = httpd_plugin.executeAction("getWebhookInfo", deviceId=0, props={u"name": self.pluginId}, waitUntilDone=True)
        self.logger.debug(u"getWebhookInfo: {}".format( self.webhook_info))
        indigo.server.subscribeToBroadcast("com.flyingdiver.indigoplugin.httpd", self.webhook_info["hook_name"], "checkMessagesHook")

The second line gets data from the HTTPd plugin that you need to configure a webhook.

Change "checkMessagesHook" in the last line to the name of your function that will get the broadcast messages. It takes one argument, which is a Python dictionary with the payload data (see above). Example:
Code: Select all
    def checkMessagesHook(self, hookData):
        self.logger.debug(u"checkMessagesHook - hookData: {}".format(hookData))
        self.checkAllMessages()

You'll need to use the info in self.webhook_info to configure the webhook on the service. For Twilio, this is done something like:
Code: Select all
        webhook_url = self.webhook_info.get("http", None)
        number.update(sms_url=webhook_url, sms_method = "GET")

For Rachio, it looks like:
Code: Select all
        webhook_url = self.webhook_info.get("http")
        url = (API_URL + "notification/webhook").format(apiVersion=RACHIO_API_VERSION)
        data = {
                "device" : {"id": dev.pluginProps["id"]},
                "externalId" : dev.id,
                "url" : webhook_url,
                "eventTypes":[{"id": r[u'id']}]           
        }
        self._make_api_call(url, request_method="post", data=data)           


If you set up the webhook manually, you can log the contents of
Code: Select all
 self.webhook_info
and use as needed. It will look something like:

Code: Select all
http://username:password@your.ddns.net:5565/webhook-com.flyingdiver.indigoplugin.twilio


Discussion of this approach is welcome. The new plugin version that supports this functionality is available on the GitHub page as a pre-release. https://github.com/FlyingDiver/Indigo-HTTPd/releases

Re: Indigo support for service Webhooks (using HTTPd plugin)

PostPosted: Tue Feb 12, 2019 4:01 pm
by FlyingDiver
I would really like to make this available to Python scripts as well as other plugins. But broadcastToSubscribers() is only for plugins. So I'm open to suggestions on how to either transmit the data to external scripts that run all the time as well as transient scripts. I suspect I'll have to use Indigo variables for this, unless I do something like HTTPd "client" devices the subscribe to the broadcasts and then make it available to the scripts.

I'm not a fan of proliferating Indigo variable so I'm probably leaning toward the second option. Suggestions welcome.

Re: Indigo support for service Webhooks (using HTTPd plugin)

PostPosted: Tue Feb 12, 2019 4:38 pm
by jay (support)
Cool. Are you completely removing the polling from the Rachio plugin or allowing the user to choose which status update method? I think I'd prefer the latter approach with a default of polling so it works out of the box so to speak.

Re: Indigo support for service Webhooks (using HTTPd plugin)

PostPosted: Tue Feb 12, 2019 4:48 pm
by FlyingDiver
jay (support) wrote:
Cool. Are you completely removing the polling from the Rachio plugin or allowing the user to choose which status update method? I think I'd prefer the latter approach with a default of polling so it works out of the box so to speak.


I'll leave it in. I haven't actually touched the update code yet, been working on subscribing and unsubscribing from the webhooks. And meanwhile browbeating Rachio into adding authentication support. They didn't support any authentication until yesterday. ;)

And while I have a Rachio, it's not installed. So not much useful data from it. :D

Re: Indigo support for service Webhooks (using HTTPd plugin)

PostPosted: Mon May 27, 2019 4:23 pm
by Bollar
Now that we're in mowing season, it would be great if the Rachio plugin could support webhooks. I have a Husqvarna Automower potentially mowing when Rachio might decide to irrigate. Although I can poll up to every 50-ish seconds without throttling, the few seconds gained from getting status from a webhook might save me occasionally mowing down a sprinkler head.

Re: Indigo support for service Webhooks (using HTTPd plugin)

PostPosted: Mon May 27, 2019 5:00 pm
by FlyingDiver
I’ll work on that next week when I get back from Las Vegas.

I’ve got the webhooks working but I’m not parsing the data yet.


Sent from my iPhone using Tapatalk

Re: Indigo support for service Webhooks (using HTTPd plugin)

PostPosted: Wed Jun 05, 2019 3:51 pm
by FlyingDiver