{"action":"created","comment":{"url":"https://api.github.com/repos/google/WebFundamentals/pulls/comments/56122462","id":56122462,"diff_hunk":"@@ -0,0 +1,545 @@\n+---\n+layout: updates/post\n+title: \"Creating a Web-enabled IoT device with Intel&reg; Edison\"\n+description: \"Internet of Things is really on everyone's lips these\n+days, but what if you could create an IoT device which you accessed\n+from your web browser? In this article we look at how you can do\n+exactly that using Physical Web, Web Bluetooth as well as Node.js,\n+running on an Intel&reg; Edison Arduino breakout board.\"\n+published_on: 2016-03-20\n+updated_on: 2016-03-20\n+authors:\n+  - kenchris\n+tags:\n+  - news\n+  - Bluetooth\n+  - IoT\n+  - WebBluetooth\n+  - PhysicalWeb\n+  - Edison\n+featured_image: /web/updates/images/2016/03/web-enabled-internet-of-things/board.jpg\n+---\n+\n+<p class=\"intro\">\n+Internet of Things is really on everyone's lips these days, and\n+it makes a tinkerer and a programmer like me very excited. Nothing\n+is much cooler than bringing your own inventions to life, and\n+being able to talk to them!\n+</p>\n+\n+![Client application](/web/updates/images/2016/03/web-enabled-internet-of-things/tablet-ux.png)\n+\n+# Web and IoT, a match to be\n+\n+There are still a lot of hurdles to be overcome before Internet\n+of Things will be a huge success. Personally, I am worrying about\n+companies requiring people to install their apps for each device\n+they produce, cluttering my phone with a multitude of apps that\n+I *rarely* use. Something that I would like to avoid.\n+\n+For this reason, I have been very excited about the\n+[Physical Web](https://google.github.io/physical-web/)\n+project, which allows devices to broadcast a URL to an online\n+website, in a *non-intrusive way*. In combinations with emerging\n+web technologies such as\n+[Web Bluetooth](https://webbluetoothcg.github.io/web-bluetooth/),\n+[Web USB](https://wicg.github.io/webusb/) and\n+[Web NFC](https://w3c.github.io/web-nfc/), the site can either\n+connect directly to the device or at least explain the proper\n+way of doing so.\n+\n+Web Bluetooth would be a good way of doing so, but in some cases\n+you might require a physical connection (say via USB) for security\n+reasons, for instance in an office setting.\n+\n+The web site can also serve as what is called a Progressive Web\n+App (aka PWA), which means that the site has a responsive app like\n+user experience, works offline and can be added to the home screen.\n+Google has a really good explainer about PWAs, which I urge you to\n+check out:\n+\n+[Progressive Web Apps](https://developers.google.com/web/progressive-web-apps)\n+\n+As a proof of concept, I have been building a small device using\n+the Intel&reg; Edison Arduino breakout board. It contains a temperature\n+sensor (TMP36) as well as an actuator (colored LED cathode). The\n+schematics can be found at the end of this article.\n+\n+![Breadboard](/web/updates/images/2016/03/web-enabled-internet-of-things/breadboard.jpg)\n+\n+The Edison is an interesting device as it can run a full Linux\n+distribution and therefore allows me to program it easily using\n+Node.js. The installer allows for installing the Intel&reg; XDK which\n+makes it easy to get started, though you can program and upload\n+to your device manually as well.\n+\n+> NOTE: It is possible to use Brillo or Ostro instead of the\n+> default OS software, in which case you should follow the documentation\n+> for these OSes for now to get a Node.js application running on the\n+> device.\n+\n+For my Node.js app, I required three node modules, as well as their\n+dependencies: `eddystone-beacon`, `parse-color`, `johnny-five`. The\n+former will automatically install `noble` which is the node module\n+that I use for talking Bluetooth Low Energy. \n+\n+> NOTE: It is important to not list `noble` as a dependency in the\n+> `package.json` file, as you need to use the same noble instance as\n+> `eddystone-beacon`, in order for them to work together.\n+\n+You can find more info [here](https://github.com/don/node-eddystone-beacon/issues/30)\n+\n+The `package.json` file for the project looks like this:\n+\n+{% highlight javascript %}\n+{\n+  \"name\": \"edison-webbluetooth-demo-server\",\n+  \"version\": \"1.0.0\",\n+  \"main\": \"main.js\",\n+  \"engines\": {\n+    \"node\": \">=0.10.0\"\n+  },\n+  \"dependencies\": {\n+    \"eddystone-beacon\" : \"1.0.5\",\n+    \"parse-color\": \"1.0.0\",\n+    \"johnny-five\": \"0.9.30\"\n+  }\n+}\n+{% endhighlight %}\n+\n+## Announcing the web site\n+\n+The beta version of Chrome on Android supports Physical Web, which\n+allows Chrome to see URLs being broadcasted by devices around it.\n+There are a few requirements, such as the sites need to be publicly\n+accessible and use HTTPS.\n+\n+My demo app is hosted at <https://edison-webbt.appspot.com/> but the\n+Eddystone protocol has a limit on the size of the URL (max 18 bytes,\n+more info\n+[here](https://github.com/google/eddystone/blob/master/eddystone-url/docs/config-service-spec.md#34-uri-data)), \n+so I have to use a URL shortener in order for it to work.\n+\n+Broadcasting the URL is quite simple. Import the required libraries and\n+call a few functions. One way of doing it is calling `advertiseUrl`, when\n+the BLE chip is turned on:\n+\n+{% highlight javascript %}\n+var beacon = require(\"eddystone-beacon\");\n+var bleno = require('eddystone-beacon/node_modules/bleno');\n+\n+bleno.on('stateChange', function(state) {    \n+  if (state === 'poweredOn') {\n+    beacon.advertiseUrl(\"https://goo.gl/9FomQC\", {name: 'Edison'});\n+  }   \n+}\n+{% endhighlight %}\n+\n+That really couldn't be much easier. Below you see that Chrome finds\n+my device nicely.\n+\n+> NOTE: The Physical Web enabled devices only show up on your phone\n+> when Bluetooth is turned on, and you launch Chrome (currently only\n+> works with Beta). Additionally, you have to opt into the feature\n+> first time you launch Chrome beta.\n+\n+<img alt=\"Chrome announces nearby Physical Web beacons\" src=\"/web/updates/images/2016/03/web-enabled-internet-of-things/nearby.png\" style=\"width: 50%; float: left\"/>\n+<img alt=\"The web app URL is listed\" src=\"/web/updates/images/2016/03/web-enabled-internet-of-things/physicalweb.png\" style=\"width: 50%; float: left\"/>\n+<br/>\n+\n+## Communicating with the sensor/actuator\n+\n+For talking to my board enhancements I used [Johnny-Five](http://johnny-five.io/).\n+In simple cases like this, this is not strictly easier than communicating\n+with the raws input (pins) manually, but for bigger projects it can be\n+a real help. Johnny-Five has a nice abstraction for talking to the TMP36\n+sensor, but for some reason I could only get it to return \"undefined\"\n+as the current temperature so I went ahead and read the temperature\n+value manually.\n+\n+Below you find the simple code for listening to temperature changes\n+as well as setting the initial LED color.\n+\n+{% highlight javascript %}\n+var five = require(\"johnny-five\");\n+var Edison = require(\"edison-io\");\n+var board = new five.Board({\n+  io: new Edison()\n+});\n+\n+board.on(\"ready\", function() {\n+  var led = new five.Led.RGB({\n+      pins: {\n+          red: 3,\n+          green: 5,\n+          blue: 6\n+      },\n+  });\n+\n+  colorCharacteristic._led = led;\n+  led.color(colorCharacteristic._value);\n+  led.intensity(30);\n+\n+  board.analogRead(\"A0\", function(raw) {\n+    var mV = 5 * 1000 * (raw / 1024);\n+    var value = (mV / 10) - 50;\n+    temperatureCharacteristic.valueChange(value);\n+  });\n+}\n+{% endhighlight %}\n+\n+Don't worry about the `*Characteristic` variables above. These are\n+what I use for communicating via Bluetooth. As you notice, I talk to\n+the TMP36 via the analog `A0` port and the voltage legs on the color\n+LED cathode are connected to digital pin 3, 5 and 6, which happens\n+to be PWM (pulse-wide modulation) pins on the Edison Arduino breakout\n+board.\n+\n+![Edison board](/web/updates/images/2016/03/web-enabled-internet-of-things/board.jpg)\n+\n+## Talking Bluetooth\n+\n+Talking Bluetooth couldn't be much easier than it is with noble.\n+\n+In the following example, we create two Bluetooth Low Energy\n+characteristics, one for the LED and one for the temperature sensor.\n+The former allows reading the current LED color as well as setting\n+a new color. The latter allows subscribing to temperature change events.\n+\n+> NOTE: Initially I had some problems with the Bluetooth connection\n+> being a bit unstable and not working on every startup, or bailing\n+> out during connecting with a Frame Reassemble failure.\n+>\n+> If that happens, typing `rfkill block bluetooth` followed by\n+> `rfkill unblock bluetooth` once over the serial connection, makes it\n+> work again. The former issue went away when I started powering the\n+> device from a power supply instead of just using USB for power.\n+>\n+> The Frame Reassemble failure happened when I sent temperature change\n+> events too often.\n+>\n+> Generally you should always use external power when using Bluetooth\n+> or connect something like a servo to your board.\n+\n+With noble, creating a characteristic is quite easy. All you need to do\n+is to define how the characteristic communicates (options are read,\n+write and notify, or any combination thereof) as well as define an uuid.\n+Creating a new object and inheriting from the `bleno.Characteristic` is\n+the easiest way.\n+\n+> NOTE: I am not using ES2016 here as Edison currently uses an older\n+> version of Node.js.\n+>\n+> With the newly launched [Ostro Project](https://ostroproject.org)\n+> which supports the Edison, that is no longer the case. Also, if you\n+> use Brillo as part of the [Brillo](https://developers.google.com/brillo/) \n+> Early Access Program, then it is possible to compile and install a\n+> recent version of Node.js.\n+\n+The resulting characteristic object looks like the following:\n+\n+{% highlight javascript %}\n+var TemperatureCharacteristic = function() {\n+  bleno.Characteristic.call(this, {\n+    uuid: 'fc0a',\n+    properties: ['read', 'notify'],\n+    value: null\n+  });\n+    \n+  this._lastValue = 0;\n+  this._total = 0;\n+  this._samples = 0;\n+  this._onChange = null;\n+};\n+\n+util.inherits(TemperatureCharacteristic, bleno.Characteristic);\n+{% endhighlight %}\n+\n+We are storing the current temperature value in the `this._lastValue`\n+variable. In order for a \"read\" to work we need to add an onReadRequest\n+method and encode the value.\n+\n+{% highlight javascript %}\n+TemperatureCharacteristic.prototype.onReadRequest = function(offset, callback) {\n+  var data = new Buffer(8);\n+  data.writeDoubleLE(this._lastValue, 0);\n+  callback(this.RESULT_SUCCESS, data);\n+};\n+{% endhighlight %}\n+\n+For \"notify\" we need to add a method to handle subscription and\n+unsubscription. Basically, we simply store a callback. When we\n+have a new temperature reason we want to send, we then call that\n+callback with the new value (encoded as above).\n+\n+{% highlight javascript %}\n+TemperatureCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {\n+  console.log(\"Subscribed to temperature change.\");\n+  this._onChange = updateValueCallback;\n+  this._lastValue = undefined;\n+};\n+\n+TemperatureCharacteristic.prototype.onUnsubscribe = function() {\n+  console.log(\"Unsubscribed to temperature change.\");\n+  this._onChange = null;\n+};\n+{% endhighlight %}\n+\n+As values can fluctuate a bit, we need to smoothen the values we\n+get from the TMP36 sensor. I opted to simply take the average over","path":"src/content/en/updates/posts/2016/03/web-enabled-internet-of-things.markdown","position":289,"original_position":289,"commit_id":"9189bcf123ea985acba9590ecc175a28e0992e7d","original_commit_id":"9189bcf123ea985acba9590ecc175a28e0992e7d","user":{"login":"pandrese","id":1724167,"avatar_url":"https://avatars.githubusercontent.com/u/1724167?v=3","gravatar_id":"","url":"https://api.github.com/users/pandrese","html_url":"https://github.com/pandrese","followers_url":"https://api.github.com/users/pandrese/followers","following_url":"https://api.github.com/users/pandrese/following{/other_user}","gists_url":"https://api.github.com/users/pandrese/gists{/gist_id}","starred_url":"https://api.github.com/users/pandrese/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pandrese/subscriptions","organizations_url":"https://api.github.com/users/pandrese/orgs","repos_url":"https://api.github.com/users/pandrese/repos","events_url":"https://api.github.com/users/pandrese/events{/privacy}","received_events_url":"https://api.github.com/users/pandrese/received_events","type":"User","site_admin":false},"body":"Actually believe that the code for SMA would have been shorter ;)","created_at":"2016-03-15T07:05:45Z","updated_at":"2016-03-15T07:05:45Z","html_url":"https://github.com/google/WebFundamentals/pull/2655#discussion_r56122462","pull_request_url":"https://api.github.com/repos/google/WebFundamentals/pulls/2655","_links":{"self":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/comments/56122462"},"html":{"href":"https://github.com/google/WebFundamentals/pull/2655#discussion_r56122462"},"pull_request":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/2655"}}},"pull_request":{"url":"https://api.github.com/repos/google/WebFundamentals/pulls/2655","id":62386522,"html_url":"https://github.com/google/WebFundamentals/pull/2655","diff_url":"https://github.com/google/WebFundamentals/pull/2655.diff","patch_url":"https://github.com/google/WebFundamentals/pull/2655.patch","issue_url":"https://api.github.com/repos/google/WebFundamentals/issues/2655","number":2655,"state":"open","locked":false,"title":"[DO NOT MERGE] Add article about web-enabled IoT devices, feat. WebBT and PhysicalWeb","user":{"login":"kenchris","id":1101339,"avatar_url":"https://avatars.githubusercontent.com/u/1101339?v=3","gravatar_id":"","url":"https://api.github.com/users/kenchris","html_url":"https://github.com/kenchris","followers_url":"https://api.github.com/users/kenchris/followers","following_url":"https://api.github.com/users/kenchris/following{/other_user}","gists_url":"https://api.github.com/users/kenchris/gists{/gist_id}","starred_url":"https://api.github.com/users/kenchris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kenchris/subscriptions","organizations_url":"https://api.github.com/users/kenchris/orgs","repos_url":"https://api.github.com/users/kenchris/repos","events_url":"https://api.github.com/users/kenchris/events{/privacy}","received_events_url":"https://api.github.com/users/kenchris/received_events","type":"User","site_admin":false},"body":"@PaulKinlan @beaufortfrancois ","created_at":"2016-03-10T13:05:56Z","updated_at":"2016-03-15T07:05:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"564ed7a890d88d12531aa0a5b55e11d4909b7af2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/google/WebFundamentals/pulls/2655/commits","review_comments_url":"https://api.github.com/repos/google/WebFundamentals/pulls/2655/comments","review_comment_url":"https://api.github.com/repos/google/WebFundamentals/pulls/comments{/number}","comments_url":"https://api.github.com/repos/google/WebFundamentals/issues/2655/comments","statuses_url":"https://api.github.com/repos/google/WebFundamentals/statuses/9189bcf123ea985acba9590ecc175a28e0992e7d","head":{"label":"kenchris:webiot","ref":"webiot","sha":"9189bcf123ea985acba9590ecc175a28e0992e7d","user":{"login":"kenchris","id":1101339,"avatar_url":"https://avatars.githubusercontent.com/u/1101339?v=3","gravatar_id":"","url":"https://api.github.com/users/kenchris","html_url":"https://github.com/kenchris","followers_url":"https://api.github.com/users/kenchris/followers","following_url":"https://api.github.com/users/kenchris/following{/other_user}","gists_url":"https://api.github.com/users/kenchris/gists{/gist_id}","starred_url":"https://api.github.com/users/kenchris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kenchris/subscriptions","organizations_url":"https://api.github.com/users/kenchris/orgs","repos_url":"https://api.github.com/users/kenchris/repos","events_url":"https://api.github.com/users/kenchris/events{/privacy}","received_events_url":"https://api.github.com/users/kenchris/received_events","type":"User","site_admin":false},"repo":{"id":44526244,"name":"WebFundamentals","full_name":"kenchris/WebFundamentals","owner":{"login":"kenchris","id":1101339,"avatar_url":"https://avatars.githubusercontent.com/u/1101339?v=3","gravatar_id":"","url":"https://api.github.com/users/kenchris","html_url":"https://github.com/kenchris","followers_url":"https://api.github.com/users/kenchris/followers","following_url":"https://api.github.com/users/kenchris/following{/other_user}","gists_url":"https://api.github.com/users/kenchris/gists{/gist_id}","starred_url":"https://api.github.com/users/kenchris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kenchris/subscriptions","organizations_url":"https://api.github.com/users/kenchris/orgs","repos_url":"https://api.github.com/users/kenchris/repos","events_url":"https://api.github.com/users/kenchris/events{/privacy}","received_events_url":"https://api.github.com/users/kenchris/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/kenchris/WebFundamentals","description":"Best practices for modern web development","fork":true,"url":"https://api.github.com/repos/kenchris/WebFundamentals","forks_url":"https://api.github.com/repos/kenchris/WebFundamentals/forks","keys_url":"https://api.github.com/repos/kenchris/WebFundamentals/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kenchris/WebFundamentals/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kenchris/WebFundamentals/teams","hooks_url":"https://api.github.com/repos/kenchris/WebFundamentals/hooks","issue_events_url":"https://api.github.com/repos/kenchris/WebFundamentals/issues/events{/number}","events_url":"https://api.github.com/repos/kenchris/WebFundamentals/events","assignees_url":"https://api.github.com/repos/kenchris/WebFundamentals/assignees{/user}","branches_url":"https://api.github.com/repos/kenchris/WebFundamentals/branches{/branch}","tags_url":"https://api.github.com/repos/kenchris/WebFundamentals/tags","blobs_url":"https://api.github.com/repos/kenchris/WebFundamentals/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kenchris/WebFundamentals/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kenchris/WebFundamentals/git/refs{/sha}","trees_url":"https://api.github.com/repos/kenchris/WebFundamentals/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kenchris/WebFundamentals/statuses/{sha}","languages_url":"https://api.github.com/repos/kenchris/WebFundamentals/languages","stargazers_url":"https://api.github.com/repos/kenchris/WebFundamentals/stargazers","contributors_url":"https://api.github.com/repos/kenchris/WebFundamentals/contributors","subscribers_url":"https://api.github.com/repos/kenchris/WebFundamentals/subscribers","subscription_url":"https://api.github.com/repos/kenchris/WebFundamentals/subscription","commits_url":"https://api.github.com/repos/kenchris/WebFundamentals/commits{/sha}","git_commits_url":"https://api.github.com/repos/kenchris/WebFundamentals/git/commits{/sha}","comments_url":"https://api.github.com/repos/kenchris/WebFundamentals/comments{/number}","issue_comment_url":"https://api.github.com/repos/kenchris/WebFundamentals/issues/comments{/number}","contents_url":"https://api.github.com/repos/kenchris/WebFundamentals/contents/{+path}","compare_url":"https://api.github.com/repos/kenchris/WebFundamentals/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kenchris/WebFundamentals/merges","archive_url":"https://api.github.com/repos/kenchris/WebFundamentals/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kenchris/WebFundamentals/downloads","issues_url":"https://api.github.com/repos/kenchris/WebFundamentals/issues{/number}","pulls_url":"https://api.github.com/repos/kenchris/WebFundamentals/pulls{/number}","milestones_url":"https://api.github.com/repos/kenchris/WebFundamentals/milestones{/number}","notifications_url":"https://api.github.com/repos/kenchris/WebFundamentals/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kenchris/WebFundamentals/labels{/name}","releases_url":"https://api.github.com/repos/kenchris/WebFundamentals/releases{/id}","deployments_url":"https://api.github.com/repos/kenchris/WebFundamentals/deployments","created_at":"2015-10-19T10:03:21Z","updated_at":"2015-10-19T10:03:52Z","pushed_at":"2016-03-11T10:08:13Z","git_url":"git://github.com/kenchris/WebFundamentals.git","ssh_url":"git@github.com:kenchris/WebFundamentals.git","clone_url":"https://github.com/kenchris/WebFundamentals.git","svn_url":"https://github.com/kenchris/WebFundamentals","homepage":"https://developers.google.com/web/fundamentals","size":978662,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"google:master","ref":"master","sha":"cf166fee96a9da2b814f2f6ad4a95163c9055eee","user":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"repo":{"id":19249835,"name":"WebFundamentals","full_name":"google/WebFundamentals","owner":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/google/WebFundamentals","description":"Best practices for modern web development","fork":false,"url":"https://api.github.com/repos/google/WebFundamentals","forks_url":"https://api.github.com/repos/google/WebFundamentals/forks","keys_url":"https://api.github.com/repos/google/WebFundamentals/keys{/key_id}","collaborators_url":"https://api.github.com/repos/google/WebFundamentals/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/google/WebFundamentals/teams","hooks_url":"https://api.github.com/repos/google/WebFundamentals/hooks","issue_events_url":"https://api.github.com/repos/google/WebFundamentals/issues/events{/number}","events_url":"https://api.github.com/repos/google/WebFundamentals/events","assignees_url":"https://api.github.com/repos/google/WebFundamentals/assignees{/user}","branches_url":"https://api.github.com/repos/google/WebFundamentals/branches{/branch}","tags_url":"https://api.github.com/repos/google/WebFundamentals/tags","blobs_url":"https://api.github.com/repos/google/WebFundamentals/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/google/WebFundamentals/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/google/WebFundamentals/git/refs{/sha}","trees_url":"https://api.github.com/repos/google/WebFundamentals/git/trees{/sha}","statuses_url":"https://api.github.com/repos/google/WebFundamentals/statuses/{sha}","languages_url":"https://api.github.com/repos/google/WebFundamentals/languages","stargazers_url":"https://api.github.com/repos/google/WebFundamentals/stargazers","contributors_url":"https://api.github.com/repos/google/WebFundamentals/contributors","subscribers_url":"https://api.github.com/repos/google/WebFundamentals/subscribers","subscription_url":"https://api.github.com/repos/google/WebFundamentals/subscription","commits_url":"https://api.github.com/repos/google/WebFundamentals/commits{/sha}","git_commits_url":"https://api.github.com/repos/google/WebFundamentals/git/commits{/sha}","comments_url":"https://api.github.com/repos/google/WebFundamentals/comments{/number}","issue_comment_url":"https://api.github.com/repos/google/WebFundamentals/issues/comments{/number}","contents_url":"https://api.github.com/repos/google/WebFundamentals/contents/{+path}","compare_url":"https://api.github.com/repos/google/WebFundamentals/compare/{base}...{head}","merges_url":"https://api.github.com/repos/google/WebFundamentals/merges","archive_url":"https://api.github.com/repos/google/WebFundamentals/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/google/WebFundamentals/downloads","issues_url":"https://api.github.com/repos/google/WebFundamentals/issues{/number}","pulls_url":"https://api.github.com/repos/google/WebFundamentals/pulls{/number}","milestones_url":"https://api.github.com/repos/google/WebFundamentals/milestones{/number}","notifications_url":"https://api.github.com/repos/google/WebFundamentals/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/google/WebFundamentals/labels{/name}","releases_url":"https://api.github.com/repos/google/WebFundamentals/releases{/id}","deployments_url":"https://api.github.com/repos/google/WebFundamentals/deployments","created_at":"2014-04-28T18:45:45Z","updated_at":"2016-03-15T05:06:23Z","pushed_at":"2016-03-15T04:29:12Z","git_url":"git://github.com/google/WebFundamentals.git","ssh_url":"git@github.com:google/WebFundamentals.git","clone_url":"https://github.com/google/WebFundamentals.git","svn_url":"https://github.com/google/WebFundamentals","homepage":"https://developers.google.com/web/fundamentals","size":984657,"stargazers_count":5314,"watchers_count":5314,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":937,"mirror_url":null,"open_issues_count":222,"forks":937,"open_issues":222,"watchers":5314,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/2655"},"html":{"href":"https://github.com/google/WebFundamentals/pull/2655"},"issue":{"href":"https://api.github.com/repos/google/WebFundamentals/issues/2655"},"comments":{"href":"https://api.github.com/repos/google/WebFundamentals/issues/2655/comments"},"review_comments":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/2655/comments"},"review_comment":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/2655/commits"},"statuses":{"href":"https://api.github.com/repos/google/WebFundamentals/statuses/9189bcf123ea985acba9590ecc175a28e0992e7d"}}}}