API - http GET works with browser but no other device

JohnW

Major Contributor
Joined
Mar 23, 2024
Messages
62
I'm trying to use a few of the commands from the API document in my smart home setup but I'm struggling at the outset and wonder of anyone can offer anything as to why it won't work.

If I type https://192.168.0.64/httpapi.asp?command=MCUKeyShortClick:2 into my browser address bar, it works perfectly; the page updates with 'ok' and the Wiim Amp in the Kitchen at that address calls Preset 2 (which starts BBC Radio 2). However when I try to do a http GET from another source it has no effect. My Hubitat hub can send a http GET from its rule engine but the Amp doesn't respond. I've also tried the same from node-red and that returns an error and doesn't work either. So I'm trying to find out why it would work from the browser address bar but not from any other device on the same network. I've asked similar on the Hubitat forum but wondered if anyone else had experienced similar issues and found a workaround? TIA
 
openssl s_client -showcerts -servername wiim -connect 192.168.1.184:443 > cacert.pemem
depth=0 C = CN, ST = Shanghai, L = Shanghai, O = linkplay, OU = linkplay, CN = www.linkplay.com, emailAddress = mail@linkplay.com
verify error:num=18:self-signed certificate
verify return:1
depth=0 C = CN, ST = Shanghai, L = Shanghai, O = linkplay, OU = linkplay, CN = www.linkplay.com, emailAddress = mail@linkplay.com
verify return:1
 
You probably instructed your browser to ignore the certicate error, when issuing this https request for the first time?

No I wouldn't have a clue how to; I just typed in the url and hit go and it works
Confirmed from the command line:

curl --insecure https://192.168.1.184/httpapi.asp?command=MCUKeyShortClick:2 ==> returns OK

curl https://192.168.1.184/httpapi.asp?command=MCUKeyShortClick:2 ==> returns curl: (60) SSL certificate problem: self-signed certificate
Thanks. I can see that confirms what's causing it not to work, but I'm not sure whether I can get around it. The interface in my Hubitat hub will only enable me to enter a url. I'm unable to specify 'curl --insecure' before it. Possibly the same in node-red (which I'd rather not use as it's adding an additional step and complexity to what I'm trying to achieve)
 
I'm assuming that you can run a node instance somewhere since you're running node-red.

ChatGPT suggests something like this javascript. Basically it's a proxy that you would point to that passes traffic to and from the WiiM. So, you could use a plain http:80 to have the Hubitat talk to the WiiM via the proxy to https:443. I haven't tested it but it should get you started.

Code:
// proxy.js 
//   --> run from command line: node ./proxy.js

const http = require('http');
const https = require('https');
const fs = require('fs');

// Configuration
const proxyPort = 443;
const devicePort = 80;
const deviceHost = 'your_device_ip'; // Replace with your device's IP address
const deviceCertFile = 'device_cert.pem'; // Replace with the path to your device's certificate

// Load the public certificate of the device
const deviceCert = fs.readFileSync(deviceCertFile);

// Create HTTPS proxy server
const proxyServer = https.createServer({
  cert: deviceCert, // Use the device's public certificate
  rejectUnauthorized: false // Ignore self-signed certificate errors
});

proxyServer.on('request', (req, res) => {
  // Forward request to the device
  const proxyReq = http.request({
    hostname: deviceHost,
    port: devicePort,
    path: req.url,
    method: req.method,
    headers: req.headers
  }, (proxyRes) => {
    res.writeHead(proxyRes.statusCode, proxyRes.headers);
    proxyRes.pipe(res, { end: true });
  });

  req.pipe(proxyReq, { end: true });
});

proxyServer.listen(proxyPort, () => {
  console.log(`Proxy server listening on port ${proxyPort}`);
});
 
No I wouldn't have a clue how to; I just typed in the url and hit go and it works
It doesn't take much more than hitting something like "ignore and connect anyway". :) But that's just the web browser.
 
It doesn't take much more than hitting something like "ignore and connect anyway". :) But that's just the web browser.
Yeah...but he's trying to connect a device that doesn't have a web browser, so the connection is getting messed up. A proxy just hides the self-signed certificate that you just ignored manually.
 
Yeah...but he's trying to connect a device that doesn't have a web browser, so the connection is getting messed up. A proxy just hides the self-signed certificate that you just ignored manually.
Exactly. I was just pointing out the difference.
 
All a bit too much for my skillset unfortunately. I just want to send a command with the url from the Hubitat hub to the Wiim and for it to be actioned. Proxies and additional code overcomplicate it for me.

I tried using node-red which I have running on a Pi4 but it's the same issue with the error. I've tried adding the following to the settings.js file process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; as was suggested elsewhere online but that doesn't work.
 
Last edited:
All a bit too much for my skillset unfortunately. I just want to send a command with the url from the Hubitat hub to the Wiim and for it to be actioned. Proxies and additional code overcomplicate it for me.
Hey @JohnW I appreciate that it's a difficult "square peg in a round hole" problem presented by your Hubitat. Converting https into http needs some device to run the square-to-round converter unfortunately. Another possibility is to open a ticket with Hubitat to see if they'll fix it -- they might.

Posting the stuff below since this worked without needing to do anything special in the browser which is what you wanted for the Hubitat. I had a few minutes today to test something with nginx (pronounced as 'engine X') which is a web server used every day by billions that can also be configure to be an http-to-https proxy. For anyone able to run Docker on a Raspberry PI or some other device with Docker, this will grab nginx and configure it and run it to work for your use case. Since a lot of folks use Docker, it's here if you want to give it a try.

Dockerfile: defines the nginx server to be built
# Use the official Nginx image
FROM nginx
# Copy Nginx configuration file
COPY nginx.conf /etc/nginx/nginx.conf
# Expose ports
EXPOSE 80
# Start Nginx
CMD ["nginx", "-g", "daemon off;"]

nginx.conf: defines the nginx configuration to run as a proxy
  • '192.168.1.57' is the IP address used for the device running this proxy; use your own
  • 192.168.1.184 - IP address of the WiiM
worker_processes auto;

events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name 192.168.1.57;

location / {
proxy_pass https://192.168.1.184;
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

docker-compose.yml: the orchestration to define the http-to-https-proxy
version: '3.8'
services:
http-to-https-proxy:
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
restart: always

Assume that you have linux host with docker and docker-compose installed (sudo apt install docker.io docker-compose). 3 files above are copied and installed on the server.

# ls
Dockerfile docker-compose.yml nginx.conf

# ./docker-compose up

Web browser: http://192.168.1.57/httpapi.asp?command=MCUKeyShortClick:2

Result: OK
 
Hey @JohnW I appreciate that it's a difficult "square peg in a round hole" problem presented by your Hubitat. Converting https into http needs some device to run the square-to-round converter unfortunately. Another possibility is to open a ticket with Hubitat to see if they'll fix it -- they might.

Posting the stuff below since this worked without needing to do anything special in the browser which is what you wanted for the Hubitat. I had a few minutes today to test something with nginx (pronounced as 'engine X') which is a web server used every day by billions that can also be configure to be an http-to-https proxy. For anyone able to run Docker on a Raspberry PI or some other device with Docker, this will grab nginx and configure it and run it to work for your use case. Since a lot of folks use Docker, it's here if you want to give it a try.

Dockerfile: defines the nginx server to be built


nginx.conf: defines the nginx configuration to run as a proxy

  • '192.168.1.57' is the IP address used for the device running this proxy; use your own
  • 192.168.1.184 - IP address of the WiiM


docker-compose.yml: the orchestration to define the http-to-https-proxy


Assume that you have linux host with docker and docker-compose installed (sudo apt install docker.io docker-compose). 3 files above are copied and installed on the server.



Web browser: http://fedora.box/httpapi.asp?command=MCUKeyShortClick:2

Result: OK
I meant to post back. I've made some progress! After messing around with ChatGPT and some help on the Hubitat forum I'm cobbling a driver together. So far I've got all the basic functions for a single player from the API working successfully. I'm going to keep at it and hopefully learn along the way
 
I meant to post back. I've made some progress! After messing around with ChatGPT and some help on the Hubitat forum I'm cobbling a driver together. So far I've got all the basic functions for a single player from the API working successfully. I'm going to keep at it and hopefully learn along the way
That's awesome! When you have something, feel free to share :)
 
That's awesome! When you have something, feel free to share :)
Just a few commands at the moment. Hopefully I'll work ou how to add some event logging for Hubitat and grab the information from getPlayerStatus to populate the Hubitat 'Current States'

Code:
 /*
 * Wiim Media Player
 *
 *  Licensed Virtual the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  Change History:
 *
 *    Date        Who                What
 *    ----        ---                ----
 *    15Apr2024   John Williamson   Initial attempt - epic fail
 *    16Apr2024      John Williamson   Fixed ignoreSSLIssues, added and tested all commands
 */

metadata {
    definition (name: "Wiim Media Player", namespace: "John Williamson", author: "John Williamson") {
        capability "Actuator"
        
        command "preset", ["number"]
        command "volumeUp"
        command "volumeDown"
        command "mute"
        command "unmute"
        command "stop"
        command "pause"
        command "seek", ["number"]
        command "resume"
        command "pausePlay"
        command "setVolume", ["number"]
        command "prev"
        command "next"
        command "playURL", ["string"]
        command "playlist", ["string"]
        command "getStatus"
        
        attribute "volume", "number"
        attribute "muted", "bool"
    }

    preferences {
        input("ipAddress", "string", title: "Device IP Address", description: "IP address of Wiim device", required: true)
    }
}

def sendHttpGetRequest(String requestURL) {
    Map requestParams =
    [
        uri: requestURL,
        ignoreSSLIssues: true
    ]

    try {
        httpGet(requestParams) { resp ->
            //log.debug resp
            if (resp != null) {
                // success
                //log.debug resp.data // uncomment to log the Get response data
            } else {
                // failure
            }
        }
    }
    catch (e) {
        // exception thrown
        log.error e
    }
}

def preset(number) {
    def url = "https://${ipAddress}/httpapi.asp?command=MCUKeyShortClick:${number}"
    sendHttpGetRequest(url)
}

def volumeUp() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:vol%2b%2b"
    sendHttpGetRequest(url)
}

def volumeDown() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:vol--"
    sendHttpGetRequest(url)
}

def mute() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:mute:1"
    sendHttpGetRequest(url)
}
def unmute() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:mute:0"
    sendHttpGetRequest(url)
}

def stop() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:stop"
    sendHttpGetRequest(url)
}

def pause() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:pause"
    sendHttpGetRequest(url)
}

def resume() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:resume"
    sendHttpGetRequest(url)
}

def pausePlay() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:onepause"
    sendHttpGetRequest(url)
}

def setVolume(number) {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:vol:${number}"
    sendHttpGetRequest(url)
}

def prev() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:prev"
    sendHttpGetRequest(url)
}

def next() {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:next"
    sendHttpGetRequest(url)
}

def seek(number) {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:seek:${number}"
    sendHttpGetRequest(url)
}

def playURL(string) {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:play:${string}"
    sendHttpGetRequest(url)
}

def playlist(string) {
    def url = "https://${ipAddress}/httpapi.asp?command=setPlayerCmd:m3u:play:${string}"
    sendHttpGetRequest(url)
}

def getStatus() {
    def url = "https://${ipAddress}/httpapi.asp?command=getPlayerStatus"
    sendHttpGetRequest(url)
}
 
Back
Top