{"action":"created","issue":{"url":"https://api.github.com/repos/nfarina/homebridge/issues/136","labels_url":"https://api.github.com/repos/nfarina/homebridge/issues/136/labels{/name}","comments_url":"https://api.github.com/repos/nfarina/homebridge/issues/136/comments","events_url":"https://api.github.com/repos/nfarina/homebridge/issues/136/events","html_url":"https://github.com/nfarina/homebridge/issues/136","id":104535060,"number":136,"title":"Liftmaster URI error","user":{"login":"shawc","id":2029999,"avatar_url":"https://avatars.githubusercontent.com/u/2029999?v=3","gravatar_id":"","url":"https://api.github.com/users/shawc","html_url":"https://github.com/shawc","followers_url":"https://api.github.com/users/shawc/followers","following_url":"https://api.github.com/users/shawc/following{/other_user}","gists_url":"https://api.github.com/users/shawc/gists{/gist_id}","starred_url":"https://api.github.com/users/shawc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shawc/subscriptions","organizations_url":"https://api.github.com/users/shawc/orgs","repos_url":"https://api.github.com/users/shawc/repos","events_url":"https://api.github.com/users/shawc/events{/privacy}","received_events_url":"https://api.github.com/users/shawc/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-09-02T17:43:23Z","updated_at":"2015-10-06T03:50:24Z","closed_at":null,"body":"I have the wink platform working with several lights so i can say \"Hey Siri, turn off front door light\" and it works - which is amazing! I added a garage door opener this weekend and i am able to get the wink app to open/close the garage door when i press the button. However, when i say \"Hey Sire, open the garage door\" i get the following error:\r\n\r\nError 'null' logging in: {\"Message\":\"No HTTP resource was found that matches the request URI 'http://myqexternal.myqdevice.com/api/user/validatewithculture?appId=JVM%2FG9Nwih5BwKgNCjLxiFUQxQijAebyyg8QUHr7JOrP%2BtuPb8iHfRHKwTmDzHOu&culture=en'.\"}\r\n\r\n\r\nAlso, I only have one garage door.\r\n\r\nConfig.json\r\n{\r\n\"bridge\": {\r\n\"name\": \"Homebridge\",\r\n\"username\": \"CC:22:3D:E3:CE:30\",\r\n\"port\": 51826,\r\n\"pin\": \"031-45-154\"\r\n},\r\n\r\n\"platforms\": [\r\n    {\r\n        \"platform\": \"Wink\",\r\n        \"name\": \"Wink\",\r\n        \"client_id\": \"i deleted this for the post\",\r\n        \"client_secret\": \"i deleted this for the post\",\r\n        \"username\": \"i deleted this for the post\",\r\n        \"password\": \"i deleted this for the post\"\r\n    }\r\n        ],\r\n\r\n\"accessories\": [\r\n  {\r\n        \"accessory\": \"LiftMaster\",\r\n        \"name\": \"Garage Door\",\r\n        \"description\": \"MyQ service.\",\r\n        \"username\": \"I deleted this for the post\",\r\n        \"password\" : \"I deleted this for the post\"\r\n    }\r\n]\r\n}\r\n\r\n\r\nLiftmaster.js\r\nvar types = require(\"HAP-NodeJS/accessories/types.js\");\r\nvar request = require(\"request\");\r\n\r\n// This seems to be the \"id\" of the official LiftMaster iOS app\r\nvar APP_ID = \"JVM/G9Nwih5BwKgNCjLxiFUQxQijAebyyg8QUHr7JOrP+tuPb8iHfRHKwTmDzHOu\"\r\n//var APP_ID = \"Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB\"\r\n\r\nfunction LiftMasterAccessory(log, config) {\r\nthis.log = log;\r\nthis.name = config[\"Garage Door\"];\r\nthis.username = config[\"i deleted this for the post\"];\r\nthis.password = config[\"i deleted this for the post\"];\r\nthis.requiredDeviceId = config[\"i deleted this for the post\"];\r\n}\r\n\r\nLiftMasterAccessory.prototype = {\r\n\r\nsetState: function(state) {\r\nthis.targetState = state;\r\nthis.login();\r\n},\r\n\r\nlogin: function() {\r\nvar that = this;\r\n\r\n// reset our logged-in state hint until we're logged in\r\nthis.deviceId = null;\r\n\r\n// querystring params\r\nvar query = {\r\n  appId: APP_ID,\r\n  username: this.username,\r\n  password: this.password,\r\n  culture: \"en\"\r\n};\r\n\r\n// login to liftmaster\r\nrequest.get({\r\n  url: \"https://myqexternal.myqdevice.com/api/user/validatewithculture\",\r\n  qs: query\r\n}, function(err, response, body) {\r\n\r\n  if (!err && response.statusCode == 200) {\r\n\r\n    // parse and interpret the response\r\n    var json = JSON.parse(body);\r\n    that.userId = json[\"UserId\"];\r\n    that.securityToken = json[\"SecurityToken\"];\r\n    that.log(\"Logged in with user ID \" + that.userId);\r\n    that.getDevice();\r\n  }\r\n  else {\r\n    that.log(\"Error '\"+err+\"' logging in: \" + body);\r\n  }\r\n});\r\n},\r\n\r\n// find your garage door ID\r\ngetDevice: function() {\r\nvar that = this;\r\n\r\n// querystring params\r\nvar query = {\r\n  appId: APP_ID,\r\n  SecurityToken: this.securityToken,\r\n  filterOn: \"true\"\r\n};\r\n\r\n// some necessary duplicated info in the headers\r\nvar headers = {\r\n  MyQApplicationId: APP_ID,\r\n  SecurityToken: this.securityToken\r\n};\r\n\r\n// request details of all your devices\r\nrequest.get({\r\n  url: \"https://myqexternal.myqdevice.com/api/v4/userdevicedetails/get\",\r\n  qs: query,\r\n  headers: headers\r\n}, function(err, response, body) {\r\n\r\n  if (!err && response.statusCode == 200) {\r\n\r\n    // parse and interpret the response\r\n    var json = JSON.parse(body);\r\n    var devices = json[\"Devices\"];\r\n    var foundDoors = [];\r\n\r\n    // look through the array of devices for an opener\r\n    for (var i=0; i<devices.length; i++) {\r\n      var device = devices[i];\r\n\r\n      if (device[\"MyQDeviceTypeName\"] == \"GarageDoorOpener\") {\r\n\r\n        // If we haven't explicity specified a door ID, we'll loop to make sure we don't have multiple openers, which is confusing\r\n        if (!that.requiredDeviceId) {\r\n          var thisDeviceId = device.MyQDeviceId;\r\n          var thisDoorName = \"Unknown\";\r\n          for (var j = 0; j < device.Attributes.length; j ++) {\r\n            var thisAttributeSet = device.Attributes[j];\r\n            if (thisAttributeSet.AttributeDisplayName == \"desc\") {\r\n              thisDoorName = thisAttributeSet.Value;\r\n              break;\r\n            }\r\n          }\r\n          foundDoors.push(thisDeviceId + \" - \" + thisDoorName);\r\n          that.deviceId = thisDeviceId;\r\n        }\r\n\r\n        // We specified a door ID, sanity check to make sure it's the one we expected\r\n        else if (that.requiredDeviceId == device.MyQDeviceId) {\r\n          that.deviceId = device.MyQDeviceId;\r\n          break;\r\n        }\r\n\r\n      }\r\n\r\n    }\r\n\r\n    // If we have multiple found doors, refuse to proceed\r\n    if (foundDoors.length > 1) {\r\n      that.log(\"WARNING: You have multiple doors on your MyQ account.\");\r\n      that.log(\"WARNING: Specify the ID of the door you want to control using the 'requiredDeviceId' property in your config.json file.\");\r\n      that.log(\"WARNING: You can have multiple liftmaster accessories to cover your multiple doors\");\r\n\r\n      for (var j = 0; j < foundDoors.length; j++) {\r\n        that.log(\"Found Door: \" + foundDoors[j]);\r\n      }\r\n\r\n      throw \"FATAL: Please specify which specific door this Liftmaster accessory should control - you have multiples on your account\";\r\n\r\n    }\r\n\r\n    // Did we get a device ID?\r\n    if (that.deviceId) {\r\n      that.log(\"Found an opener with ID \" + that.deviceId +\". Ready to send command...\");\r\n      that.setTargetState();\r\n    }\r\n    else\r\n    {\r\n      that.log(\"Error: Couldn't find a door device, or the ID you specified isn't associated with your account\");\r\n    }\r\n  }\r\n  else {\r\n    that.log(\"Error '\"+err+\"' getting devices: \" + body);\r\n  }\r\n});\r\n},\r\n\r\nsetTargetState: function() {\r\n\r\nvar that = this;\r\nvar liftmasterState = (this.targetState + \"\") == \"1\" ? \"0\" : \"1\";\r\n\r\n// querystring params\r\nvar query = {\r\n  appId: APP_ID,\r\n  SecurityToken: this.securityToken,\r\n  filterOn: \"true\"\r\n};\r\n\r\n// some necessary duplicated info in the headers\r\nvar headers = {\r\n  MyQApplicationId: APP_ID,\r\n  SecurityToken: this.securityToken\r\n};\r\n\r\n// PUT request body\r\nvar body = {\r\n  AttributeName: \"desireddoorstate\",\r\n  AttributeValue: liftmasterState,\r\n  ApplicationId: APP_ID,\r\n  SecurityToken: this.securityToken,\r\n  MyQDeviceId: this.deviceId\r\n};\r\n\r\n// send the state request to liftmaster\r\nrequest.put({\r\n  url: \"https://myqexternal.myqdevice.com/api/v4/DeviceAttribute/PutDeviceAttribute\",\r\n  qs: query,\r\n  headers: headers,\r\n  body: body,\r\n  json: true\r\n}, function(err, response, json) {\r\n\r\n  if (!err && response.statusCode == 200) {\r\n\r\n    if (json[\"ReturnCode\"] == \"0\")\r\n      that.log(\"State was successfully set.\");\r\n    else\r\n      that.log(\"Bad return code: \" + json[\"ReturnCode\"]);\r\n      that.log(\"Raw response \" + JSON.stringify(json));\r\n  }\r\n  else {\r\n    that.log(\"Error '\"+err+\"' setting door state: \" + JSON.stringify(json));\r\n  }\r\n});\r\n},\r\n\r\ngetServices: function() {\r\nvar that = this;\r\nreturn [{\r\nsType: types.ACCESSORY_INFORMATION_STYPE,\r\ncharacteristics: [{\r\ncType: types.NAME_CTYPE,\r\nonUpdate: null,\r\nperms: [\"pr\"],\r\nformat: \"string\",\r\ninitialValue: this.name,\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"Name of the accessory\",\r\ndesignedMaxLength: 255\r\n},{\r\ncType: types.MANUFACTURER_CTYPE,\r\nonUpdate: null,\r\nperms: [\"pr\"],\r\nformat: \"string\",\r\ninitialValue: \"LiftMaster\",\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"Manufacturer\",\r\ndesignedMaxLength: 255\r\n},{\r\ncType: types.MODEL_CTYPE,\r\nonUpdate: null,\r\nperms: [\"pr\"],\r\nformat: \"string\",\r\ninitialValue: \"Rev-1\",\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"Model\",\r\ndesignedMaxLength: 255\r\n},{\r\ncType: types.SERIAL_NUMBER_CTYPE,\r\nonUpdate: null,\r\nperms: [\"pr\"],\r\nformat: \"string\",\r\ninitialValue: \"A1S2NASF88EW\",\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"SN\",\r\ndesignedMaxLength: 255\r\n},{\r\ncType: types.IDENTIFY_CTYPE,\r\nonUpdate: null,\r\nperms: [\"pw\"],\r\nformat: \"bool\",\r\ninitialValue: false,\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"Identify Accessory\",\r\ndesignedMaxLength: 1\r\n}]\r\n},{\r\nsType: types.GARAGE_DOOR_OPENER_STYPE,\r\ncharacteristics: [{\r\ncType: types.NAME_CTYPE,\r\nonUpdate: null,\r\nperms: [\"pr\"],\r\nformat: \"string\",\r\ninitialValue: \"Garage Door Opener Control\",\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"Name of service\",\r\ndesignedMaxLength: 255\r\n},{\r\ncType: types.CURRENT_DOOR_STATE_CTYPE,\r\nonUpdate: function(value) { that.log(\"Update current state to \" + value); },\r\nperms: [\"pr\",\"ev\"],\r\nformat: \"int\",\r\ninitialValue: 0,\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"BlaBla\",\r\ndesignedMinValue: 0,\r\ndesignedMaxValue: 4,\r\ndesignedMinStep: 1,\r\ndesignedMaxLength: 1\r\n},{\r\ncType: types.TARGET_DOORSTATE_CTYPE,\r\nonUpdate: function(value) { that.setState(value); },\r\nperms: [\"pr\",\"pw\",\"ev\"],\r\nformat: \"int\",\r\ninitialValue: 0,\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"BlaBla\",\r\ndesignedMinValue: 0,\r\ndesignedMaxValue: 1,\r\ndesignedMinStep: 1,\r\ndesignedMaxLength: 1\r\n},{\r\ncType: types.OBSTRUCTION_DETECTED_CTYPE,\r\nonUpdate: function(value) { that.log(\"Obstruction detected: \" + value); },\r\nperms: [\"pr\",\"ev\"],\r\nformat: \"bool\",\r\ninitialValue: false,\r\nsupportEvents: false,\r\nsupportBonjour: false,\r\nmanfDescription: \"BlaBla\"\r\n}]\r\n}];\r\n}\r\n};\r\n\r\nmodule.exports.accessory = LiftMasterAccessory;\r\n"},"comment":{"url":"https://api.github.com/repos/nfarina/homebridge/issues/comments/145735026","html_url":"https://github.com/nfarina/homebridge/issues/136#issuecomment-145735026","issue_url":"https://api.github.com/repos/nfarina/homebridge/issues/136","id":145735026,"user":{"login":"SphtKr","id":3403287,"avatar_url":"https://avatars.githubusercontent.com/u/3403287?v=3","gravatar_id":"","url":"https://api.github.com/users/SphtKr","html_url":"https://github.com/SphtKr","followers_url":"https://api.github.com/users/SphtKr/followers","following_url":"https://api.github.com/users/SphtKr/following{/other_user}","gists_url":"https://api.github.com/users/SphtKr/gists{/gist_id}","starred_url":"https://api.github.com/users/SphtKr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SphtKr/subscriptions","organizations_url":"https://api.github.com/users/SphtKr/orgs","repos_url":"https://api.github.com/users/SphtKr/repos","events_url":"https://api.github.com/users/SphtKr/events{/privacy}","received_events_url":"https://api.github.com/users/SphtKr/received_events","type":"User","site_admin":false},"created_at":"2015-10-06T03:50:24Z","updated_at":"2015-10-06T03:50:24Z","body":"Yeah--currently it only reports status right (at best) if you only open and close it via Homebridge...kindof annoying. It's on the list of things I want to write a pull request for. To do anything different it'll have to poll MyQ's services every few seconds...which hopefully won't annoy the MyQ servers... :-\\"}}