-
Notifications
You must be signed in to change notification settings - Fork 5
Description
For the sake of not repeating a whole lot of typing, I'm going to link a downstream project issue that I opened for this before I understood that the problem was actually upstream:
ToddGreenfield/homebridge-directv#17
In very, very brief terms - older set top boxes are compatible (mostly), but they do not support the type
argument to /info/getLocations, and depending on how pervasive this is in the code, it could come up elsewhere. Using one of my own STBs as an example:
pi@homebridge:~/scripts/directv$ curl http://172.16.30.79:8080/info/getLocations | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 195 100 195 0 0 1772 0 --:--:-- --:--:-- --:--:-- 1772
{
"locations": [
{
"clientAddr": "0",
"locationName": "LIVING ROOM"
}
],
"status": {
"code": 200,
"commandResult": 0,
"msg": "OK.",
"query": "/info/getLocations"
}
}
Note that "type" isn't being sent. Now let's look at what directv-remote attempts to send to that receiver:
172.16.30.28.54386 > 172.16.30.76.http-alt: Flags [P.], cksum 0x9505 (incorrect -> 0x27e4), seq 1:87, ack 1, win 502, options [nop,nop,TS val 1843789388 ecr 516813839], length 86: HTTP, length: 86
GET /info/getLocations?type=1 HTTP/1.1
Host: 172.16.30.76:8080
Connection: close
/info/getLocations?type=1
If I attempt to do that, here's what happens:
pi@homebridge:~/scripts/directv$ curl http://172.16.30.79:8080/info/getLocations?type=1
{"status": {
"code": 403,
"commandResult": 1,
"msg": "Forbidden.Invalid URL parameter(s) found.",
"query": "/info/getLocations?type=1"
Which makes sense, because if you poll /info/getOptions, you get this back:
pi@homebridge:~/scripts/directv$ curl http://172.16.30.79:8080/info/getOptions | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5873 100 5873 0 0 20607 0 --:--:-- --:--:-- --:--:-- 20679
{
"options": [
{
"command": "/info/getLocations",
"description": "List of available client locations. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
}
]
},
*snip*
It only supports wrapper and callback. Neither is required. So if we bounce back over to your code, we have this block:
// "List of available client locations."
// Returns an array of the networked set top boxes
// type is an optional parameter
// the docs label it as 'int'
// only 0 and 1 aren't *Forbidden*
// i'm not sure what the difference is yet,
// but 1 shows more of my wireless Genie STBs
this.getLocations = function(type, callback){
var path = '/info/getLocations';
var options = {
hostname: this.IP_ADDRESS,
port: 8080,
path: path
};
if (typeof type !== 'undefined') {
options.path = buildQueryString(options.path, { type: type });
}
makeRequest(options, callbackHandler(callback));
};
So type is "optional", but then a bit further down we check to make sure type isn't undefined. If it is, we don't do anything. If it isn't, we makeRequest(). Best I can tell, there's a line missing to call buildQueryString without the type parameter, probably inside of an else something like this (not a nodeJS dev, please correct if I have syntactical mistakes!):
if (typeof type !== 'undefined') {
options.path = buildQueryString(options.path, { type: type });
}
else {
options.path = buildQueryString(options.path);
}
That way we build the query string no matter what, and then makeRequest should handle the rest from there.
For the sake of completeness, here is the full output of /info/getOptions. I can make a cursory scan to see if this "type" business is present anywhere else, but it is a comparison that will be challenging without output of a more modern set of hardware. My suspicion is that on older hardware, type wasn't defined. On newer genie-era hardware type 0 is likely a standalone STB, and type 1 is a genie with child units. Either way, I think my solution solves for that. Again, not a nodeJS dev - need to go spelunking on my filesystem and see if I can apply this fix and see it work.
{
"options": [
{
"command": "/info/getLocations",
"description": "List of available client locations. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
}
]
},
{
"command": "/info/getSerialNum",
"description": "STB serial number. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
},
{
"isRequired": false,
"name": "clientAddr",
"type": "string"
}
]
},
{
"command": "/info/getVersion",
"description": "Set-top-box and SHEF information. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
}
]
},
{
"command": "/info/mode",
"description": "Set-top-box mode. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
},
{
"isRequired": false,
"name": "clientAddr",
"type": "string"
}
]
},
{
"command": "/remote/processKey",
"description": "Process a key request from the remote control. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
},
{
"isRequired": false,
"name": "clientAddr",
"type": "string"
},
{
"isRequired": false,
"name": "hold",
"type": "string"
},
{
"isRequired": true,
"name": "key",
"type": "string"
}
]
},
{
"command": "/serial/processCommand",
"description": "Process a command request from remote control. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
},
{
"isRequired": true,
"name": "cmd",
"type": "hex"
}
]
},
{
"command": "/tv/getProgInfo",
"description": "Program information of specified channel at current or specific time. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "minor",
"type": "int"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
},
{
"isRequired": true,
"name": "major",
"type": "int"
},
{
"isRequired": false,
"name": "clientAddr",
"type": "string"
},
{
"isRequired": false,
"name": "time",
"type": "long"
}
]
},
{
"command": "/tv/getTuned",
"description": "Information about the currently viewed program. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
},
{
"isRequired": false,
"name": "videoWindow",
"type": "string"
},
{
"isRequired": false,
"name": "clientAddr",
"type": "string"
}
]
},
{
"command": "/tv/tune",
"description": "Tune to a channel. Warning: This command may change or be disabled in the future.",
"formControls": [],
"urlParameters": [
{
"isRequired": false,
"name": "wrapper",
"type": "string"
},
{
"isRequired": false,
"name": "minor",
"type": "int"
},
{
"isRequired": false,
"name": "callback",
"type": "string"
},
{
"isRequired": true,
"name": "major",
"type": "int"
},
{
"isRequired": false,
"name": "source",
"type": "int"
},
{
"isRequired": false,
"name": "clientAddr",
"type": "string"
}
]
}
],
"status": {
"code": 200,
"commandResult": 0,
"msg": "OK.",
"query": "/info/getOptions"
}
}