[Solved] PUT /v2/devices/{deviceId}/actions/{actionId} returns HTML page?

I am trying to trigger an action via the HTTP API, but the bridge is serving an HTML response instead of triggering the action. Am I doing something wrong?

curl -v -H "BOND-Token: <redacted>" -H "Content-Type: application/json" -X PUT http://192.168.1.22/devices/4f0a116f/actions/TurnOff --data "{}"
*   Trying 192.168.1.22...
* TCP_NODELAY set
* Connected to 192.168.1.22 (192.168.1.22) port 80 (#0)
> PUT /devices/4f0a116f/actions/TurnOff HTTP/1.1
> Host: 192.168.1.22
> User-Agent: curl/7.54.0
> Accept: */*
> BOND-Token: <redacted>
> Content-Type: application/json
> Content-Length: 2
> 
* upload completely sent off: 2 out of 2 bytes
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 13618
< 
<html>
<head>
    <style>
        * {
            margin: 0 auto;
            width: 100%;
            display: block;
            text-align: center;
            font-family: sans-serif;
            transition: all .75s
        }

        body {
            max-width: 25em;
            width: 90%;
            margin: 15 auto
        }

        label>* {
            display: inline;
            width: auto
        }

        style,
        script {
            display: none
        }

        br,
        hr {
            margin: 5 auto;
            max-width: 26em
        }

        button,
        select,
        input {
            margin: 10 auto;
            padding: 15 32;
            text-decoration: none;
            font-size: 16px
        }

        button {
            background-color: #018FBE;
            color: white;
        }
    </style>
    <meta charset="UTF-8">
    <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' />
</head>

<body><svg enable-background="new 0 0 620 171.359" viewBox="0 0 620 171.359">
 <g fill="#018FBE"><path d="m0 167v-163h25.7c14.9 0 25.8.961 32.6 2.88 9.77 2.59 17.5 7.4 23.3 14.4 5.78 7.02 8.66 15.3 8.66 24.8 0 6.21-1.31 11.9-3.94 16.9-2.62 5.06-6.9 9.82-12.8 14.3 9.91 4.66 17.2 10.5 21.7 17.5 4.58 6.99 6.88 15.2 6.88 24.8 0 9.17-2.37 17.5-7.11 25.1-4.74 7.54-10.8 13.2-18.3 16.9-7.48 3.73-17.8 5.6-31 5.6h-45.6zm31.1-133v34.4h6.79c7.57 0 13.2-1.59 16.9-4.77 3.67-3.18 5.51-7.5 5.51-13 0-5.1-1.75-9.15-5.23-12.1-3.49-3-8.8-4.49-15.9-4.49h-8.1zm0 62.1v41.8h7.78c12.9 0 21.6-1.63 26.1-4.88 4.48-3.25 6.73-7.99 6.73-14.2 0-7.02-2.63-12.6-7.9-16.6-5.26-4.06-14-6.1-26.2-6.1h-6.5z"/><path d="m323 4.1h29.8l69.8 107v-107h31.1v163h-29.9l-69.7-107v107h-31.1v-163z"/><path d="m491 4.1h36.8c23.7 0 41.3 2.94 52.9 8.82 11.5 5.88 21 15.4 28.5 28.7 6 13.2 10 28.7 10 46.4 0 12.6-2.09 24.1-6.26 34.7-4.18 10.5-9.94 19.3-17.3 26.2-7.35 6.95-15.3 11.8-23.9 14.4-8.57 2.66-23.4 3.99-44.5 3.99h-37.3v-163zm31 29.9v103h14.4c14.2 0 24.5-1.63 30.9-4.89s11.6-8.73 15.7-16.4c4.07-7.7 6.1-17.2 6.1-28.4 0-17.3-4.84-30.8-14.5-40.3-8.73-8.58-22.7-12.9-42-12.9h-10z"/></g><circle r="69.7" style="stroke:#018fbe;stroke-miterlimit:0;stroke-width:31.6;fill:none" cx="208" cy="85.7"/></svg>
    <br>Everything Smart...
    <br>
    <hr><br>
    <div id="splash">Connecting to BOND product...</div>
    <div id="token"><div id="div_pin">Because this device has been powered on for more than 10 minutes, a 4-digit PIN number from the product label is required:
        <input type="text" id="pin" autocomplete="off" autocorrect="off" autocapitalize="off" placeholder="4-digit PIN" spellcheck="false">
        <small>If you cannot find the PIN, try power cycling the product. On a Powered By BOND product, you may also hold down the Learn button on the remote control for 5 seconds to unlock.</small>
        <br>
        </div>
        To connect to the correct BOND account, please enter the Account Code from the Account Settings in the BOND App:
        <input type="text" id="account_code" autocomplete="off" autocorrect="off" autocapitalize="off" placeholder="16-digit account code" spellcheck="false">
        <font color="red"><div id="token_err"></div></font>
        <small>If you cannot find the account code in the BOND app, please contact BOND Customer Support.</small>
        <br>
        <button id='submit_token' onclick="patch_token()">Add BOND to Account</button>
    </div>
    <hr><br>
    <p><h3><div id="wifi_msg"></div></h3><br></p>
    <div id="m">Select Wifi Network to connect the BOND to<button id='r' onclick="get_sys_wifi_scan()">Refresh Wi-Fi List</button>
        <select onchange="hide(this)" id='o'><option>Loading...</option></select>
        <input type="text" id="p" autocomplete="off" autocorrect="off" autocapitalize="off" placeholder="Password" spellcheck="false" onclick="ch ? 0 : this.setSelectionRange(0, this.value.length); ch=1; hide(this)" onkeypress="hide(this);ch=1;">
        <label><input type="checkbox" checked id="x" onchange="hide(this)"> Show Password</label>
        <br>
        
        <div id='a' onclick="advanced();">Advanced</div>
        <br>
        <div id='b'>
            <label><input id='st'  onclick="static_ip();" type="checkbox" > Static IP</label>
            <div id='s'>
                <div>The following three fields are mandatory</div>
                <input type="text" placeholder="IP" onchange="this.style.color= ValidateIPaddress(this.value) ? 'black':'red';" id="ip" />
                <input type="text" placeholder="Netmask" onchange="this.style.color= ValidateIPaddress(this.value) ? 'black':'red';" id="netmask" />
                <input type="text" placeholder="Gateway" onchange="this.style.color= ValidateIPaddress(this.value) ? 'black':'red';" id="gw" />
                <div>The following two fields are optional</div>
                <input type="text" placeholder="DNS" onchange="this.style.color= ValidateIPaddress(this.value) ? 'black':'red';" id="dns" />
                <input type="text" placeholder="Fallback DNS" onchange="this.style.color= ValidateIPaddress(this.value) ? 'black':'red';" id="dns_alt" />
            </div>
        </div>
       

        <button id='s' onclick="put_sys_wifi_sta()">Connect to WiFi</button></div>

    <hr><br>

      <div id="status">loading BOND status...</div>
</body>
<script type="text/javascript">
    var adv = 1;
    var ch = 0;
    d = document;
    de = d.getElementById.bind(d);
    var p = de("p");
    var s = de("s");
    var o = de('o');
    var pin = de('pin');
    var account_code = de('account_code');

    var token = '';
    var token_locked = 1;
    var tried_patch = false;
    var scaned_once = false;
    var success_put_sta = false;

    function get_token() {
      var x = new XMLHttpRequest();
      x.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          w = JSON.parse(this.response);
          token_locked = w.locked
          de("token").style.display = "block";
          if (token_locked === 1) {
            de("splash").style.display = "none";
            de("div_pin").style.display = "block";
            if (w.pin_attempts_left === 0) {
              de("token_err").innerHTML = "PIN entered incorrectly too many times.<br>Please power cycle BOND.";
            }
          }
          if ("token" in w) {
            token = w.token
            de("splash").style.display = "none";
            if ("account_code" in w && w.account_code.length > 0 && tried_patch) {
              de("token").style.display = "none";
            }
            if (!success_put_sta) de("m").style.display = "block";
            /* hide the pin number once we have the token */
            de("div_pin").style.display = "none";
            if (!scaned_once) {
              get_sys_wifi_scan();
              scaned_once = true;
            }
          }
        }
      }
      x.open("GET", "/v2/token", true);
      x.send();
    };

    function patch_token() {
      tried_patch = true;
      if (token_locked === 1) {
        if (!/^([0-9?]{4})$/.test(de("pin").value)) {
          de("token_err").innerHTML = "PIN must be 4 digits";
          return;
        }
      }
      ac = de("account_code").value.replace(/\s/g, '');
      if (!/^([a-fA-F0-9]{16})$/.test(ac)) {
        de("token_err").innerHTML = "account_code must be 16 digits without spaces (letters A-F and numbers 0-9)";
        return;
      }
      var sum = 0;
      for (i in ac.slice(0,14)) { sum += parseInt(ac[i], 16); }
      sum = sum % (16*16);
      var checksum = parseInt(ac.slice(14,16), 16);
      if (checksum !== sum) {
        de("token_err").innerHTML = "the account_code you entered is invalid";
        return;
      }
      ac = ac.slice(0,14)
      de("token_err").innerHTML = "";
      var x = new XMLHttpRequest();
      x.onreadystatechange = function() {
        if (this.readyState == 4 && this.status >= 200 && this.status < 300) {
          de("splash").style.display = "block";
          get_token();
        }
        if (this.readyState == 4 && this.status == 401) {
          de("token_err").innerHTML = "the PIN you entered is incorrect";
          get_token();
        }
      };
      x.open("PATCH", "/v2/token", true);
      x.setRequestHeader("Content-type", "application/json; charset=UTF-8");
      if (token_locked === 1) {
        x.send(JSON.stringify({
          locked: 0,
          pin: pin.value,
          account_code: ac
        }));
      } else {
        /* dont provide pin when not neeeded and possibly incorrect */
        x.send(JSON.stringify({
          locked: 0,
          account_code: ac
        }));
      }
    }

    function put_sys_wifi_sta() {
        base64_ssid = de(o.value).attributes.b.value;
        if (base64_ssid === "NONE") return;
        var x = new XMLHttpRequest();
        x.onreadystatechange = function() {
          if (this.readyState == 4 && this.status >= 200 && this.status < 300) {
            de("m").style.display = "none";
            success_put_sta = true;
            setTimeout(function() {
                de("wifi_msg").innerHTML = "Your BOND is now connecting to WiFi...";
            }, 750)
          }
        };
        let body = {password: btoa(p.value), ssid: base64_ssid}
        if (de('st').checked) {
          for (let key of ['ip', 'gw', 'netmask', 'dns', 'dns_alt']) {
            let value = de(key).value;
            if (value != null && value != "") {
              body[key] = value;
            }
          }
        }
        x.open("PUT", "/v2/sys/wifi/sta");
        x.setRequestHeader("BOND-Token", token);
        x.setRequestHeader("Content-type", "application/json; charset=UTF-8");
        x.send(JSON.stringify(body));
    }

    function get_sys_wifi_sta() {
        var x = new XMLHttpRequest();
        x.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 204) {
                de("status").innerHTML = "WiFi not configured"
            }
            if (this.readyState == 4 && this.status == 200) {
                w = JSON.parse(this.response);
                de("status").innerHTML = ""
                de("status").innerHTML += "SSID: '" + atob(w.ssid) + "'<br>"
                de("status").innerHTML += "Status: "
                if (w.status === -2) {
                  de("status").innerHTML += "authentication failure<br>"
                }
                if (w.status === -1) {
                  de("status").innerHTML += "network not found<br>"
                }
                if (w.status === 0) {
                  de("status").innerHTML += "connecting...<br>"
                }
                if (w.status === 1) {
                  de("status").innerHTML += "acquiring ip...<br>"
                }
                if (w.status === 2) {
                  de("status").innerHTML += "connected to WiFi<br>"
                  de("status").innerHTML += "ip: " + w.ip + "<br>"
                  de("status").innerHTML += "netmask: " + w.netmask + "<br>"
                  de("status").innerHTML += "gateway: " + w.gw + "<br>"
                  de("status").innerHTML += "dns: " + w.dns + "<br>"
                  de("status").innerHTML += "mac: " + w.mac + "<br>"
                  de("wifi_msg").innerHTML = "Your BOND is connected to WiFi!";
                }
            }
            if (this.readyState == 4 && this.status == 401) {
                de("status").innerHTML = "will load status after PIN provided...";
            }
        };
        x.open("GET", "/v2/sys/wifi/sta", true);
        x.setRequestHeader("BOND-Token", token);
        x.send();
    }

    function get_sys_wifi_scan() {
        var x = new XMLHttpRequest();
        x.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                w = JSON.parse(this.response);
                o.innerHTML = "<option id=\"Select your Access Point...\" b=\"NONE\" selected>Select your Access Point...</option>";
                for (i in w.results) {
                    i = w.results[i];
                    id = atob(i[0]); /* base64 decode */
                    if (!id) continue;
                    o.innerHTML += "<option id=\"" + id + "\" b=\"" + i[0] + "\">" + id + "</option>";
                }
            }
            if (this.readyState == 4 && this.status == 204) {
              setTimeout(function(){ get_sys_wifi_scan(); }, 1000);
            }
        };
        x.open("GET", "/v2/sys/wifi/scan", true);
        x.setRequestHeader("BOND-Token", token);
        x.send();
    }

    function hide(that) {
        p.type = de("x").checked ? 'text' : 'password';
        that.nodeName == 'SELECT' ? p.disabled = de(that.value).attributes.b.value == "NONE" ? 1 : 0 : 0
    }

    function advanced() {
        adv = adv ? 0 : 1;
        de('b').style.display = adv ? 'block' : 'none';
        de('a').textContent = (adv ? "▴" : "▾") + ' Advanced'
    }

    function static_ip() {
        de('s').style.display = de('st').checked ? 'block' : 'none';
    }

    function ValidateIPaddress(ipaddress) {
        return (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress))
    }
    de("token").style.display = "none";
    de("m").style.display = "none";
    get_token();
    advanced();
    static_ip();
    setInterval(function() {
      get_token();
      get_sys_wifi_sta();
    }, 2000)
</script>
</html>
* Connection #0 to host 192.168.1.22 left intact

You need /v2/ before /devices/ in the URL.

Doh :sweat_smile:

That’s what I get for coding too late into the night.

1 Like

:crazy_face: I know the feeling!