SSE over REST-API

Update: I set the Log Leve to debug and now could log the error on the openhab2 side:

2017-01-01 16:44:38.853 [DEBUG] [io.rest.JSONResponse$ExceptionMapper] - exception during REST Handling
javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable
	at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.getMethodRouter(MethodSelectingRouter.java:529)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.access$000(MethodSelectingRouter.java:94)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter$4.apply(MethodSelectingRouter.java:779)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.apply(MethodSelectingRouter.java:371)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:109)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:92)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:61)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.process.internal.Stages.process(Stages.java:197)[155:org.glassfish.jersey.core.jersey-common:2.22.2]
	at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:318)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)[155:org.glassfish.jersey.core.jersey-common:2.22.2]
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)[155:org.glassfish.jersey.core.jersey-common:2.22.2]
	at org.glassfish.jersey.internal.Errors.process(Errors.java:315)[155:org.glassfish.jersey.core.jersey-common:2.22.2]
	at org.glassfish.jersey.internal.Errors.process(Errors.java:297)[155:org.glassfish.jersey.core.jersey-common:2.22.2]
	at org.glassfish.jersey.internal.Errors.process(Errors.java:267)[155:org.glassfish.jersey.core.jersey-common:2.22.2]
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)[155:org.glassfish.jersey.core.jersey-common:2.22.2]
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)[156:org.glassfish.jersey.core.jersey-server:2.22.2]
	at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)[153:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]
	at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)[153:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)[153:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)[153:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]
	at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)[153:org.glassfish.jersey.containers.jersey-container-servlet-core:2.22.2]
	at com.eclipsesource.jaxrs.publisher.internal.ServletContainerBridge.service(ServletContainerBridge.java:76)[11:com.eclipsesource.jaxrs.publisher:5.3.1.201602281253]
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812)[79:org.eclipse.jetty.servlet:9.2.14.v20151106]
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:587)[79:org.eclipse.jetty.servlet:9.2.14.v20151106]
	at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:70)[170:org.ops4j.pax.web.pax-web-jetty:4.2.4]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)[77:org.eclipse.jetty.security:9.2.14.v20151106]
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:271)[170:org.ops4j.pax.web.pax-web-jetty:4.2.4]
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)[79:org.eclipse.jetty.servlet:9.2.14.v20151106]
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection.handle(JettyServerHandlerCollection.java:80)[170:org.ops4j.pax.web.pax-web-jetty:4.2.4]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.server.Server.handle(Server.java:499)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)[78:org.eclipse.jetty.server:9.2.14.v20151106]
	at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)[70:org.eclipse.jetty.io:9.2.14.v20151106]
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)[81:org.eclipse.jetty.util:9.2.14.v20151106]
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)[81:org.eclipse.jetty.util:9.2.14.v20151106]
	at java.lang.Thread.run(Thread.java:745)[:1.8.0_40-internal]
201

Is there anyone who could help?

Thanks for bring the topic up. I am also interested in this for some time.
Maybe you could try to change the url of eventSource

var eventSource = new EventSource(“http://192.168.178.52:8080/rest/rest/events?topics=smarthome/items/FEG_HZ_Ist/state”);

Hi Martin!

Thank you for replying. For sadness your URL also does not work: I get an 404 Not Found Error now.

But your hint was although very helpful. The following URL now gives me all events:
var eventSource = new EventSource("http://192.168.178.52:8080/rest/events");

Do you have an idea how I could delimit the events on one or more specified items and only next to updated Items?

Greetings Dominic

I have just realized that I had a typo in my url

http://192.168.178.52:8080/rest/events?topics=smarthome/items/FEG_HZ_Ist/state

try using the one above

I have test a bit further.
It seems that under Safari the described function is working but NOT under chrome nor Firefox it is not.

From Chrome it is also working as far as I can see.

What maybe is hampering is the authorization ( check if you see in the developers tab a “No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://zzz’ is therefore not allowed access.” errors)

Putting very basic example in the html folder of openhab2 will give you results in a filtered way (replace with your item)

<!DOCTYPE html>
<html>
<body>
<h1>Getting server updates</h1>
<div id="result"></div>
<script>
if(typeof(EventSource) !== "undefined") {
    var source = new EventSource("http://192.168.3.50:8080/rest/events?topics=smarthome/items/ReigerPowerAbsWd/state");
    source.onmessage = function(event) {
        document.getElementById("result").innerHTML += event.data + "<br>";
    };
} else {
    document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>
</body>
</html>
1 Like

To get rid of “No ‘Access-Control-Allow-Origin’ you have to edit “service.cfg” under runtime and add the line
"org.eclipse.smarthome.cors:enable=true”.

Then SSE is working on all browsers I have tested.

Hey there,

thank you very much. The combination of both solutions does work.

Just one question to @marcel_verpaalen : if I just want to get the state element - how would I have to change your code? I tried

document.getElementById("result").innerHTML = event.data.state + "<br>"; and
document.getElementById("result").innerHTML += event.state + "<br>";

but both gives me “undefined”.

Sound I use a json parser?

You mean something like this:

This filters only state events and parses the data (and puts it in a table). I think you indeed need to use the jsonparser

<!DOCTYPE html>
<html>
<head>
<style> table, td {border: 1px solid black;} </style>
</head>
<body>

<h1>Getting Openhab server updates</h1>

<div id="result"></div>
<table id="myTable">
  <tr>
    <th>Topic</th>
	<th>Value</th>
    <th>Type</th> 
    <th>Payload</th>
  </tr>
</table>
<br>

<script>
if(typeof(EventSource) !== "undefined") {
    var source = new EventSource("http://192.168.3.50:8080/rest/events?topics=smarthome/items/ReigerPowerAbsWd/state");
    source.onmessage = function(eventPayload) {
        <!--document.getElementById("result").innerHTML += eventPayload.data + "<br>"; -->
		var event = JSON.parse(eventPayload.data);
		if (event.type == "ItemStateEvent") {
			var stateData = JSON.parse(event.payload);
			var table = document.getElementById("myTable");
			var row = table.insertRow(1);
			var cell1 = row.insertCell(0);
			var cell2 = row.insertCell(1);
			var cell3 = row.insertCell(2);
			var cell4 = row.insertCell(3);
			cell1.innerHTML = event.topic;
			cell2.innerHTML = stateData.value;
			cell3.innerHTML = event.type;
			cell4.innerHTML = event.payload;
		};
	};
} else {
    document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>
</body>
</html>

thank you very much! that´s it!

Hey there again,

Just another question:

If i would like to connect to the event Stream and get events for more then just 1device- what whoud be the best way? Should i open many single event connections or is there a better way?

Perhaps I have to describe it a little bit further:

Th following Code gives me an event stream for one Device / Item.

var eventSource = new EventSource("http://192.168.178.52:8080/rest/events?topics=smarthome/items/GEG_HZ_Ist/state");

    eventSource.addEventListener('message', function (eventPayload) {

        var event = JSON.parse(eventPayload.data);  
        if (event.type === 'ItemStateEvent') {
            var Result = JSON.parse(event.payload);
            console.log(Result.value);
       
    }
});

If I would change the line

var eventSource = new EventSource("http://192.168.178.52:8080/rest/events?topics=smarthome/items/GEG_HZ_Ist/state");

to

var eventSource = new EventSource("http://192.168.178.52:8080/rest/events?topics=smarthome/items/*/state");

i will get lot of values. How could I use this values seperatly? Or in other words: If there are three values in the Stream (1,2,3) for three Devices (A,B,C) how could I take the pair “B2” to use it further in my html code?

@marcel_verpaalen: @Marty56 : It would be great, if you could help me again.

Greetings Dominic

Sorry, have no experience with that approach.
Guess it will be trial & error if no body knows

hey there,

i have some working code for now. But because i´m a newbie to javascript the code is for sure not optimized.
In my example tyou can see the code for getting the events for 2 devices.

Could someone please have a look at the code?

<script>



var eventSource = new EventSource("http://192.168.178.52:8080/rest/events");

eventSource.addEventListener('message', function (eventPayload) {

    var event = JSON.parse(eventPayload.data);
    
    if ((event.type == 'ItemStateEvent') && (event.topic="smarthome/items/GEG_HZ_Soll/state"))
    
    {
    
    var GEG_HZ_Soll = JSON.parse(event.payload);
    console.log(GEG_HZ_Soll.value);

    }
    
    
    
      if ((event.type == 'ItemStateEvent') && (event.topic="smarthome/items/GEG_HZ_Ist/state"))
    
    {
    
    var GEG_HZ_Ist = JSON.parse(event.payload);
    console.log(GEG_HZ_Ist.value);

    }
    
    
    
});














</script>

Hi,

i just worked on exactly that topic for the vscode-extension:

You can find a bit more about using SSE here:

In the event method i parse the incoming events.

You can use ‘/rest/events?topics=smarthome/items’ to only get item events. With rest/events you will get all other events as well.

Sorry for being a bit late with my reply, i just noticed, that the last post here is ~2years ago :sweat_smile:

Thank you Marcel, this is a very useful example!

All the endpoints can be found here in the documentation
https://www.eclipse.org/smarthome/documentation/features/events.html
Johannes

Good Day Guys
Just a quick question regarding the REST API, Is there any way to pass an event origin parameter to rest API and get it back from the event streaming API. What I want to do is differentiate the events coming from openhab and connected devices vs events coming back form the updates I sent from REST to avoid loops.
Cheers
Roshan

I am desperate. I have tried everything possible, but I never get a message about my event source.
Do I need to activate anything in openhab? I am running openhabian, on a Raspberry 4, in the latest version.

I do not receive an error message. The HTTP-Request is built and kept open. See picture.

This is the Code.

Does anybody have an idea?

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EventSource</title>
</head>

<body>
	<div id="result"></div>


    </script>
<script>
if(typeof(EventSource) !== "undefined") {
    var source = new EventSource("http://192.168.178.56:8080/rest/events?topics=smarthome/items");
    source.onmessage = function(event) {
        document.getElementById("result").innerHTML += event.data + "<br>";
		console.log(event);
    };
} else {
    document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>
</body>

</html>

I have tried it now with the Edge Browser. Everything works fine there. On my One Plus 7T Pro with Chrome Browser it also works.
On my PC with Chrome Browser it does not work. But it doesn’t throw an error message either.