Differences
This shows you the differences between two versions of the page.
| developer_apis:plugin_store_api [2018/02/20 23:59] – created jay | developer_apis:plugin_store_api [2026/04/07 18:27] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Plugin Store API ====== | ||
| + | With the release of the Plugin Store, we soft-launched an API that would allow an external source to get the details of the latest release for the specified plugin. We really hadn't intended to publish it because it is our intention to deliver a more fully developed API later (with API keys, etc.), but we've had a lot of developers ask for the ability to get the current release from the store, so we decided to go ahead and publish this one. | ||
| + | |||
| + | ===== General Rules ===== | ||
| + | Here are a few general rules for using the API: | ||
| + | |||
| + | * Version check failures of any kind **__should never__** be fatal or treated as a blocking/ | ||
| + | * Never loop a call to the API quickly (especially under failure cases). You could unintentionally cause a DoS attack on our hosted systems if calls are made too frequently. | ||
| + | * If you do automatic timed calls, do them at most **__once a day__**. | ||
| + | * Always catch exceptions and bad HTTP return codes (400-599 basically) returned from the API call. Again, don't loop if you get a bad return code - just log the error and continue your normal operation. | ||
| + | |||
| + | We don't currently have the backend infrastructure of larger organizations, | ||
| + | |||
| + | ===== API v2 ===== | ||
| + | Despite the version number, this is in fact just the first API that can be used to get the latest version from the plugin store. It's a very simple HTTP call: | ||
| + | |||
| + | '' | ||
| + | |||
| + | Assuming the call completes successfully, | ||
| + | |||
| + | { | ||
| + | " | ||
| + | { | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | }, | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | ] | ||
| + | } | ||
| + | |||
| + | The return is a dictionary with one key, " | ||
| + | |||
| + | ^Key^Value^ | ||
| + | |docUrl|The URL to the docs or help for the plugin as specified in the plugin store.| | ||
| + | |githubRepoUrl|The URL to the GitHub repo where the plugin is hosted (blank if it's not hosted on GitHub).| | ||
| + | |helpUrl|URL to get help as configured for the plugin in the store.| | ||
| + | |iconUrl|URL to get the icon for the plugin.| | ||
| + | |id|Internal ID for the plugin in the store. Can be used to construct a URL to the plugin' | ||
| + | |latestRelease|Dictionary with release specific information (see next table for details) **OR** a string if the plugin is one of the built-in plugins in Indigo (you won't process those since the version is tied to the Indigo release).| | ||
| + | |name|Name of the plugin.| | ||
| + | |pluginId|String ID of the plugin (same as what you passed into the URL above).| | ||
| + | |summary|Short summary that's shown when you mouse over a plugin in the plugin list.| | ||
| + | |||
| + | The dictionary returned in the '' | ||
| + | |||
| + | ^Key^Value^ | ||
| + | |downloadCount|Number of times the plugin has been downloaded (currently relatively inaccurate).| | ||
| + | |downloadUrl|URL to download the release.| | ||
| + | |number|Release number (i.e. 1.6.9, etc.).| | ||
| + | |releaseDate|Date the release was created (string of the form YYYY-MM-DD).| | ||
| + | |requirements|Requirements specified for this release (if any, otherwise empty string).| | ||
| + | |summary|Summary of changes in this release.| | ||
| + | |whatsNew|What' | ||
| + | |||
| + | ==== Example ==== | ||
| + | We want to provide an example of how you might call this from within a plugin. Eventually we will have version checking and update notifications available as a built-in feature of plugins. In the meantime, however, you can follow this pattern. | ||
| + | |||
| + | First, here's a method you can implement in your Plugin subclass. You'll need to add this import at the top: '' | ||
| + | |||
| + | def version_check(pluginId=self.pluginId): | ||
| + | # Create some URLs we'll use later on | ||
| + | current_version_url = " | ||
| + | store_detail_url = " | ||
| + | try: | ||
| + | # GET the url from the servers with a short timeout (avoids hanging the plugin) | ||
| + | reply = requests.get(current_version_url, | ||
| + | # This will raise an exception if the server returned an error | ||
| + | reply.raise_for_status() | ||
| + | # We now have a good reply so we get the json | ||
| + | reply_dict = reply.json() | ||
| + | plugin_dict = reply_dict[" | ||
| + | # Make sure that the ' | ||
| + | latest_release = plugin_dict[" | ||
| + | if isinstance(latest_release, | ||
| + | # Compare the current version with the one returned in the reply dict | ||
| + | if LooseVersion(latest_release[" | ||
| + | # The release in the store is newer than the current version. | ||
| + | # We'll do a couple of things: first, we'll just log it | ||
| + | self.logger.info( | ||
| + | "A new version of the plugin (v{}) is available at: {}" | ||
| + | latest_release[" | ||
| + | store_detail_url.format(plugin_dict[" | ||
| + | ) | ||
| + | ) | ||
| + | # We'll change the value of a variable named " | ||
| + | # which the user can then build a trigger on (or whatever). You could also insert the download URL, | ||
| + | # the store URL, whatever. | ||
| + | try: | ||
| + | variable_name = " | ||
| + | indigo.variable.updateValue(variable_name, | ||
| + | except: | ||
| + | pass | ||
| + | # We'll execute an action group named "New Version for Plugin Name". The action group could | ||
| + | # then get the value of the variable above to do some kind of notification. | ||
| + | try: | ||
| + | action_group_name = "New Version for {}" | ||
| + | indigo.actionGroup.execute(action_group_name) | ||
| + | except: | ||
| + | pass | ||
| + | # There are lots of other things you could do here. The final thing we're going to do is send | ||
| + | # an email to self.version_check_email which I'll assume that you've set from the plugin | ||
| + | # config. | ||
| + | if hasattr(self, | ||
| + | indigo.server.sendEmailTo( | ||
| + | self.version_check_email, | ||
| + | subject=" | ||
| + | body=" | ||
| + | ) | ||
| + | except Exception as exc: | ||
| + | self.logger.error(unicode(exc)) | ||
| + | |||
| + | This function checks the store for the latest version, and if it exists it performs several actions: | ||
| + | - It writes the to the log | ||
| + | - It updates a variable with a specific name (Plugin_Name_Current_Version) with the new release number | ||
| + | - It calls an action group with a specific name (New Version for Plugin Name) | ||
| + | - It sends an email to self.version_check_email (assumes that you set it somehow, likely from the plugin prefs) | ||
| + | |||
| + | Notice how safe this method is: it doesn' | ||
| + | |||
| + | So, how do you call this method? You can call it in the '' | ||
| + | |||
| + | def runConcurrentThread(self): | ||
| + | self.logger.debug(" | ||
| + | try: | ||
| + | while True: | ||
| + | self.version_check() | ||
| + | self.sleep(60*60*24) # Sleep for 24 hours | ||
| + | except self.StopThread: | ||
| + | self.logger.debug(" | ||
| + | |||
| + | If your plugin does implement one, then you'll need to add the 24-hour wait logic depending on your implementation. As mentioned above, **__you should not check more than daily__**. | ||