Jump to content

Notification


BobFrankston

Recommended Posts

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

Link to comment

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.

Link to comment
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?

Link to comment

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);
```

 

Link to comment

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?

 

Link to comment
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.

Link to comment

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

Link to comment
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.

Link to comment

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.

 

Link to comment

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. 

Link to comment

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...

Link to comment

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.

 

Link to comment

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>

 

Link to comment

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();
Link to comment

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?

Link to comment

Archived

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


×
×
  • Create New...