{"action":"created","comment":{"url":"https://api.github.com/repos/openhab/openhab/pulls/comments/30650202","id":30650202,"diff_hunk":"@@ -0,0 +1,559 @@\n+/**\n+ * Copyright (c) 2010-2015, openHAB.org and others.\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * which accompanies this distribution, and is available at\n+ * http://www.eclipse.org/legal/epl-v10.html\n+ */\n+\n+package org.openhab.io.myopenhab.internal;\n+\n+import java.io.IOException;\n+import java.net.URI;\n+import java.net.URISyntaxException;\n+import java.net.URLEncoder;\n+import java.util.HashMap;\n+import java.util.Iterator;\n+import java.util.Map;\n+\n+import org.eclipse.jetty.client.ContentExchange;\n+import org.eclipse.jetty.client.HttpClient;\n+import org.eclipse.jetty.io.Buffer;\n+import org.eclipse.jetty.io.ByteArrayBuffer;\n+import org.eclipse.jetty.util.URIUtil;\n+import org.json.JSONException;\n+import org.json.JSONObject;\n+import org.slf4j.Logger;\n+import org.slf4j.LoggerFactory;\n+\n+import com.github.nkzawa.emitter.Emitter;\n+import com.github.nkzawa.engineio.client.Transport;\n+import com.github.nkzawa.socketio.client.IO;\n+import com.github.nkzawa.socketio.client.Manager;\n+import com.github.nkzawa.socketio.client.Socket;\n+\n+/** \n+ * This class provides communication between openHAB and my.openHAB service.\n+ * It also implements async http proxy for serving requests from user to\n+ * openHAB through my.openHAB. It uses Socket.IO connection to connect to\n+ * my.openHAB service and Jetty Http client to send local http requests to\n+ * openHAB.\n+ * \n+ * @author Victor Belov\n+ * @since 1.3.0\n+ *\n+ */\n+\n+public class MyOHClient {\n+\t// Logger\n+\tprivate static Logger logger = LoggerFactory.getLogger(MyOHClient.class);\n+\t// Base URL for my.openhab service\n+\tprivate String mMyOHBaseUrl = \"https://my.openhab.org/\";\n+\t// openHAB UUID\n+\tprivate String mUUID;\n+\t// openHAB Secret\n+\tprivate String mSecret;\n+\t// local openHAB base URL\n+\tprivate String mOHBaseUrl = \"http://localhost:8080\";\n+\t// Http client for issuing local requests to openHAB\n+\tprivate HttpClient mJettyClient;\n+\t// HashMap of currently running HTTP requests to openHAB\n+\tprivate HashMap<Integer, MyOHExchange> mRunningRequests;\n+\t// Active connection flag\n+\tprivate boolean mIsConnected;\n+\t// Version of openHAB\n+\tprivate String mOpenHABVersion;\n+\t// Socket.io\n+\tprivate Socket mSocket;\n+\t// MyOHClient listener\n+\tprivate MyOHClientListener mListener;\n+\n+\t/**\n+\t * Constructor of MyOHClient\n+\t * \n+\t * @param uuid openHAB's UUID to connect to my.openHAB\n+\t * @param secret openHAB's Secret to connect to my.openHAB\n+\t * \n+\t */\n+\n+\tpublic MyOHClient(String uuid, String secret) {\n+\t\tmUUID = uuid;\n+\t\tmSecret = secret;\n+\t\tmRunningRequests = new HashMap<Integer, MyOHExchange>();\n+\t\tmJettyClient = new HttpClient();\n+\t\tmJettyClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);\n+\t\tmJettyClient.setMaxConnectionsPerAddress(200);\n+\t\tmJettyClient.setTimeout(30000);\n+\t\ttry {\n+\t\t\tmJettyClient.start();\n+\t\t} catch (Exception e) {\n+\t\t\tlogger.error(\"Error starting JettyClient: {}\", e.getMessage());\n+\t\t}\n+\t}\n+\t\n+\t/**\n+\t * Connect to my.openHAB\n+\t */\n+\t\n+\tpublic void connect() {\n+\t\ttry {\n+\t\t\tmSocket = IO.socket(mMyOHBaseUrl);\n+\t\t\tmSocket.io().on(Manager.EVENT_TRANSPORT, \n+\t\t\t\tnew Emitter.Listener() {\n+\t\t\t\t\t@Override\n+\t\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\t\tlogger.debug(\"Manager.EVENT_TRANSPORT\");\n+\t\t\t\t\t\tTransport transport = (Transport)args[0];\n+\t\t\t\t\t\ttransport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {\n+\t\t\t\t\t\t\t@Override\n+\t\t\t\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\t\t\t\tlogger.debug(\"Transport.EVENT_REQUEST_HEADERS\");\n+\t\t\t\t\t\t\t\tMap<String, String> headers = (Map<String, String>) args[0];\n+\t\t\t\t\t\t\t\theaders.put(\"uuid\", mUUID);\n+\t\t\t\t\t\t\t\theaders.put(\"secret\", mSecret);\n+\t\t\t\t\t\t\t\theaders.put(\"openhabversion\", mOpenHABVersion);\n+\t\t\t\t\t\t\t\theaders.put(\"myohversion\", MyOpenHABServiceImpl.myohVersion);\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t});\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t);\n+\t\t\tmSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {\n+\t\t\t\t@Override\n+\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\tlogger.debug(\"Socket.IO connected\");\n+\t\t\t\t\tmIsConnected = true;\n+\t\t\t\t\tonConnect();\n+\t\t\t\t}\n+\t\t\t}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {\n+\t\t\t\t@Override\n+\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\tlogger.debug(\"Socket.IO disconnected\");\n+\t\t\t\t\tmIsConnected = false;\n+\t\t\t\t\tonDisconnect();\n+\t\t\t\t}\n+\t\t\t}).on(Socket.EVENT_ERROR, new Emitter.Listener() {\n+\t\t\t\t\t\t\t\t@Override\n+\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\tlogger.error(\"Socket.IO error: \" + args[0]);\n+\t\t\t\t}\n+\t\t\t}).on(\"request\", new Emitter.Listener() {\n+\t\t\t\t@Override\n+\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\tonEvent(\"request\", (JSONObject)args[0]);\n+\t\t\t\t}\n+\t\t\t}).on(\"cancel\", new Emitter.Listener() {\n+\t\t\t\t@Override\n+\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\tonEvent(\"cancel\", (JSONObject)args[0]);\n+\t\t\t\t}\n+\t\t\t}).on(\"command\", new Emitter.Listener() {\n+\t\t\t\t\n+\t\t\t\t@Override\n+\t\t\t\tpublic void call(Object... args) {\n+\t\t\t\t\tonEvent(\"command\", (JSONObject)args[0]);\n+\t\t\t\t}\n+\t\t\t});\n+\t\t\tmSocket.connect();\n+\t\t} catch (URISyntaxException e) {\n+\t\t\tlogger.error(e.getMessage());\n+\t\t}\n+\t}\n+\t\n+\t/**\n+\t * Callback method for socket.io client which is called when connection is established\n+\t */\n+\t\n+\tpublic void onConnect() {\n+\t\tlogger.info(\"Connected to my.openHAB service\");\n+\t\tmIsConnected = true;\n+\t}\n+\n+\t/**\n+\t * Callback method for socket.io client which is called when disconnect occurs\n+\t */\n+\t\n+\tpublic void onDisconnect() {\n+\t\tlogger.info(\"Disconnected from my.openHAB service\");\t\t\n+\t\tmIsConnected = false;\n+\t}\n+\n+\t/**\n+\t * Callback method for socket.io client which is called when an error occurs\n+\t */\n+\n+\tpublic void onError(IOException error) {\n+\t\tlogger.error(error.getMessage());\n+\t}\n+\t\n+\t/**\n+\t * Callback method for socket.io client which is called when a message is received\n+\t */\n+\n+\tpublic void onEvent(String event, JSONObject data) {\n+\t\t// TODO Auto-generated method stub\n+\t\tlogger.debug(\"on(): \" + event);\n+\t\tif (event.equals(\"request\")) {\n+\t\t\ttry {\n+\t\t\t\t// Get myOH uniq request Id\n+\t\t\t\tint requestId = data.getInt(\"id\");\n+\t\t\t\tlogger.debug(String.format(\"Got request %d\", requestId));\n+\t\t\t\t// Get request path\n+\t\t\t\tString requestPath = data.getString(\"path\");\n+\t\t\t\t// Get request method\n+\t\t\t\tString requestMethod = data.getString(\"method\");\n+\t\t\t\t// Get request body\n+\t\t\t\tString requestBody = data.getString(\"body\");\n+\t\t\t\t// Get JSONObject for request headers\n+\t\t\t\tJSONObject requestHeadersJson = data.getJSONObject(\"headers\");\n+\t\t\t\tlogger.debug(requestHeadersJson.toString());\n+\t\t\t\t// Get JSONObject for request query parameters\n+\t\t\t\tJSONObject requestQueryJson = data.getJSONObject(\"query\");\n+\t\t\t\tIterator<String> headersIterator = requestHeadersJson.keys();\n+\t\t\t\t// Create URI builder with base request URI of openHAB and path from request\n+\t\t\t\tString newPath = URIUtil.addPaths(mOHBaseUrl, requestPath);\n+\t\t\t\tIterator<String> queryIterator = requestQueryJson.keys();\n+\t\t\t\t// Add query parameters to URI builder, if any\n+\t\t\t\tnewPath +=\"?\";\n+\t\t\t\twhile (queryIterator.hasNext()) {\n+\t\t\t\t\tString queryName = queryIterator.next();\n+\t\t\t\t\tnewPath += queryName;\n+\t\t\t\t\tnewPath += \"=\";\n+\t\t\t\t\tnewPath += URLEncoder.encode(requestQueryJson.getString(queryName), \"UTF-8\");\n+\t\t\t\t\tif (queryIterator.hasNext())\n+\t\t\t\t\t\tnewPath += \"&\";\n+\t\t\t\t}\n+\t\t\t\t// Finally get the future request URI\n+\t\t\t\tURI requestUri = new URI(newPath);\n+\t\t\t\t// All preparations which are common for different methods are done\n+\t\t\t\t// Now perform the request to openHAB\n+\t\t\t\t// If method is GET\n+\t\t\t\tlogger.debug(\"Request method is \" + requestMethod);\n+\t\t\t\tMyOHExchange exchange = new MyOHExchange(requestId);\n+\t\t\t\texchange.setURI(requestUri);\n+\t\t\t\texchange.setRequestHeaders(requestHeadersJson);\n+\t\t\t\texchange.setRequestHeader(\"X-Forwarded-Proto\", \"https\");\n+\t\t\t\tif (requestMethod.equals(\"GET\")) {\n+\t\t\t\t\texchange.setMethod(\"GET\");\n+\t\t\t\t} else if (requestMethod.equals(\"POST\")) {\n+\t\t\t\t\texchange.setMethod(\"POST\");\n+\t\t\t\t\tBuffer requestContent = new ByteArrayBuffer(requestBody);\n+\t\t\t\t\texchange.setRequestContent(requestContent);\n+\t\t\t\t} else if (requestMethod.equals(\"PUT\")) {\n+\t\t\t\t\texchange.setMethod(\"PUT\");\n+\t\t\t\t\tBuffer requestContent = new ByteArrayBuffer(requestBody);\n+\t\t\t\t\texchange.setRequestContent(requestContent);\n+\t\t\t\t} else {\n+\t\t\t\t\t// TODO: Reject unsupported methods\n+\t\t\t\t\tlogger.error(\"Unsupported request method \" + requestMethod);\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\t\t\t\tmJettyClient.send(exchange);\n+\t\t\t\t// If successfully submitted request to http client, add it to the list of currently\n+\t\t\t\t// running requests to be able to cancel it if needed\n+\t\t\t\tmRunningRequests.put(requestId, exchange);\n+\t\t\t} catch (JSONException e) {\n+\t\t\t\tlogger.error(e.getMessage());\n+\t\t\t} catch (IOException e) {\n+\t\t\t\tlogger.error(e.getMessage());\n+\t\t\t} catch (URISyntaxException e) {\n+\t\t\t\tlogger.error(e.getMessage());\n+\t\t\t}\n+\t\t} else if (event.equals(\"cancel\")) {\n+\t\t\ttry {\n+\t\t\t\tint requestId = data.getInt(\"id\");\n+\t\t\t\tlogger.debug(String.format(\"Received cancel for request %d\", requestId));\n+\t\t\t\t// Find and cancel running request\n+\t\t\t\tif (mRunningRequests.containsKey(requestId)) {\n+\t\t\t\t\tMyOHExchange requestExchange = mRunningRequests.get(requestId);\n+\t\t\t\t\trequestExchange.cancel();\n+\t\t\t\t\tmRunningRequests.remove(requestId);\n+\t\t\t\t}\n+\t\t\t} catch (JSONException e) {\n+\t\t\t\tlogger.error(e.getMessage());\n+\t\t\t}\n+\t\t} else if (event.equals(\"command\")) {\n+\t\t\ttry {\n+\t\t\t\tlogger.debug(\"Received command \" + data.getString(\"command\") + \" for item \" + data.getString(\"item\"));\n+\t\t\t\tif (this.mListener != null)\n+\t\t\t\t\tthis.mListener.sendCommand(data.getString(\"item\"), data.getString(\"command\"));\n+\t\t\t} catch (JSONException e) {\n+\t\t\t\tlogger.error(e.getMessage());\n+\t\t\t}\n+\t\t} else {\n+\t\t\tlogger.warn(\"Unsopported event from my.openHAB: \" + event);","path":"bundles/io/org.openhab.io.myopenhab/src/main/java/org/openhab/io/myopenhab/internal/MyOHClient.java","position":285,"original_position":285,"commit_id":"06f58ede47217aa02485cda950c209ceb0bcdf63","original_commit_id":"06f58ede47217aa02485cda950c209ceb0bcdf63","user":{"login":"teichsta","id":2108636,"avatar_url":"https://avatars.githubusercontent.com/u/2108636?v=3","gravatar_id":"","url":"https://api.github.com/users/teichsta","html_url":"https://github.com/teichsta","followers_url":"https://api.github.com/users/teichsta/followers","following_url":"https://api.github.com/users/teichsta/following{/other_user}","gists_url":"https://api.github.com/users/teichsta/gists{/gist_id}","starred_url":"https://api.github.com/users/teichsta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/teichsta/subscriptions","organizations_url":"https://api.github.com/users/teichsta/orgs","repos_url":"https://api.github.com/users/teichsta/repos","events_url":"https://api.github.com/users/teichsta/events{/privacy}","received_events_url":"https://api.github.com/users/teichsta/received_events","type":"User","site_admin":false},"body":"type \"Unsupported\"","created_at":"2015-05-19T21:35:45Z","updated_at":"2015-05-19T21:35:45Z","html_url":"https://github.com/openhab/openhab/pull/2547#discussion_r30650202","pull_request_url":"https://api.github.com/repos/openhab/openhab/pulls/2547","_links":{"self":{"href":"https://api.github.com/repos/openhab/openhab/pulls/comments/30650202"},"html":{"href":"https://github.com/openhab/openhab/pull/2547#discussion_r30650202"},"pull_request":{"href":"https://api.github.com/repos/openhab/openhab/pulls/2547"}}},"pull_request":{"url":"https://api.github.com/repos/openhab/openhab/pulls/2547","id":34673967,"html_url":"https://github.com/openhab/openhab/pull/2547","diff_url":"https://github.com/openhab/openhab/pull/2547.diff","patch_url":"https://github.com/openhab/openhab/pull/2547.patch","issue_url":"https://api.github.com/repos/openhab/openhab/issues/2547","number":2547,"state":"open","locked":false,"title":"Initial contribution of my.openHAB bundle","user":{"login":"belovictor","id":1630396,"avatar_url":"https://avatars.githubusercontent.com/u/1630396?v=3","gravatar_id":"","url":"https://api.github.com/users/belovictor","html_url":"https://github.com/belovictor","followers_url":"https://api.github.com/users/belovictor/followers","following_url":"https://api.github.com/users/belovictor/following{/other_user}","gists_url":"https://api.github.com/users/belovictor/gists{/gist_id}","starred_url":"https://api.github.com/users/belovictor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/belovictor/subscriptions","organizations_url":"https://api.github.com/users/belovictor/orgs","repos_url":"https://api.github.com/users/belovictor/repos","events_url":"https://api.github.com/users/belovictor/events{/privacy}","received_events_url":"https://api.github.com/users/belovictor/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-05-04T20:52:35Z","updated_at":"2015-05-19T21:35:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"365e6f0af1b31dee6eedbbe781a1f157cf3ebd59","assignee":{"login":"teichsta","id":2108636,"avatar_url":"https://avatars.githubusercontent.com/u/2108636?v=3","gravatar_id":"","url":"https://api.github.com/users/teichsta","html_url":"https://github.com/teichsta","followers_url":"https://api.github.com/users/teichsta/followers","following_url":"https://api.github.com/users/teichsta/following{/other_user}","gists_url":"https://api.github.com/users/teichsta/gists{/gist_id}","starred_url":"https://api.github.com/users/teichsta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/teichsta/subscriptions","organizations_url":"https://api.github.com/users/teichsta/orgs","repos_url":"https://api.github.com/users/teichsta/repos","events_url":"https://api.github.com/users/teichsta/events{/privacy}","received_events_url":"https://api.github.com/users/teichsta/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/openhab/openhab/milestones/7","html_url":"https://github.com/openhab/openhab/milestones/1.7.0","labels_url":"https://api.github.com/repos/openhab/openhab/milestones/7/labels","id":865837,"number":7,"title":"1.7.0","description":"","creator":{"login":"teichsta","id":2108636,"avatar_url":"https://avatars.githubusercontent.com/u/2108636?v=3","gravatar_id":"","url":"https://api.github.com/users/teichsta","html_url":"https://github.com/teichsta","followers_url":"https://api.github.com/users/teichsta/followers","following_url":"https://api.github.com/users/teichsta/following{/other_user}","gists_url":"https://api.github.com/users/teichsta/gists{/gist_id}","starred_url":"https://api.github.com/users/teichsta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/teichsta/subscriptions","organizations_url":"https://api.github.com/users/teichsta/orgs","repos_url":"https://api.github.com/users/teichsta/repos","events_url":"https://api.github.com/users/teichsta/events{/privacy}","received_events_url":"https://api.github.com/users/teichsta/received_events","type":"User","site_admin":false},"open_issues":30,"closed_issues":249,"state":"open","created_at":"2014-11-13T22:14:40Z","updated_at":"2015-05-19T18:33:38Z","due_on":"2015-05-23T22:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/openhab/openhab/pulls/2547/commits","review_comments_url":"https://api.github.com/repos/openhab/openhab/pulls/2547/comments","review_comment_url":"https://api.github.com/repos/openhab/openhab/pulls/comments{/number}","comments_url":"https://api.github.com/repos/openhab/openhab/issues/2547/comments","statuses_url":"https://api.github.com/repos/openhab/openhab/statuses/06f58ede47217aa02485cda950c209ceb0bcdf63","head":{"label":"belovictor:master","ref":"master","sha":"06f58ede47217aa02485cda950c209ceb0bcdf63","user":{"login":"belovictor","id":1630396,"avatar_url":"https://avatars.githubusercontent.com/u/1630396?v=3","gravatar_id":"","url":"https://api.github.com/users/belovictor","html_url":"https://github.com/belovictor","followers_url":"https://api.github.com/users/belovictor/followers","following_url":"https://api.github.com/users/belovictor/following{/other_user}","gists_url":"https://api.github.com/users/belovictor/gists{/gist_id}","starred_url":"https://api.github.com/users/belovictor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/belovictor/subscriptions","organizations_url":"https://api.github.com/users/belovictor/orgs","repos_url":"https://api.github.com/users/belovictor/repos","events_url":"https://api.github.com/users/belovictor/events{/privacy}","received_events_url":"https://api.github.com/users/belovictor/received_events","type":"User","site_admin":false},"repo":{"id":35056834,"name":"openhab","full_name":"belovictor/openhab","owner":{"login":"belovictor","id":1630396,"avatar_url":"https://avatars.githubusercontent.com/u/1630396?v=3","gravatar_id":"","url":"https://api.github.com/users/belovictor","html_url":"https://github.com/belovictor","followers_url":"https://api.github.com/users/belovictor/followers","following_url":"https://api.github.com/users/belovictor/following{/other_user}","gists_url":"https://api.github.com/users/belovictor/gists{/gist_id}","starred_url":"https://api.github.com/users/belovictor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/belovictor/subscriptions","organizations_url":"https://api.github.com/users/belovictor/orgs","repos_url":"https://api.github.com/users/belovictor/repos","events_url":"https://api.github.com/users/belovictor/events{/privacy}","received_events_url":"https://api.github.com/users/belovictor/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/belovictor/openhab","description":"Source code of the open Home Automation Bus (openHAB)","fork":true,"url":"https://api.github.com/repos/belovictor/openhab","forks_url":"https://api.github.com/repos/belovictor/openhab/forks","keys_url":"https://api.github.com/repos/belovictor/openhab/keys{/key_id}","collaborators_url":"https://api.github.com/repos/belovictor/openhab/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/belovictor/openhab/teams","hooks_url":"https://api.github.com/repos/belovictor/openhab/hooks","issue_events_url":"https://api.github.com/repos/belovictor/openhab/issues/events{/number}","events_url":"https://api.github.com/repos/belovictor/openhab/events","assignees_url":"https://api.github.com/repos/belovictor/openhab/assignees{/user}","branches_url":"https://api.github.com/repos/belovictor/openhab/branches{/branch}","tags_url":"https://api.github.com/repos/belovictor/openhab/tags","blobs_url":"https://api.github.com/repos/belovictor/openhab/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/belovictor/openhab/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/belovictor/openhab/git/refs{/sha}","trees_url":"https://api.github.com/repos/belovictor/openhab/git/trees{/sha}","statuses_url":"https://api.github.com/repos/belovictor/openhab/statuses/{sha}","languages_url":"https://api.github.com/repos/belovictor/openhab/languages","stargazers_url":"https://api.github.com/repos/belovictor/openhab/stargazers","contributors_url":"https://api.github.com/repos/belovictor/openhab/contributors","subscribers_url":"https://api.github.com/repos/belovictor/openhab/subscribers","subscription_url":"https://api.github.com/repos/belovictor/openhab/subscription","commits_url":"https://api.github.com/repos/belovictor/openhab/commits{/sha}","git_commits_url":"https://api.github.com/repos/belovictor/openhab/git/commits{/sha}","comments_url":"https://api.github.com/repos/belovictor/openhab/comments{/number}","issue_comment_url":"https://api.github.com/repos/belovictor/openhab/issues/comments{/number}","contents_url":"https://api.github.com/repos/belovictor/openhab/contents/{+path}","compare_url":"https://api.github.com/repos/belovictor/openhab/compare/{base}...{head}","merges_url":"https://api.github.com/repos/belovictor/openhab/merges","archive_url":"https://api.github.com/repos/belovictor/openhab/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/belovictor/openhab/downloads","issues_url":"https://api.github.com/repos/belovictor/openhab/issues{/number}","pulls_url":"https://api.github.com/repos/belovictor/openhab/pulls{/number}","milestones_url":"https://api.github.com/repos/belovictor/openhab/milestones{/number}","notifications_url":"https://api.github.com/repos/belovictor/openhab/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/belovictor/openhab/labels{/name}","releases_url":"https://api.github.com/repos/belovictor/openhab/releases{/id}","created_at":"2015-05-04T20:11:50Z","updated_at":"2015-05-04T20:51:58Z","pushed_at":"2015-05-18T22:56:23Z","git_url":"git://github.com/belovictor/openhab.git","ssh_url":"git@github.com:belovictor/openhab.git","clone_url":"https://github.com/belovictor/openhab.git","svn_url":"https://github.com/belovictor/openhab","homepage":null,"size":160840,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"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":0,"default_branch":"master"}},"base":{"label":"openhab:master","ref":"master","sha":"6ef88a75e30b8900c3e4311de939884fa2101dfd","user":{"login":"openhab","id":1007353,"avatar_url":"https://avatars.githubusercontent.com/u/1007353?v=3","gravatar_id":"","url":"https://api.github.com/users/openhab","html_url":"https://github.com/openhab","followers_url":"https://api.github.com/users/openhab/followers","following_url":"https://api.github.com/users/openhab/following{/other_user}","gists_url":"https://api.github.com/users/openhab/gists{/gist_id}","starred_url":"https://api.github.com/users/openhab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openhab/subscriptions","organizations_url":"https://api.github.com/users/openhab/orgs","repos_url":"https://api.github.com/users/openhab/repos","events_url":"https://api.github.com/users/openhab/events{/privacy}","received_events_url":"https://api.github.com/users/openhab/received_events","type":"Organization","site_admin":false},"repo":{"id":12983151,"name":"openhab","full_name":"openhab/openhab","owner":{"login":"openhab","id":1007353,"avatar_url":"https://avatars.githubusercontent.com/u/1007353?v=3","gravatar_id":"","url":"https://api.github.com/users/openhab","html_url":"https://github.com/openhab","followers_url":"https://api.github.com/users/openhab/followers","following_url":"https://api.github.com/users/openhab/following{/other_user}","gists_url":"https://api.github.com/users/openhab/gists{/gist_id}","starred_url":"https://api.github.com/users/openhab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openhab/subscriptions","organizations_url":"https://api.github.com/users/openhab/orgs","repos_url":"https://api.github.com/users/openhab/repos","events_url":"https://api.github.com/users/openhab/events{/privacy}","received_events_url":"https://api.github.com/users/openhab/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/openhab/openhab","description":"Source code of the open Home Automation Bus (openHAB)","fork":false,"url":"https://api.github.com/repos/openhab/openhab","forks_url":"https://api.github.com/repos/openhab/openhab/forks","keys_url":"https://api.github.com/repos/openhab/openhab/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openhab/openhab/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openhab/openhab/teams","hooks_url":"https://api.github.com/repos/openhab/openhab/hooks","issue_events_url":"https://api.github.com/repos/openhab/openhab/issues/events{/number}","events_url":"https://api.github.com/repos/openhab/openhab/events","assignees_url":"https://api.github.com/repos/openhab/openhab/assignees{/user}","branches_url":"https://api.github.com/repos/openhab/openhab/branches{/branch}","tags_url":"https://api.github.com/repos/openhab/openhab/tags","blobs_url":"https://api.github.com/repos/openhab/openhab/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openhab/openhab/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openhab/openhab/git/refs{/sha}","trees_url":"https://api.github.com/repos/openhab/openhab/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openhab/openhab/statuses/{sha}","languages_url":"https://api.github.com/repos/openhab/openhab/languages","stargazers_url":"https://api.github.com/repos/openhab/openhab/stargazers","contributors_url":"https://api.github.com/repos/openhab/openhab/contributors","subscribers_url":"https://api.github.com/repos/openhab/openhab/subscribers","subscription_url":"https://api.github.com/repos/openhab/openhab/subscription","commits_url":"https://api.github.com/repos/openhab/openhab/commits{/sha}","git_commits_url":"https://api.github.com/repos/openhab/openhab/git/commits{/sha}","comments_url":"https://api.github.com/repos/openhab/openhab/comments{/number}","issue_comment_url":"https://api.github.com/repos/openhab/openhab/issues/comments{/number}","contents_url":"https://api.github.com/repos/openhab/openhab/contents/{+path}","compare_url":"https://api.github.com/repos/openhab/openhab/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openhab/openhab/merges","archive_url":"https://api.github.com/repos/openhab/openhab/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openhab/openhab/downloads","issues_url":"https://api.github.com/repos/openhab/openhab/issues{/number}","pulls_url":"https://api.github.com/repos/openhab/openhab/pulls{/number}","milestones_url":"https://api.github.com/repos/openhab/openhab/milestones{/number}","notifications_url":"https://api.github.com/repos/openhab/openhab/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openhab/openhab/labels{/name}","releases_url":"https://api.github.com/repos/openhab/openhab/releases{/id}","created_at":"2013-09-20T20:28:43Z","updated_at":"2015-05-19T14:11:38Z","pushed_at":"2015-05-19T18:33:38Z","git_url":"git://github.com/openhab/openhab.git","ssh_url":"git@github.com:openhab/openhab.git","clone_url":"https://github.com/openhab/openhab.git","svn_url":"https://github.com/openhab/openhab","homepage":null,"size":233469,"stargazers_count":1365,"watchers_count":1365,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":798,"mirror_url":null,"open_issues_count":456,"forks":798,"open_issues":456,"watchers":1365,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/openhab/openhab/pulls/2547"},"html":{"href":"https://github.com/openhab/openhab/pull/2547"},"issue":{"href":"https://api.github.com/repos/openhab/openhab/issues/2547"},"comments":{"href":"https://api.github.com/repos/openhab/openhab/issues/2547/comments"},"review_comments":{"href":"https://api.github.com/repos/openhab/openhab/pulls/2547/comments"},"review_comment":{"href":"https://api.github.com/repos/openhab/openhab/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/openhab/openhab/pulls/2547/commits"},"statuses":{"href":"https://api.github.com/repos/openhab/openhab/statuses/06f58ede47217aa02485cda950c209ceb0bcdf63"}}}}