openhab / openhab-webui

Web UIs of openHAB
Eclipse Public License 2.0
215 stars 237 forks source link

Connection status feedback for mobile #1441

Open digitaldan opened 2 years ago

digitaldan commented 2 years ago

When the MainUI App has loaded, there is not a good way to observe if the client has active SSE connections and is healthy, or if it's not able to make these connections and is actually in a disconnected state (not receiving events).

This is an issue in our mobile clients where the wrapping Webview can only really tell if the initial loading of the app is successful, but does not have any visibility into the SSE connections of the app. It would be nice to have something similar to our other JS callbacks (like how we handle the side menu) where we can register for callbacks if the client is having trouble connecting.

digitaldan commented 1 year ago

@ghys do you have any thoughts on how i can monitor the network "health" of the app? Some javascript method i could poll from the wrapping app, or maybe a callback that the app triggers when it's unable to reach the network ?

ghys commented 1 year ago

@digitaldan sorry for the delay, I did have some thoughts but nothing definitive to make an answer.

I think we could make both a short answer and a long answer here.

The short answer is, we can notify the host app when the error event occurs on a SSE connection, around here: https://github.com/openhab/openhab-webui/blob/e8dee7a7fc94e8ed9ee79d93b7ef4ddae1c8e1d7/bundles/org.openhab.ui/web/src/js/openhab/sse.js#L38-L46 This should in theory catch both issues when connecting to the EventSource, and issues while the EventSource is connected and it had to be closed unexpectedly. https://developer.mozilla.org/en-US/docs/Web/API/EventSource/readyState is also there to track SSE connection statuses, but it's not that reliable in my experience (see below). There is also a crude tracker of current SSE connections here: https://github.com/openhab/openhab-webui/blob/e8dee7a7fc94e8ed9ee79d93b7ef4ddae1c8e1d7/bundles/org.openhab.ui/web/src/js/openhab/sse.js#L4 that could be made available with an API extension.

The long answer is, it seems SSE connections can be broken and not necessarily being notified as such when you examine them; they would simply not report any new events but still their status would be "open". This has been reported numerous times. This can happen for a number of reasons, like on mobile apps could be "put to sleep" to make way for newer ones when they are opened, or there could be a change of networks which closes existing HTTP connections or the like.

In any case the current implementation would not recover from this. I chose to have pages open and close SSE connections themselves, when they're meant to be for them and the duration for which they are in the foreground, so they would be as short-lived as possible, instead of the former implementations in Paper UI or HABPanel where you would have a long-lived connection, in order to cope for this - a navigation to another page would refresh SSE connections so they would normally work again if they were not.

This situation could perhaps be solved with a more robust SSE connection monitoring scheme which may involve regular "ping" events, and if such an event isn't received in time, close & make another SSE connection transparently.

digitaldan commented 1 year ago

@digitaldan sorry for the delay, I did have some thoughts but nothing definitive to make an answer.

No worries, i had a feeling there was not going to be an easy answer :-)

The short answer is, we can notify the host app when the error event occurs on a SSE connection, around here:

So i think you and i have come to the same conclusion, which is good in that i'm not way off on my current solution, but bad in that its not ideal given the nature of SSE connections.

So i took the same approach over the weekend, and came up with this which i just checked in:

https://github.com/digitaldan/openhab-webui/blob/9b01c4c297f1c301fbae229509b851fafc1849d2/bundles/org.openhab.ui/web/src/js/store/modules/states.js#L70

When a SSE connection is succesfull it sets a new state property like so:

context.commit('sseConnected', true)

And if it errors it sets

context.commit('sseConnected', false)

I then have a listener set here: https://github.com/digitaldan/openhab-webui/blob/9b01c4c297f1c301fbae229509b851fafc1849d2/bundles/org.openhab.ui/web/src/components/app.vue#L388

Which will try and communicate with a wrapping web view

 watch: {
    '$store.state.states.sseConnected': {
      handler: function (connected) {
        console.debug('sseConnected', connected)
        if (window.OHApp && typeof window.OHApp.sseConnected === 'function') {
          try {
            window.OHApp.sseConnected(connected)
          } catch {}
        }
      },
      immediate: true // provides initial (not changed yet) state
    }
  },

This surprising works well if the app cannot establish a new connection, like when navigating pages, but as you have alluded too, the connection can die in other ways where no errors are actually reported.

I was very surprised how limited the SSE API is in browsers, and also the "magic" they use to try and reconnect streams with virtually no way of monitoring the health of a connection that has already been established. Seem like a spec that was never finished, but shipped anyways.

This situation could perhaps be solved with a more robust SSE connection monitoring scheme which may involve regular "ping" events, and if such an event isn't received in time, close & make another SSE connection transparently.

Agreed, after several testing efforts i also think this is the only way to really monitor the heath of a connection so my next step will probably be looking at adding a heartbeat option to the openhab-core SSE service , something optional you can ask for when opening a connection.