{"action":"created","comment":{"url":"https://api.github.com/repos/google/WebFundamentals/pulls/comments/35129859","id":35129859,"diff_hunk":"@@ -0,0 +1,279 @@\n+---\n+\n+layout: update\n+published: true\n+\n+collection: updates\n+category: chrome\n+product: chrome\n+type: news\n+date: 2015-07-22\n+\n+title: \"Access BLE devices with Web Bluetooth\"\n+description: \"A Web API has been added to Chrome OS M45 that\n+makes it possible for websites to discover and communicate with devices over\n+the Bluetooth 4 wireless standard using GATT.\"\n+article:\n+  written_on: 2015-07-22\n+  updated_on: 2015-07-22\n+\n+authors:\n+  - beaufortfrancois\n+tags:\n+  - Bluetooth\n+  - IoT\n+  - WebBluetooth\n+  - PhysicalWeb\n+permalink: /updates/2015/07/access-ble-devices-with-web-bluetooth.html\n+---\n+\n+How about I tell you websites could communicate with nearby Bluetooth devices\n+in a secure and privacy-preserving way? This way, heart rate monitors, singing\n+lightbulbs and [turtles](https://www.youtube.com/watch?v=1LV1Fk5ZXwA) could\n+be managed directly from a website.\n+\n+## Before we start\n+\n+This article assumes you have some basic knowledge of how works Bluetooth Low\n+Energy (BLE) and Generic Attribute Profile (GATT).\n+\n+Even though the [Web Bluetooth API\n+specification](https://webbluetoothcg.github.io/web-bluetooth/) is not\n+finalized yet, the Chrome Team is actively looking for enthusiastic developers\n+(I mean you) to try out this work-in-progress API and give\n+[feedback](https://code.google.com/p/chromium/issues/entry?labels=Cr-Blink-Bluetooth).\n+\n+Web Bluetooth API is at the time of writing partially implemented in Chrome OS\n+M45 behind an experimental flag. Go to `chrome://flags/#enable-web-bluetooth`,\n+enable the highlighted flag, restart Chrome and you should be able to\n+[scan](#scan-bluetooth-devices) and [connect](#connect-to-a-bluetooth-device)\n+to nearby Bluetooth devices and\n+[read](#read-a-bluetooth-characteristic)/[write](#write-to-a-bluetooth-characteristic)\n+Bluetooth characteristics.\n+\n+<img src=\"/web/updates/images/2015-07-22-access-ble-devices-with-web-bluetooth/web-bluetooth-flag.png\" alt=\"Web Bluetooth Flag highlighted in chrome://flags\"/>\n+\n+## Security Requirements\n+\n+### HTTPS Only\n+\n+Because the Web Bluetooth API is a powerful new feature added to the Web,\n+Google Chrome aims to make it [available only to secure\n+origins](https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features).\n+This means you'll need to build with TLS in mind.\n+\n+During development you'll be able to play with Web Bluetooth through\n+http://localhost, but to deploy it on a site you'll need to have HTTPS setup\n+on your server. I personally enjoy [GitHub Pages](https://pages.github.com) for\n+demo purposes.\n+To add HTTPS to your server then you'll need to get a TLS certificate and set\n+it up. Be sure to check out [Mozilla's SSL config\n+generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/) for\n+best practices there.\n+\n+### User Gesture Required\n+\n+As a security feature, discovering nearby Bluetooth devices with\n+`navigator.bluetooth.requestDevice` must be called via a user gesture\n+like a touch or mouse click.\n+\n+## Get into the Code\n+\n+The Web Bluetooth API relies heavily on JavaScript\n+[Promises](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise).\n+If you're not familiar with them, check out this great\n+[Promises tutorial](http://www.html5rocks.com/en/tutorials/es6/promises/). One\n+more thing, `() => {}` are simply ECMAScript 2015 [Arrow\n+functions](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions)\n+- they have a shorter syntax compared to function expressions and lexically\n+bind the `this` value.\n+\n+### Scan Bluetooth Devices\n+\n+This version of the Web Bluetooth API specification intends to allow websites,\n+running in the Central role, to connect to GATT Remote Servers over either a\n+Basic Rate/Enhanced Data Rate (BR/EDR) or BLE connection. It also aims to\n+support communication among devices that only implement Bluetooth 4.0 or 4.1.\n+\n+When a website requests access to nearby BLE devices using\n+`navigator.bluetooth.requestDevice`, Google Chrome will prompt user with a\n+device chooser where he can pick one device or simply cancel the request. At\n+the time of writing though, **the device chooser hasn't been implemented yet.**\n+Only the closer device that matches filters will be returned.\n+\n+The `navigator.bluetooth.requestDevice` function takes a mandatory Object that\n+define Bluetooth GATT services filters. These filters are used to return\n+only devices that advertise these services.\n+\n+For instance, scanning Bluetooth devices advertising [Bluetooth GATT Battery Service](https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.battery_service.xml) is that simple.\n+\n+{% highlight javascript %}\n+navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })\n+.then(device => {...})\n+.catch(error => { console.log(error); });\n+{% endhighlight %}\n+\n+If your Bluetooth GATT Service is not the list of [the officially adopted Bluetooth\n+GATT\n+services](https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx)\n+though, you may want to use the handy `window.BluetoothUUID.getService` helper\n+function then. Calling it with `0x1234` will return\n+`\"00001234-0000-1000-8000-00805f9b34fb\"`.\n+\n+{% highlight javascript %}\n+var myService = BluetoothUUID.getService(0x1234);\n+navigator.bluetooth.requestDevice({ filters: [{ services: [myService] }] })\n+.then(device => {...})\n+.catch(error => { console.log(error); });\n+{% endhighlight %}\n+\n+### Connect to a Bluetooth Device\n+\n+So what do you do now that you have a `BluetoothDevice` resolved by the\n+`navigator.bluetooth.requestDevice` Promise? Let's connect to the Bluetooth\n+GATT Remote Server which holds the Attribute Protocol (ATT) lookup data and\n+service and characteristic definitions.\n+\n+{% highlight javascript %}\n+navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })\n+.then(device => {\n+  // Human-readable name of the device.\n+  console.log(device.name);\n+  // Indicates whether or not the device is paired with the system.\n+  console.log(device.paired);\n+  // UUIDs of protocols, profiles and services advertised by the device.\n+  console.log(device.uuids);\n+\n+  // Attempts to connect to GATT Remote Server.\n+  return device.connectGATT();\n+})\n+.then(server => {...})\n+.catch(error => { console.log(error); });\n+{% endhighlight %}\n+\n+\n+### Read a Bluetooth Characteristic\n+\n+Here we are connected to the GATT Remote Server of the Bluetooth device. Now we\n+want to get a Primary Bluetooth GATT Service and read a characteristic that\n+belongs to this service. Let's try for instance to read the current charge\n+level of a connected device battery.\n+\n+In the example below, `battery_level` is the [adopted Bluetooth GATT\n+Characteristic Battery\n+Level](https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.battery_level.xml).\n+\n+{% highlight javascript %}\n+navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })\n+.then(device => device.connectGATT())\n+.then(server => {\n+  // Getting Battery Service...\n+  return server.getPrimaryService('battery_service');\n+})\n+.then(service => {\n+  if (!service) throw 'Battery Service not found';\n+  // Getting Battery Level Characteristic...\n+  return service.getCharacteristic('battery_level');\n+})\n+.then(characteristic => {\n+  // Reading Battery Level...\n+  return characteristic.readValue();\n+})\n+.then(buffer => {\n+  var data = new DataView(buffer);\n+  console.log('Battery percentage is ' + data.getUint8(0));\n+})\n+.catch(error => { console.log(error); });\n+{% endhighlight %}\n+\n+If you use a custom Bluetooth GATT characteristic,\n+`window.BluetoothUUID.getCharacteristic` definitely comes in handy. Calling it\n+with `0x5678` will return `\"00005678-0000-1000-8000-00805f9b34fb\"`.\n+\n+{% highlight javascript %}\n+...\n+.then(service => {\n+  var myCharacteristic = BluetoothUUID.getCharacteristic(0x5678);\n+  return service.getCharacteristic(myCharacteristic);\n+})\n+{% endhighlight %}\n+\n+### Write to a Bluetooth Characteristic\n+\n+Writing to a Bluetooth GATT Characteristic is as easy as reading it. This time,\n+let's reset the value of the Energy Expended field in the Heart Rate\n+Measurement characteristic to 0 on a heart rate monitor device.\n+\n+There is no magic there I promise. It's all explained in the [Heart Rate\n+Control Point Characteristic\n+page](https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_control_point.xml).\n+\n+{% highlight javascript %}\n+navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })\n+.then(device => device.connectGATT())\n+.then(server => server.getPrimaryService('heart_rate'))\n+.then(service => service.getCharacteristic('heart_rate_control_point'))\n+.then(characteristic => {\n+  var resetEnergyExpended = new Uint8Array([1]);\n+  return characteristic.writeValue(resetEnergyExpended);\n+})\n+.then(function(value) {\n+  console.log('Energy expended has been reset.');\n+})\n+.catch(error => { console.log(error); });\n+{% endhighlight %}\n+\n+## Samples\n+\n+Samples below have been tested on Chrome OS M45 with the Web Bluetooth flag\n+enabled. To enjoy these samples to their fullest, I recommend you install the [BLE Peripheral Simulator Android\n+App](https://play.google.com/store/apps/details?id=io.github.webbluetoothcg.bletestperipheral)\n+which simulates a BLE Peripheral with a Battery Service or a Heart Rate\n+Service.\n+\n+- Battery Level Sample - [https://googlechrome.github.io/samples/web-bluetooth](https://googlechrome.github.io/samples/web-bluetooth)\n+- Reset Energy Expended Sample - [https://scheib.github.io/bluetooth-demos/heart-rate-write.html](https://scheib.github.io/bluetooth-demos/heart-rate-write.html)","path":"src/_langs/en/updates/2015-07-22-access-ble-devices-with-web-bluetooth.markdown","position":235,"original_position":235,"commit_id":"79e3ed7a59009031c5c9a53b32fb5040eaa189e5","original_commit_id":"79e3ed7a59009031c5c9a53b32fb5040eaa189e5","user":{"login":"scheib","id":386971,"avatar_url":"https://avatars.githubusercontent.com/u/386971?v=3","gravatar_id":"","url":"https://api.github.com/users/scheib","html_url":"https://github.com/scheib","followers_url":"https://api.github.com/users/scheib/followers","following_url":"https://api.github.com/users/scheib/following{/other_user}","gists_url":"https://api.github.com/users/scheib/gists{/gist_id}","starred_url":"https://api.github.com/users/scheib/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scheib/subscriptions","organizations_url":"https://api.github.com/users/scheib/orgs","repos_url":"https://api.github.com/users/scheib/repos","events_url":"https://api.github.com/users/scheib/events{/privacy}","received_events_url":"https://api.github.com/users/scheib/received_events","type":"User","site_admin":false},"body":"@g-ortuno and I don't think these samples are in shape to share. They're definitely not best practice code right now. We think that if we want to use these samples they should get cleaned up more and just folded into googlechrome.../samples.","created_at":"2015-07-21T17:31:15Z","updated_at":"2015-07-21T17:31:15Z","html_url":"https://github.com/google/WebFundamentals/pull/1460#discussion_r35129859","pull_request_url":"https://api.github.com/repos/google/WebFundamentals/pulls/1460","_links":{"self":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/comments/35129859"},"html":{"href":"https://github.com/google/WebFundamentals/pull/1460#discussion_r35129859"},"pull_request":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/1460"}}},"pull_request":{"url":"https://api.github.com/repos/google/WebFundamentals/pulls/1460","id":40452522,"html_url":"https://github.com/google/WebFundamentals/pull/1460","diff_url":"https://github.com/google/WebFundamentals/pull/1460.diff","patch_url":"https://github.com/google/WebFundamentals/pull/1460.patch","issue_url":"https://api.github.com/repos/google/WebFundamentals/issues/1460","number":1460,"state":"open","locked":false,"title":"DO NOT MERGE YET - Added Web Bluetooth Article","user":{"login":"beaufortfrancois","id":634478,"avatar_url":"https://avatars.githubusercontent.com/u/634478?v=3","gravatar_id":"","url":"https://api.github.com/users/beaufortfrancois","html_url":"https://github.com/beaufortfrancois","followers_url":"https://api.github.com/users/beaufortfrancois/followers","following_url":"https://api.github.com/users/beaufortfrancois/following{/other_user}","gists_url":"https://api.github.com/users/beaufortfrancois/gists{/gist_id}","starred_url":"https://api.github.com/users/beaufortfrancois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/beaufortfrancois/subscriptions","organizations_url":"https://api.github.com/users/beaufortfrancois/orgs","repos_url":"https://api.github.com/users/beaufortfrancois/repos","events_url":"https://api.github.com/users/beaufortfrancois/events{/privacy}","received_events_url":"https://api.github.com/users/beaufortfrancois/received_events","type":"User","site_admin":false},"body":"Hello @scheib,\r\nCan you review this Web Bluetooth article?\r\nIt is going to be published at https://developers.google.com/web/updates/2015/07/access-ble-devices-with-web-bluetooth\r\n","created_at":"2015-07-21T12:42:29Z","updated_at":"2015-07-21T17:31:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a7bcd4d8743b0240f9578b8d75233316b9eb4ae7","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/google/WebFundamentals/pulls/1460/commits","review_comments_url":"https://api.github.com/repos/google/WebFundamentals/pulls/1460/comments","review_comment_url":"https://api.github.com/repos/google/WebFundamentals/pulls/comments{/number}","comments_url":"https://api.github.com/repos/google/WebFundamentals/issues/1460/comments","statuses_url":"https://api.github.com/repos/google/WebFundamentals/statuses/79e3ed7a59009031c5c9a53b32fb5040eaa189e5","head":{"label":"beaufortfrancois:web-bluetooth","ref":"web-bluetooth","sha":"79e3ed7a59009031c5c9a53b32fb5040eaa189e5","user":{"login":"beaufortfrancois","id":634478,"avatar_url":"https://avatars.githubusercontent.com/u/634478?v=3","gravatar_id":"","url":"https://api.github.com/users/beaufortfrancois","html_url":"https://github.com/beaufortfrancois","followers_url":"https://api.github.com/users/beaufortfrancois/followers","following_url":"https://api.github.com/users/beaufortfrancois/following{/other_user}","gists_url":"https://api.github.com/users/beaufortfrancois/gists{/gist_id}","starred_url":"https://api.github.com/users/beaufortfrancois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/beaufortfrancois/subscriptions","organizations_url":"https://api.github.com/users/beaufortfrancois/orgs","repos_url":"https://api.github.com/users/beaufortfrancois/repos","events_url":"https://api.github.com/users/beaufortfrancois/events{/privacy}","received_events_url":"https://api.github.com/users/beaufortfrancois/received_events","type":"User","site_admin":false},"repo":{"id":39246354,"name":"WebFundamentals","full_name":"beaufortfrancois/WebFundamentals","owner":{"login":"beaufortfrancois","id":634478,"avatar_url":"https://avatars.githubusercontent.com/u/634478?v=3","gravatar_id":"","url":"https://api.github.com/users/beaufortfrancois","html_url":"https://github.com/beaufortfrancois","followers_url":"https://api.github.com/users/beaufortfrancois/followers","following_url":"https://api.github.com/users/beaufortfrancois/following{/other_user}","gists_url":"https://api.github.com/users/beaufortfrancois/gists{/gist_id}","starred_url":"https://api.github.com/users/beaufortfrancois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/beaufortfrancois/subscriptions","organizations_url":"https://api.github.com/users/beaufortfrancois/orgs","repos_url":"https://api.github.com/users/beaufortfrancois/repos","events_url":"https://api.github.com/users/beaufortfrancois/events{/privacy}","received_events_url":"https://api.github.com/users/beaufortfrancois/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/beaufortfrancois/WebFundamentals","description":"Best practices for modern web development","fork":true,"url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals","forks_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/forks","keys_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/keys{/key_id}","collaborators_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/teams","hooks_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/hooks","issue_events_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/issues/events{/number}","events_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/events","assignees_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/assignees{/user}","branches_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/branches{/branch}","tags_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/tags","blobs_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/git/refs{/sha}","trees_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/git/trees{/sha}","statuses_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/statuses/{sha}","languages_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/languages","stargazers_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/stargazers","contributors_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/contributors","subscribers_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/subscribers","subscription_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/subscription","commits_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/commits{/sha}","git_commits_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/git/commits{/sha}","comments_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/comments{/number}","issue_comment_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/issues/comments{/number}","contents_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/contents/{+path}","compare_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/compare/{base}...{head}","merges_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/merges","archive_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/downloads","issues_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/issues{/number}","pulls_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/pulls{/number}","milestones_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/milestones{/number}","notifications_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/labels{/name}","releases_url":"https://api.github.com/repos/beaufortfrancois/WebFundamentals/releases{/id}","created_at":"2015-07-17T09:29:28Z","updated_at":"2015-07-17T09:29:37Z","pushed_at":"2015-07-21T12:59:55Z","git_url":"git://github.com/beaufortfrancois/WebFundamentals.git","ssh_url":"git@github.com:beaufortfrancois/WebFundamentals.git","clone_url":"https://github.com/beaufortfrancois/WebFundamentals.git","svn_url":"https://github.com/beaufortfrancois/WebFundamentals","homepage":"https://developers.google.com/web/fundamentals","size":614825,"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":"af46f5bf57823de3e60ec983b81b1032a6c4dc02","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}","created_at":"2014-04-28T18:45:45Z","updated_at":"2015-07-21T15:12:55Z","pushed_at":"2015-07-21T17:27:55Z","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":627260,"stargazers_count":3807,"watchers_count":3807,"language":"HTML","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":598,"mirror_url":null,"open_issues_count":186,"forks":598,"open_issues":186,"watchers":3807,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/1460"},"html":{"href":"https://github.com/google/WebFundamentals/pull/1460"},"issue":{"href":"https://api.github.com/repos/google/WebFundamentals/issues/1460"},"comments":{"href":"https://api.github.com/repos/google/WebFundamentals/issues/1460/comments"},"review_comments":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/1460/comments"},"review_comment":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/google/WebFundamentals/pulls/1460/commits"},"statuses":{"href":"https://api.github.com/repos/google/WebFundamentals/statuses/79e3ed7a59009031c5c9a53b32fb5040eaa189e5"}}}}