{"action":"created","comment":{"url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/comments/131384951","pull_request_review_id":54360497,"id":131384951,"diff_hunk":"@@ -0,0 +1,230 @@\n+## ESP8266 Lua OTA\n+\n+Espressif use an optional update approach for their firmware know as OTA (over the air).  \n+This module offers an equivalent facility for Lua applications developers, and enables \n+module development and production updates by carrying out automatic synchronisation \n+with a named provisioning service at reboot.\n+\n+### Overview\n+\n+This `luaOTA` provisioning service uses a different approach to \n+[enduser setup](https://nodemcu.readthedocs.io/en/dev/en/modules/enduser-setup/).\n+The basic concept here is that the ESP modules are configured with a pre-imaged file \n+system that includes a number of files in the luaOTA namespace.  (SPIFFS doesn't \n+implement a directory hierarchy as such, but instead simply treats the conventional\n+directory separator as a character in the filename.  Nonetheless, the \"luaOTA/\"\n+prefix serves to separate the lc files in the luaOTA namespace.) \n+\n+-  `luaOTA/check.lc`  This module should always be first executed at startup.\n+-  `luaOTA/_init.lc` \n+-  `luaOTA/_doTick.lc`\n+-  `luaOTA/_provision.lc`\n+\n+A fifth file `luaOTA/config.json` contains a JSON parameterised local configuration that\n+can be initially create by and subsequently updated by the provisioning process. Most\n+importantly this configuration contains the TCP address of the provisioning service, and\n+a shared secret that is used to sign any records exchanged between the ESP client and\n+the provisioning service. \n+\n+Under this approach, `init.lua` is still required but it is reduced to a one-line lua\n+call which invokes the `luaOTA` module by a `require \"luaOTA.check\"` statement.\n+\n+The `config.json` file which provides the minimum configuration parameters to connect to\n+the WiFi and provisioning server, however these can by overridden through the UART by\n+first doing a `tmr.stop(0) and then a manual initialisation as described in the\n+[init.lua](#initlua) section below.\n+\n+`luaOTA` configures the wifi and connects to the required sid in STA mode using the\n+local configuration. The ESP's IP address is allocated using DHCP unless the optional\n+three static IP parameters have been configured. It then attempts to establish a\n+connection to the named provisioning service. If this is absent, a timeout occurs or the\n+service returns a \"no update\" status, then module does a full clean up of all the\n+`luaOTA` resources (if the `leave` parameter is false, then the wifi stack is then also\n+shutdown.), and it then transfers control by a `node.task.post()` to the configured\n+application module and function.\n+\n+If `luaOTA` does establish a connection to IP address:port of the provisioning service,\n+it, then issues a \"getupdate\" request using its CPU ID and a configuration parameter\n+block as context. This update dialogue uses a simple JSON protocol(described below) that\n+enables the provision server either to respond with a \"no update\", or to start a\n+dialogue to reprovision the ESP8266's SPIFFS.\n+\n+In the case of \"no update\", `luaOTA` is by design ephemeral, that is it shuts down the\n+net services and does a full resource clean up. Hence the presence of the provisioning\n+service is entirely optional and it doesn't needed to be online during normal operation,\n+as `luaOTA` will fail back to transferring control to the main Lua application.\n+\n+In the case of an active update, **the ESP is restarted** so resource cleanup on\n+completion is not an issue. The provisioning dialogue is signed, so the host\n+provisioning service and the protocol are trusted, with the provisioning service driving\n+the process.  This greatly simplifies the `luaOTA` client coding as this is a simple \n+responder, which actions simple commands such as: \n+- download a file, \n+- download and compile file,\n+- upload a file\n+- rename (or delete) a file\n+\n+with the ESP being rebooted on completion of the updates to the SPIFFS.  Hence in \n+practice the ESP boots into one one two modes: \n+- _normal execution_ or \n+- _OTA update_ followed by reboot and normal execution.\n+\n+Note that even though NodeMCU follows the Lua convention of using the `lua` and `lc`\n+extensions respectively for source files that need to be compiled, and for pre-compiled\n+files, the Lua loader itself only uses the presence of a binary header to determine the\n+file mode. Hence if the `init.lua` file contains pre-compiled content, and similarly all\n+loaded modules use pre-compiled lc files, then the ESP can run in production mode\n+_without needing to invoke the compiler at all_.\n+\n+The simplest model for the host provisioning service to maintain a reference source\n+directory on the host (per ESP module). The Lua developer keep this under **git** or\n+equivalent and make any changes there, so that synchronisation of the ESP will be done\n+automatically on reboot.\n+\n+### init.lua\n+\n+This is typically includes a single line: \n+```Lua\n+require \"LuaOTA.check\"\n+```\n+however if the configuration is incomplete then this can be aborted as manual process \n+by entering the manual command through the UART\n+``Lua\n+tmr.stop(0); require \"luaOTA.check\":_init {ssid =\"SOMESID\" --[[etc. ]]}\n+``` \n+where the parameters to the `_init` method are: \n+\n+-  `ssid` and `spwd`.  The SSID of the Wifi service to connect to, together with its \n+password.\n+-  `server` and `port`.  The name or IP address and port of the provisioning server.\n+-  `secret`.  A site-specific secret shared with the provsioning server for MD5-based \n+signing of the protocol messages.\n+-  `leave`.  If true the STA service is left connected otherwise the wifi is shutdown\n+-  `espip`,`gw`,`nm`,`ns`.  These parameters are omitted if the ESP is using a DHCP \n+service for IP configuration, otherwise you need to specify the ESP's IP, gateway, \n+netmask and default nameserver.\n+\n+If the global `DEBUG` is set, then LuaOTA will also dump out some diagnostic debug.\n+\n+### luaOTA.check\n+\n+This only has one public method: `_init` which can be called with the above parameters.\n+However the require wrapper in the check module also posts a call to `self:_init()` as a\n+new task.  This new task function includes a guard to prevent a double call in the case\n+where the binding require includes an explicit call to `_init()` \n+\n+Any provisioning changes results in a reboot, so the only normal \"callback\" is to invoke\n+the application entry method as defined in `config.json` using a `node.task.post()`\n+\n+\n+### luaOTAserver.lua\n+\n+This is often tailored to specific project requirements, but a simple example of a\n+provisioning server is included which provides the corresponding server-side\n+functionality. This example is coded in Lua and can run on any development PC or server\n+that supports Lua 5.1 - 5.3 and the common modules `socket`, `lfs`, `md5` and `cjson`.\n+It can be easiliy be used as the basis of one for your specific project needs.\n+\n+Note that even though this file is included in the `luaOTA` subdirectory within Lua\n+examples, this is designed to run on the host and should not be included in the\n+ESP SPIFFS.\n+\n+## Implementation Notes\n+\n+-  The NodeMCu build must include the following modules: `wifi`, `net`, `file`, `tmr`,\n+`crypto` and`sjason`. \n+\n+-  This implementation follow ephemral practices, that it is coded to ensure that all\n+resources used are collected by the Lua GC, and hence the available heap on \n+application start is the same as if luaOTA had not been called.\n+\n+-  Methods in the `check` file are static and inherit self as an upvalue.\n+\n+-  In order to run comfortably within ESP resources, luaOTA executes its main \n+functionality as a number of overlay methods. These are loaded dynamically (and largely\n+transparently by an `__index` metamethod.","path":"lua_examples/luaOTA/README.md","position":145,"original_position":145,"commit_id":"c6845b1bdb06752a28cd37b997623ba3b99de6a9","original_commit_id":"c6845b1bdb06752a28cd37b997623ba3b99de6a9","user":{"login":"nickandrew","id":7175,"avatar_url":"https://avatars0.githubusercontent.com/u/7175?v=4","gravatar_id":"","url":"https://api.github.com/users/nickandrew","html_url":"https://github.com/nickandrew","followers_url":"https://api.github.com/users/nickandrew/followers","following_url":"https://api.github.com/users/nickandrew/following{/other_user}","gists_url":"https://api.github.com/users/nickandrew/gists{/gist_id}","starred_url":"https://api.github.com/users/nickandrew/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nickandrew/subscriptions","organizations_url":"https://api.github.com/users/nickandrew/orgs","repos_url":"https://api.github.com/users/nickandrew/repos","events_url":"https://api.github.com/users/nickandrew/events{/privacy}","received_events_url":"https://api.github.com/users/nickandrew/received_events","type":"User","site_admin":false},"body":"Missing a close parenthesis here.","created_at":"2017-08-04T13:11:27Z","updated_at":"2017-08-04T13:36:18Z","html_url":"https://github.com/nodemcu/nodemcu-firmware/pull/2059#discussion_r131384951","pull_request_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059","_links":{"self":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/comments/131384951"},"html":{"href":"https://github.com/nodemcu/nodemcu-firmware/pull/2059#discussion_r131384951"},"pull_request":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059"}}},"pull_request":{"url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059","id":134161593,"html_url":"https://github.com/nodemcu/nodemcu-firmware/pull/2059","diff_url":"https://github.com/nodemcu/nodemcu-firmware/pull/2059.diff","patch_url":"https://github.com/nodemcu/nodemcu-firmware/pull/2059.patch","issue_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/issues/2059","number":2059,"state":"open","locked":false,"title":"Lua OTA proviisioning system","user":{"login":"TerryE","id":690173,"avatar_url":"https://avatars3.githubusercontent.com/u/690173?v=4","gravatar_id":"","url":"https://api.github.com/users/TerryE","html_url":"https://github.com/TerryE","followers_url":"https://api.github.com/users/TerryE/followers","following_url":"https://api.github.com/users/TerryE/following{/other_user}","gists_url":"https://api.github.com/users/TerryE/gists{/gist_id}","starred_url":"https://api.github.com/users/TerryE/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TerryE/subscriptions","organizations_url":"https://api.github.com/users/TerryE/orgs","repos_url":"https://api.github.com/users/TerryE/repos","events_url":"https://api.github.com/users/TerryE/events{/privacy}","received_events_url":"https://api.github.com/users/TerryE/received_events","type":"User","site_admin":false},"body":"-  This PR is for the `dev` branch rather than for `master`.\r\n- This PR is compliant with the [other contributing guidelines](https://github.com/nodemcu/nodemcu-firmware/blob/dev/CONTRIBUTING.md) as well (if not, please describe why).\r\n- I have thoroughly tested my contribution.\r\n\r\n## Description of and rationale behind this PR\r\n\r\nSee README.md.  Currently the Lua examples are not documented in the documentation folder so I have included the documentation as an in-directory README.  The intro is\r\n\r\n> Espressif use an optional update approach for their firmware know as OTA (over the air).   This module offers an equivalent facility for Lua applications developers, and enables  module development and production updates by carrying out automatic synchronisation  with a named provisioning service at reboot.\r\n> \r\n> This `luaOTA` provisioning service uses a different approach to  [enduser setup](https://nodemcu.readthedocs.io/en/dev/en/modules/enduser-setup/). The basic concept here is that the ESP modules are configured with a pre-imaged file system that includes a number of files in the luaOTA namespace.  (SPIFFS doesn't implement a directory hierarchy as such, but instead simply treats the conventionaldirectory separator as a character in the filename.  Nonetheless, the \"luaOTA/\" prefix serves to separate the lc files in the luaOTA namespace.) \r\n  ","created_at":"2017-08-04T12:59:22Z","updated_at":"2017-08-04T13:36:18Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d5cd4b0529895cc5d95c5389d7cebf9184a6628d","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059/commits","review_comments_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059/comments","review_comment_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/issues/2059/comments","statuses_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/statuses/c6845b1bdb06752a28cd37b997623ba3b99de6a9","head":{"label":"TerryE:dev-luaOTA","ref":"dev-luaOTA","sha":"c6845b1bdb06752a28cd37b997623ba3b99de6a9","user":{"login":"TerryE","id":690173,"avatar_url":"https://avatars3.githubusercontent.com/u/690173?v=4","gravatar_id":"","url":"https://api.github.com/users/TerryE","html_url":"https://github.com/TerryE","followers_url":"https://api.github.com/users/TerryE/followers","following_url":"https://api.github.com/users/TerryE/following{/other_user}","gists_url":"https://api.github.com/users/TerryE/gists{/gist_id}","starred_url":"https://api.github.com/users/TerryE/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TerryE/subscriptions","organizations_url":"https://api.github.com/users/TerryE/orgs","repos_url":"https://api.github.com/users/TerryE/repos","events_url":"https://api.github.com/users/TerryE/events{/privacy}","received_events_url":"https://api.github.com/users/TerryE/received_events","type":"User","site_admin":false},"repo":{"id":41672709,"name":"nodemcu-firmware","full_name":"TerryE/nodemcu-firmware","owner":{"login":"TerryE","id":690173,"avatar_url":"https://avatars3.githubusercontent.com/u/690173?v=4","gravatar_id":"","url":"https://api.github.com/users/TerryE","html_url":"https://github.com/TerryE","followers_url":"https://api.github.com/users/TerryE/followers","following_url":"https://api.github.com/users/TerryE/following{/other_user}","gists_url":"https://api.github.com/users/TerryE/gists{/gist_id}","starred_url":"https://api.github.com/users/TerryE/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TerryE/subscriptions","organizations_url":"https://api.github.com/users/TerryE/orgs","repos_url":"https://api.github.com/users/TerryE/repos","events_url":"https://api.github.com/users/TerryE/events{/privacy}","received_events_url":"https://api.github.com/users/TerryE/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/TerryE/nodemcu-firmware","description":"lua based interactive firmware for mcu like esp8266","fork":true,"url":"https://api.github.com/repos/TerryE/nodemcu-firmware","forks_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/forks","keys_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/teams","hooks_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/hooks","issue_events_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/issues/events{/number}","events_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/events","assignees_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/assignees{/user}","branches_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/branches{/branch}","tags_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/tags","blobs_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/git/refs{/sha}","trees_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/statuses/{sha}","languages_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/languages","stargazers_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/stargazers","contributors_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/contributors","subscribers_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/subscribers","subscription_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/subscription","commits_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/commits{/sha}","git_commits_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/git/commits{/sha}","comments_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/comments{/number}","issue_comment_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/issues/comments{/number}","contents_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/contents/{+path}","compare_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/merges","archive_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/downloads","issues_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/issues{/number}","pulls_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/pulls{/number}","milestones_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/milestones{/number}","notifications_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/labels{/name}","releases_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/releases{/id}","deployments_url":"https://api.github.com/repos/TerryE/nodemcu-firmware/deployments","created_at":"2015-08-31T11:52:26Z","updated_at":"2016-06-27T02:38:48Z","pushed_at":"2017-08-04T12:52:39Z","git_url":"git://github.com/TerryE/nodemcu-firmware.git","ssh_url":"git@github.com:TerryE/nodemcu-firmware.git","clone_url":"https://github.com/TerryE/nodemcu-firmware.git","svn_url":"https://github.com/TerryE/nodemcu-firmware","homepage":"http://nodemcu.com","size":101735,"stargazers_count":1,"watchers_count":1,"language":"C","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"dev"}},"base":{"label":"nodemcu:master","ref":"master","sha":"c8ac5cfb912ff206b03dd7c60ffbb2dafb83fe5e","user":{"login":"nodemcu","id":9865736,"avatar_url":"https://avatars0.githubusercontent.com/u/9865736?v=4","gravatar_id":"","url":"https://api.github.com/users/nodemcu","html_url":"https://github.com/nodemcu","followers_url":"https://api.github.com/users/nodemcu/followers","following_url":"https://api.github.com/users/nodemcu/following{/other_user}","gists_url":"https://api.github.com/users/nodemcu/gists{/gist_id}","starred_url":"https://api.github.com/users/nodemcu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nodemcu/subscriptions","organizations_url":"https://api.github.com/users/nodemcu/orgs","repos_url":"https://api.github.com/users/nodemcu/repos","events_url":"https://api.github.com/users/nodemcu/events{/privacy}","received_events_url":"https://api.github.com/users/nodemcu/received_events","type":"User","site_admin":false},"repo":{"id":26917568,"name":"nodemcu-firmware","full_name":"nodemcu/nodemcu-firmware","owner":{"login":"nodemcu","id":9865736,"avatar_url":"https://avatars0.githubusercontent.com/u/9865736?v=4","gravatar_id":"","url":"https://api.github.com/users/nodemcu","html_url":"https://github.com/nodemcu","followers_url":"https://api.github.com/users/nodemcu/followers","following_url":"https://api.github.com/users/nodemcu/following{/other_user}","gists_url":"https://api.github.com/users/nodemcu/gists{/gist_id}","starred_url":"https://api.github.com/users/nodemcu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nodemcu/subscriptions","organizations_url":"https://api.github.com/users/nodemcu/orgs","repos_url":"https://api.github.com/users/nodemcu/repos","events_url":"https://api.github.com/users/nodemcu/events{/privacy}","received_events_url":"https://api.github.com/users/nodemcu/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nodemcu/nodemcu-firmware","description":"lua based interactive firmware for mcu like esp8266","fork":false,"url":"https://api.github.com/repos/nodemcu/nodemcu-firmware","forks_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/forks","keys_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/teams","hooks_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/hooks","issue_events_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/issues/events{/number}","events_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/events","assignees_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/assignees{/user}","branches_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/branches{/branch}","tags_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/tags","blobs_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/git/refs{/sha}","trees_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/statuses/{sha}","languages_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/languages","stargazers_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/stargazers","contributors_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/contributors","subscribers_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/subscribers","subscription_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/subscription","commits_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/commits{/sha}","git_commits_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/git/commits{/sha}","comments_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/comments{/number}","issue_comment_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/issues/comments{/number}","contents_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/contents/{+path}","compare_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/merges","archive_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/downloads","issues_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/issues{/number}","pulls_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls{/number}","milestones_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/milestones{/number}","notifications_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/labels{/name}","releases_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/releases{/id}","deployments_url":"https://api.github.com/repos/nodemcu/nodemcu-firmware/deployments","created_at":"2014-11-20T15:06:45Z","updated_at":"2017-08-04T04:40:55Z","pushed_at":"2017-08-04T12:59:23Z","git_url":"git://github.com/nodemcu/nodemcu-firmware.git","ssh_url":"git@github.com:nodemcu/nodemcu-firmware.git","clone_url":"https://github.com/nodemcu/nodemcu-firmware.git","svn_url":"https://github.com/nodemcu/nodemcu-firmware","homepage":"http://nodemcu.com","size":102090,"stargazers_count":3603,"watchers_count":3603,"language":"C","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1676,"mirror_url":null,"open_issues_count":168,"forks":1676,"open_issues":168,"watchers":3603,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059"},"html":{"href":"https://github.com/nodemcu/nodemcu-firmware/pull/2059"},"issue":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/issues/2059"},"comments":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/issues/2059/comments"},"review_comments":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059/comments"},"review_comment":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/pulls/2059/commits"},"statuses":{"href":"https://api.github.com/repos/nodemcu/nodemcu-firmware/statuses/c6845b1bdb06752a28cd37b997623ba3b99de6a9"}}}}
{"actor":{"display_login":"nickandrew"}}