Jump to content

Notification


BobFrankston

Recommended Posts

Posted

If you only want to monitor one or two devices a simple program for each drvice that triggers on each devices status and send a notification or a NR containing a REST style Get packet out to your remote device.

Sent using Tapatalk

Posted

The WebSockets interfaces is, or would be, what I want except I can't get it to work because of the authentication issue. Ideally the authentication would be carried in a payload because WebSockets don't support it in the URL. But instead there is a very special case kludge work-around using an Apache service.

I just want a simple example going direct to the ISY -- Ideally in NodeJS but I can program around. Also would be nice to have a browser client example too but that's secondary as I can work around any issues.

Posted
1 hour ago, BobFrankston said:

I just want a simple example going direct to the ISY -- Ideally in NodeJS but I can program around. Also would be nice to have a browser client example too but that's secondary as I can work around any issues.

Cannot help you with Node.JS - but put this .html file on the ISY web hosting and point a browser to it. Browser get's the code from ISY - then the code subscribes (when the user clicks 'Connect' - and the events are printed to the canvas as received. This is a browser talking direct to ISY - no intermediary...

Seems to have broken in more recent Chrome versions - but still works fine in Edge...


<!DOCTYPE html>
<html lang="en">
<head>
  <title>WebSocket ISY Client</title>
  <meta charset="UTF-8" />
  <script>
    "use strict";
    // Initialize everything when the window finishes loading
    window.addEventListener("load", function(event) {
      var status = document.getElementById("status");
      var url = document.getElementById("url");
      var open = document.getElementById("open");
      var close = document.getElementById("close");
      var message = document.getElementById("message");
      var socket;

      status.textContent = "Not Connected";
      var isy_host = window.location.host;
      var x = location.protocol;
      if (x == "https:") {
        var ws_host = "wss://" + isy_host
      } else {
	    var ws_host = "ws://" + isy_host
      }
      url.value = ws_host + "/rest/subscribe";
      close.disabled = true;

      // Create a new connection when the Connect button is clicked
      open.addEventListener("click", function(event) {
        open.disabled = true;
        socket = new WebSocket(url.value, "ISYSUB");

        socket.addEventListener("open", function(event) {
          close.disabled = false;
          status.textContent = "Connected";
		  console.log("Connected");
        });

        // Display messages received from the server
        socket.addEventListener("message", function(event) {
		  document.getElementById("log").innerText = event.data + String.fromCharCode(13) + document.getElementById("log").innerText;
        });

        // Display any errors that occur
        socket.addEventListener("error", function(event) {
          message.textContent = "Error: " + event;
		  console.log("Error: " + event)
        });

        socket.addEventListener("close", function(event) {
          open.disabled = false;
          status.textContent = "Not Connected";
		  console.log("Not Connected");
        });
      });

      // Close the connection when the Disconnect button is clicked
      close.addEventListener("click", function(event) {
        close.disabled = true;
        message.textContent = "";
        socket.close();
      });

    });
  </script>
</head>
<body>
  Status: <span id="status"></span><br />
  <input id="url" type="hidden" />
  <input id="open" type="button" value="Connect" />&nbsp;
  <input id="close" type="button" value="Disconnect" /><br />
  <span id="message"></span>
  <p id="log"></p>
</body>
</html>

The issue with Chrome 73+ appears to be new CORB policy is now affecting even inline Javascript (https://www.chromestatus.com/feature/5629709824032768). The error is "Unchecked runtime.lastError: The message port closed before a response was received.".

@Michel - is there any way yet or planned to set a CORS policy (Adding a "Access-Control-Allow-Origin" header to ISYs responses) on the ISY - so that Javascript hosted elsewhere can actually subscribe to events?

Posted

I also tried Edge to make sure. 

Node is simple - just the JavaScript (TypeScript) code without the browser restrictions.

 

// import * as ws from 'ws';
import ws from 'ws';
import * as http from 'http';

const user = "admin";
const pass = 'admin';
const cred = `${user}:${pass}`;
const isy = "192.55.226.204";

let iws: ws;

function Connect(cred?: string) {
    try {
        let url = `ws://${isy}/rest/subscribe`
        if (cred)
            url = `ws://${cred}@${isy}/rest/subscribe`;
        console.log(`URL ${url}`);
        iws = new ws(url, "ISYSUB");
        iws.on('error', ev => console.error(`Failed ${ev.message}`));
        iws.on('open', () => console.log('opened'));
        iws.on('message', data => console.log(`Received ${data}`));
    }
    catch (e) {
        console.error(`Connecting to ${isy}: ${e.error}`);
        // debugger;
    }

}

Connect();
Connect(cred);
 
Connect();
Connect(cred);
```

 

Posted

Did you put the html on the ISY web host?

 

You cannot put it on another host and point the URl - it must be served directly from ISY...

 

Posted

Are you saying I"m supposed to serve the HTML from the ISY? If could do that I can hack together a work-around for notification by having it create a separate WS stream to my own systems.

What is the cookbook approach for placing a file on the ISY and serving it?

 

Posted
Are you saying I"m supposed to serve the HTML from the ISY? If could do that I can hack together a work-around for notification by having it create a separate WS stream to my own systems.

What is the cookbook approach for placing a file on the ISY and serving it?

 

 

Your ISY needs the network module to be able to serve files. In the Admin Console, navigate to ‘Configuration’, ‘Networking’, ‘Web Server’. Upload your .html to the /USER/WEB folder.

 

Then it’s referenced from your browser at http://1.2.3.4/USER/WEB/. (where 1.2.3.4 should be modified to your ISYs IP)

 

The browser will prompt for basic authentication - then that session is used by the script to authenticate the socket subscription.

Posted

Now that I understand how to make it work I can program the rest myself. I'll look for the documentation on the messages I'm getting but can probably figure it out anyway.

Thanks again

Posted

In experimenting I want to try copying a directory and, failing that, files into a subdirectory but something isn't working. Even when I drag an individual file I get "bad request". Is there a problem with subdirectories? Even better would b e a programmatic interface for sending files to the device so I can automate the updating.

Discovered adding an extension like .htm works.

OK - the answer is that files need an extension name and then even directory dragging works. Very good news. Just needs a better error message.

image.png

Posted
On 5/18/2019 at 7:49 AM, BobFrankston said:

The WebSockets interfaces is, or would be, what I want except I can't get it to work because of the authentication issue. Ideally the authentication would be carried in a payload because WebSockets don't support it in the URL. But instead there is a very special case kludge work-around using an Apache service.

I just want a simple example going direct to the ISY -- Ideally in NodeJS but I can program around. Also would be nice to have a browser client example too but that's secondary as I can work around any issues. 

Not sure what your authentication issue is creating a WS stream - my Python softconsole creates such a stream with very straightforward code:

if self.isy.addr.startswith('http://'):
   wsurl = 'ws://' + self.isy.addr[7:] + '/rest/subscribe'
elif self.isy.addr.startswith('https://'):
   wsurl = 'wss://' + self.isy.addr[8:] + '/rest/subscribe'
else:
   wsurl = 'ws://' + self.isy.addr + '/rest/subscribe'
while True:
   try:
      # noinspection PyArgumentList
      ws = websocket.WebSocketApp(wsurl, on_message=on_message,
                           on_error=on_error,
                           on_close=on_close, on_open=on_open,
                           subprotocols=['ISYSUB'],
                           header={'Authorization': 'Basic ' + self.a.decode('ascii')})
      break
   except AttributeError as e:
      logsupport.Logs.Log(self.hubname + " Problem starting WS handler - retrying: ", repr(e))
self.lastheartbeat = time.time()
ws.run_forever(ping_timeout=999, sslopt={"cert_reqs": ssl.CERT_NONE})

This embeds the user/password info in the url just fine.  If you want to see the larger structure of which this is a part take a look at  https://github.com/kevinkahn/softconsole/blob/master/hubs/isy/isyeventmonitor.py   Not nodeJS but should be similar.

Posted

Kevin -- already solved it. The key was in recognizing that the sample had to run in the browser but be sourced from the ISY thus avoiding cross-origin and authentication issues.

Where does your python app run -- on the ISY itself? I may experiment more with authentication using websockets but, for now, serving from the server may be sufficient.

 

Posted

My app is a control console running on a Pi. It provides a touch interface for ISY and Home assistant hubs as well as stuff like weather. There is a long thread here that I started that is essentially its support page that points at all its functionality. 

Posted

The CORS issue is a browser issue. It’s possible to send the correct subscription from any websocket library that supports basic authentication. There are not many of those - but I think the Python library added support for basic auth...

Posted

I'll work-around the CORS issue using the browser and will consider the Python option ( possibly using Kevin Kahn's example). I also discovered that /CONFIG is a dictionary for values I retrieve via the API even if not complete.

I'm coming up to speed and plan to toss thousands of lines of code that I wrote over the years to deal directly with the Insteon stuff. (some of it far too clever)

I now want to understand the subscription and other XML message formats. (is there a JSON option?). Is there documentation on the formats such especially the subscription messages? I can guess a lot but far better to have a document.

 

Posted

@BobFrankston,

I am so very glad to hear. I am still a little confused as to what is this "document" you keep referring to. Have you taken a look at our WSDK? What's missing? Even the controls for which you had questions in another post are listed there OR described in the schemas. 

With kind regards,
Michel

Posted

Just as an FYI I have gotten the web version messaging my main program but I want to shift to a standalone app. I've looked at Kevin's program and will try to figure out the nugget that is the WebSocket interface. Thanks.

 

Posted

OK, progress ... I now run the webpage served from the ISY using Puppeteer. Kludgy but it works. I'm planning to switch away from the 2242 but ... some things aren't quite there yet.

Kevin, I realize I was confused by the examples you gave me and overlooked your original more focused post -- I will look again if the Puppeteer approach isn't sustainable.

But there is some problem with the subscription. For a while, instead of getting messages like ST in control I got messages like _0. What is that? I also notice messages sent by other paths (such as the 2245) are not always seen. I presume they are supposed to be? Even when I do a query and the ISY sees it is off it doesn't send a state message to the subscribers.

BTW, while I'm at it -- it would be nice if the administrative console offered sorting by name and/or address and for error messages to be in a window rather than all model dialogs.

6:36:26 PM <Event seqnum="358" sid="uuid:94"><control>_0</control><action>120</action><node></node><eventInfo></eventInfo></Event>
6:36:01 PM <Event seqnum="357" sid="uuid:94"><control>_0</control><action>120</action><node></node><eventInfo></eventInfo></Event>
6:36:00 PM <Event seqnum="356" sid="uuid:94"><control>_23</control><action>1</action><node></node><eventInfo><PortalStatus isConnected="true" isRegistered="true"/></eventInfo></Event>
6:36:00 PM <Event seqnum="355" sid="uuid:94"><control>_22</control><action>1</action><node></node><eventInfo><totalCostToday>0.000000</totalCostToday><totalCostPeriod>0.000000</totalCostPeriod><totalUsagePeriod>0.000000</totalUsagePeriod><cycleEndTime>2019/05/21 18:36:03</cycleEndTime></eventInfo></Event>
6:36:00 PM <Event seqnum="354" sid="uuid:94"><control>_4</control><action>6</action><node></node><eventInfo><status>1</status></eventInfo></Event>
6:36:00 PM <Event seqnum="353" sid="uuid:94"><control>_4</control><action>5</action><node></node><eventInfo><status>0</status></eventInfo></Event>
6:36:00 PM <Event seqnum="352" sid="uuid:94"><control>_3</control><action>WH</action><node>1F 66 A1 1</node><eventInfo><status>1</status></eventInfo></Event>
6:36:00 PM <Event seqnum="351" sid="uuid:94"><control>_3</control><action>WH</action><node>2C B0 36 1</node><eventInfo><status>1</status></eventInfo></Event>
6:36:00 PM <Event seqnum="350" sid="uuid:94"><control>_3</control><action>WH</action><node>8 1C 8B 1</node><eventInfo><status>1</status></eventInfo></Event>
6:36:00 PM <Event seqnum="349" sid="uuid:94"><control>ST</control><action uom="100" prec="0">0</action><node>9 45 2C 1</node><eventInfo></eventInfo><fmtAct>Off</fmtAct></Event>
6:36:00 PM <Event seqnum="348" sid="uuid:94"><control>OL</control><action uom="100" prec="0">255</action><node>9 45 2C 1</node><eventInfo></eventInfo><fmtAct>100%</fmtAct></Event>
6:36:00 PM <Event seqnum="347" sid="uuid:94"><control>RR</control><action uom="25" prec="0">31</action><node>9 45 2C 1</node><eventInfo></eventInfo><fmtAct>0.1 seconds</fmtAct></Event>
6:36:00 PM <Event seqnum="346" sid="uuid:94"><control>ST</control><action uom="100" prec="0">0</action><node>4 5E 15 1</node><eventInfo></eventInfo><fmtAct>Off</fmtAct></Event>
6:36:00 PM <Event seqnum="345" sid="uuid:94"><control>OL</control><action uom="100" prec="0">255</action><node>4 5E 15 1</node><eventInfo></eventInfo><fmtAct>100%</fmtAct></Event>
6:36:00 PM <Event seqnum="344" sid="uuid:94"><control>RR</control><action uom="25" prec="0">31</action><node>4 5E 15 1</node><eventInfo></eventInfo><fmtAct>31</fmtAct></Event>
6:36:00 PM <Event seqnum="343" sid="uuid:94"><control>ST</control><action uom="100" prec="0">0</action><node>1F DB E1 1</node><eventInfo></eventInfo><fmtAct>Off</fmtAct></Event>
6:36:00 PM <Event seqnum="342" sid="uuid:94"><control>OL</control><action uom="100" prec="0">255</action><node>1F DB E1 1</node><eventInfo></eventInfo><fmtAct>100%</fmtAct></Event>
6:36:00 PM <Event seqnum="341" sid="uuid:94"><control>RR</control><action uom="25" prec="0">28</action><node>1F DB E1 1</node><eventInfo></eventInfo><fmtAct>28</fmtAct></Event>
6:36:00 PM <Event seqnum="340" sid="uuid:94"><control>ST</control><action uom="100" prec="0">0</action><node>4 6F 5C 1</node><eventInfo></eventInfo><fmtAct>Off</fmtAct></Event>
6:36:00 PM <Event seqnum="339" sid="uuid:94"><control>OL</control><action uom="100" prec="0">255</action><node>4 6F 5C 1</node><eventInfo></eventInfo><fmtAct>100%</fmtAct></Event>
6:36:00 PM <Event seqnum="338" sid="uuid:94"><control>RR</control><action uom="25" prec="0">31</action><node>4 6F 5C 1</node><eventInfo></eventInfo><fmtAct>31</fmtAct></Event>
6:36:00 PM <Event seqnum="337" sid="uuid:94"><control>ST</control><action uom="100" prec="0">0</action><node>9 97 F0 6</node><eventInfo></eventInfo><fmtAct>Off</fmtAct></Event>
6:36:00 PM <Event seqnum="336" sid="uuid:94"><control>ST</control><action uom="100" prec="0">0</action><node>9 97 F0 5</node><eventInfo></eventInfo><fmtAct>Off</fmtAct></Event>

 

Posted

I got the TypeScript example working! Very short and simple

import WebSocket from 'ws';

 
function btoa(txt: string) {
return Buffer.from(txt).toString("base64");
}
 
function tryws() {
try {
var url = "ws://<address>/rest/subscribe";
var cred = "<Credentials>";
var opts: WebSocket.ClientOptions = {
headers: {
Authorization: `Basic ${btoa(cred)}`
},
origin: "com.universal-devices.websockets.isy",
 
};
var sock = new WebSocket(url, ["ISYSUB"], opts);
sock.on('open', () => console.log('Connected'));
sock.on('error', err => console.log(`err ${err.message}`));
sock.on('message', msg => console.log(`Message ${msg}`));
sock.on('close', () => console.log('Closed'));
console.log("Started");
}
catch (e) {
console.error(`Error ${e.message}`);
debugger;
}
}
 
tryws();
import WebSocket from 'ws';

function btoa(txt: string) {
    return Buffer.from(txt).toString("base64");
}

function tryws() {
    try {
        var url = "ws://192.55.226.204/rest/subscribe";
        var cred = "admin:admin";
        var opts: WebSocket.ClientOptions = {
            headers: {
                Authorization: `Basic ${btoa(cred)}`
            },
            origin: "com.universal-devices.websockets.isy",

        };
        var sock = new WebSocket(url, ["ISYSUB"], opts);
        sock.on('open', () => console.log('Connected'));
        sock.on('error', err => console.log(`err ${err.message}`));
        sock.on('message', msg => console.log(`Message ${msg}`));
        sock.on('close', () => console.log('Closed'));
        console.log("Started");
    }
    catch (e) {
        console.error(`Error ${e.message}`);
        debugger;
    }
}

tryws();
Posted

At least it was working
 

<?xml version=\"1.0\" encoding=\"UTF-8\"?><RestResponse succeeded=\"false\"><status>815</status></RestResponse>

In debugging I spin up a subscription for each run but, apparently, they don't get cleaned up and there is a small limit. Is there a rest command?

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...