Should Habpanel refresh?

I tried this setting “keep wi-fi on during sleep”, but the status of the widgets is not refreshed.
I get also the wrong status when changing to an other dashbord. All switch widgets are showing the wrong status.
I tried this on different web-browser (firefox, chrome) on Linux Notebook and Android tablets. It is always the same.
I use openhab 2 (actual snapshot).

After some more investigation I found that this behavior is only on items which are not bind to a thing (physical actor).

I have a similar issue as the original poster, as long as it concerns switches, no problems with a slider. My openHab is running on a Raspberry Pi (2), and I am using OH2.0. I have tried running habpanel on a windows 10 laptop, tried it with Chrome, Internet Explorer and Edge. The behavior is always the same. Suppose a light is off, when I start habpanel this is displayed properly. When I now click on that switch on the habpanel screen, the actual light switches on. This is however not shown in habpanel. If I click again, nothing happens, the lights stays on. In the openhab logs I see that another “switch on” command is sent. After a refresh (F5), the correct state is shown, and I can again change the actual state (once).

In chrome I have played with the F12, and there are no error messages, but also no messages indicating anything gets refreshed. Just the actual command is seen there.

The interesting thing is that I created a basic habpanel on my phone (Android, Moto 4G+), in the Chrome browser, and it works perfectly. Also if I switch on/off a light using another device, the new state is visible on the habpanel on my phone immediately, but not on my laptop So it appears to be a windows issue …

Anybody any ideas what to try next?

I’m experiencing this issue on Android tablet (Nexus 7) in Chrome. Widgets aren’t updating.

Ive found that if I am playng with config (items/sitemaps) irrespective of them being in habpanel then there is (sometimes) a problem - a quick close and reopen of the browser and it seems to fix it. Happy for the price I am paying for the product.

@PeterJ this is what you should see in the Network tab of Chrome’s dev tools:

A long-running, never-ending “events” request with a 200 status of type “eventsource”.

If you do and click on it, the “EventStream” displays the server-sent events (SSE) sent by openHAB.

If you don’t, there’s a problem with your setup.

Same here. Nexus 7 with Chrome.
I have to push the refresh button, then the widget update.
Same panel on Lenovo TAB2 works fine.

@ysc, thanks for these screenshots. I hope it will indeed help me to track down the problem, as I now know better what to look for., I do see the “events” request, but not with a 200 status. The status says “pending”, and that does not change. Clicking on it says “stalled”, and there is an orange warning “Caution: Request is not finished yet!” (and that does not change).

I have a somewhat busy week ahead, so I don’t know when I have time to dive into it a bit deeper. But I will definitely come back to this, either with more questions, or with the solution (maybe more people have the same issue).

As a follow up to my own post, googling learned me that my problem might be related to Sophos Protection. I already found out I have no problems when trying to use habpabel on my phone, I now added my wife’s tablet (Zenbook) and my daughters laptop (like my own, running Windows 10, but F-secure instead of Sophos) to list of devices were habpanel works without problems,

Unfortunately I can’t ultimately test it by switching off (temporarily) Sophos on my laptop, as it is managed by the company I work for. Another reason often found on the internet for the problem is adblockers. In my case that is certainly not the reason, as I don’t have any adblockers on Internet Explorer or Edge. For me no big deal, as my ultimate goal is to have habpanel running in the future on a dedicated wall-mounted tablet. So during development, I can push F5 occasionally.

1 Like

I was thinking about this too. Proxy servers (common in enterprise environments) and antiviruses intercepting network communications (and sometimes acting as transparent HTTP proxies) may not like or support the SSE connection, hence the refresh problems.
Not much we can do about it, perhaps there’s a way to make an exception for certain hosts in Sophos.

I am now fully convinced it is indeed Sophos that is causing the problem. I found the following site, with some explanation and also a response from Sophos: https://community.sophos.com/products/free-antivirus-tools-for-desktops/f/sophos-anti-virus-for-mac-home-edition/5750/sophos-av-blocks-server-sent-events-sse-on-mac-os-x-yosemite. There is also a reference to a little and very basic testscript, which indeed also does not work on my Sophos (and company-managed) laptop. On my phone it does work when on 4G. It does not work when my phone is on my company’s WiFi. For those interested, the script is here: http://neutrino.otterbein.edu/~tagg/streamtest/test.cgi

As I said, for me not important, I can still develop on my laptop (for developing I do need the instant updates, as long as I understand why it won’t work). “Production” will be on a table in a later stage. Anyway, thanks a lot for creating the amazing habpanel, I like it a lot, I start especially enjoying the template widget, as it gives a lot of freedom.

I see the request when I click the switch, but when I try to click it, it says “This request has no preview” Is this normal?

As many described above, I had the following issue with habpanel on my fire 7 tablet:

  • everything worked, including auto-updated items (via event stream)
  • after a few minutes (could not really pin this down - seemed to be quite random) automatic updates would simply stop
  • I have tried all sorts of browsers (chrome, silk, fully, habpanelviewer) -> same behavior everywhere
  • buttons would still work, refresh would work -> but no more automatic updates
  • on any other machine (windows and macbook) the event stream worked forever - must be some tablet-specific thing
  • I remote debugged -> no errors, no error codes, events connection was reported as up, it would simply stop receiving anything after a while

After sinking days of time into this I decided to stop investigating and just patch this thing up. Little heads up: I am a professional software developer, but I am a Microsoft kind-of-guy, have zero angular experience and the last time I touched java script was >10 years ago (things were different back then ; ).

Nevertheless, I managed to get this thing building and added the following change to the openhab.service.js

  • every 2 minutes (hard-coded), close the event source connection and force an item reload

This did the trick for me, my habpanel is now running stably on the fire 7 and does not lose its connection to events. Due to my lack of knowledge of angularjs, I cannot really guarantee if that is a sustainable solution in general.

As I do not see myself putting anything on github, I thought I could at least share it in this forum thread. Find the code attached - I hope this will work for others as well.

openhab.service.js

(function() {
'use strict';

    angular
        .module('app.services')
        .service('OHService', OHService)
        .value('OH2ServiceConfiguration', {})
        .service('OH2StorageService', OH2StorageService);

    OHService.$inject = ['$rootScope', '$http', '$q', '$timeout', '$interval', '$filter', '$location', 'SpeechService', 'tmhDynamicLocale', '$translate'];
    function OHService($rootScope, $http, $q, $timeout, $interval, $filter, $location, SpeechService, tmhDynamicLocale, $translate) {
        this.getItem = getItem;
        this.getItems = getItems;
        this.getLocale = getLocale;
        this.onUpdate = onUpdate;
        this.sendCmd = sendCmd;
        this.sendVoice = sendVoice;
        this.reloadItems = reloadItems;

        var liveUpdatesEnabled = false, prevAudioUrl = '', locale = null, eventSource = null;
        var autoRefreshEnabled = false, refresher = null;

        ////////////////

        function onUpdate(scope, name, callback) {
            var handler = $rootScope.$on('openhab-update', callback);
            scope.$on('$destroy', handler);
            //watchItem(name);
            //longPollUpdates(name);
        }

        function loadItems() {
            $http.get('/rest/items')
            .then(function (data) {
                if (angular.isArray(data.data)) {
                    console.log("Loaded " + data.data.length + " openHAB items");
                    $rootScope.reconnecting = false;
                    $rootScope.items = data.data;
                    if (!liveUpdatesEnabled) registerEventSource();
                    if (!autoRefreshEnabled) {
                        autoRefreshEnabled = true;
                        console.log("Setting up 2 minute reconnect watch-dog.");
                        
                        refresher = $interval(function () {
                            console.log("Renewing SSE connection...");
                            if(eventSource != null) {
                                eventSource.close();
                                console.log("Closing old stream");
                                eventSource = null;
                            }
                            liveUpdatesEnabled = false;
                            $timeout(loadItems, 100);
                        }, 120000); //re-establish connection to SSE API every two minutes
                    }
                } else {
                    console.warn("Items not found? Retrying in 5 seconds");
                    $rootScope.reconnecting = true;
                    $rootScope.items = [];
                    $timeout(loadItems, 5000);
                }
                $rootScope.$emit('openhab-update');
            },
            function (err) {
                console.warn("Error loading openHAB items... retrying in 5 seconds");
                $rootScope.reconnecting = true;
                $timeout(loadItems, 5000);
            });
        }

        function getItem(name) {
            var item = $filter('filter')($rootScope.items, {name: name}, true); 
            return (item) ? item[0] : null;
        }

        function getItems() {
            return $rootScope.items;
        }

        /**
         * Sends command to openHAB
         * @param  {string} item Item's id
         * @param  {string} cmd  Command
         */
        function sendCmd(item, cmd) {
            $http({
                method : 'POST',
                url    : '/rest/items/' + item,
                data   : cmd,
                headers: { 'Content-Type': 'text/plain' }
            }).then(function (data) {
                console.log('Command sent: ' + item + '=' + cmd);

                // should be handled by server push messages but their delivery is erratic
                // so perform a full refresh every time a command is sent
                //loadItems();
            });
        }

        /**
         * Returns a promise with the configured locale
         */
        function getLocale() {
            var deferred = $q.defer();

            if (locale) {
                deferred.resolve(locale);
            } else {
                $http.get('/rest/services/org.eclipse.smarthome.core.i18nprovider/config')
                .then(function (response) {
                    var language;
                    if (!response.data.language) {
                        if (navigator && navigator.languages) {
                            locale = navigator.languages[0];
                            language = locale.split('-')[0];
                        } else if (navigator && navigator.language) {
                            locale = navigator.language;
                            language = locale.split('-')[0];
                        } else {
                            locale = language = 'en';
                        }
                    } else {
                        language = response.data.language;
                        locale = response.data.language + ((response.data.region) ? '-' + response.data.region : '');
                    }

                    /* consider the region only for selected common exceptions where the date/number formats
                        are significantly different than the language's default.
                        If more are needed change the gulpfile.js too and run the 'vendor-angular-i18n' gulp task */
                    if (['es-ar', 'de-at', 'en-au', 'fr-be', 'es-bo', 'pt-br', 'en-ca',
                            'fr-ca', 'fr-ch', 'es-co', 'en-gb', 'en-hk', 'zh-hk', 'en-ie',
                            'en-in', 'fr-lu', 'es-mx', 'en-nz', 'en-sg', 'zh-sg',
                            'es-us', 'zh-tw', 'en-za'].indexOf(locale.toLowerCase()) < 0) {
                        locale = language;
                    }

                    if (language !== "en") {
                        console.log('Setting interface language to: ' + language);
                        $translate.use(language);
                    }

                    console.log('Setting locale to: ' + locale);
                    tmhDynamicLocale.set(locale.toLowerCase());

                    deferred.resolve(locale);
                }, function(error) {
                    console.warn('Couldn\'t retrieve locale settings. Setting default to "en-US"');
                    locale = 'en-US';
                    deferred.resolve(locale);
                });
            }

            return deferred.promise;
        }

        /**
         * Sends POST request to openHAB REST
         * voice interpreters
         * @param  {string} text - STT output
         */
        function sendVoice(text) {
            $http({
                method : 'POST',
                url    : '/rest/voice/interpreters',
                data   : text,
                headers: { 'Content-Type': 'text/plain' }
            }).then(function (data) {
                console.log('Voice command sent: "' + text + '"');
            }, function(error) {
                console.error('Error occured while sending voice command.');
            });
        }

        function reloadItems() {
            loadItems();
        }
        
        function registerEventSource() {
            if (typeof(EventSource) !== "undefined") {
                eventSource = new EventSource('/rest/events');
                liveUpdatesEnabled = true;

                eventSource.onmessage = function (event) {
                    try {
                        var evtdata = JSON.parse(event.data);
                        var topicparts = evtdata.topic.split('/');

                        if (evtdata.type === 'ItemStateEvent' || evtdata.type === 'ItemStateChangedEvent' || evtdata.type === 'GroupItemStateChangedEvent') {
                            var payload = JSON.parse(evtdata.payload);
                            var newstate = payload.value;
                            var item = $filter('filter')($rootScope.items, {name: topicparts[2]}, true)[0];
                            if (item && item.state !== payload.value) {
                                $rootScope.$apply(function () {
                                    console.log("Updating " + item.name + " state from " + item.state + " to " + payload.value);
                                    item.state = payload.value;

                                    if (!item.transformedState) {
                                        // no transformation on state
                                        $rootScope.$emit('openhab-update', item);

                                        if (item.state && $rootScope.settings.speech_synthesis_item === item.name) {
                                            console.log('Speech synthesis item state changed! Speaking it now.');
                                            SpeechService.speak($rootScope.settings.speech_synthesis_voice, item.state);
                                        }
                                        if (item.state && $rootScope.settings.dashboard_control_item === item.name) {
                                            console.log('Dashboard control item state changed, attempting navigation to: ' + item.state);
                                            $location.url('/view/' + item.state);
                                        }
                                    } else {
                                        // fetch the new transformed state
                                        $http.get('/rest/items/' + item.name).then(function (response) {
                                            if (response.data && response.data.transformedState) {
                                                item.transformedState = response.data.transformedState;
                                                $rootScope.$emit('openhab-update', item);
                                            } else {
                                                console.error("Failed to retrieve the new transformedState of item: " + item.name);
                                                item.transformedState = null;
                                                $rootScope.$emit('openhab-update', item);
                                            }
                                        });
                                    }


                                });
                            }
                        } else if (evtdata.topic === "smarthome/webaudio/playurl") {
                            var context, audioBuffer;
                            try {
                                window.AudioContext = window.AudioContext || window.webkitAudioContext;
                                if (typeof (window.AudioContext) != "undefined") {
                                    context = new AudioContext();
                                }

                                var audioUrl = JSON.parse(evtdata.payload);
                                console.log("Audio event received: playing " + audioUrl);

                                if (prevAudioUrl !== audioUrl) {
                                    if (context) {
                                        $http({
                                            url : audioUrl,
                                            method : 'GET',
                                            responseType : 'arraybuffer'
                                        }).then(function(response) {
                                            context.decodeAudioData(response.data, function(buffer) {
                                                audioBuffer = buffer;
                                                var source = context.createBufferSource();
                                                source.buffer = buffer;
                                                source.connect(context.destination);
                                                source.onended = function () {
                                                    context.close();
                                                }
                                                source.start(0);
                                            });
                                        });
                                    } else {
                                        if (!angular.element(document).find("bgsound").length) {
                                            angular.element(document).find("body").append("<bgsound loop='1' />");
                                        }

                                        angular.element(document).find("bgsound").attr('src', audioUrl);
                                    }
                                    prevAudioUrl = audioUrl;
                                }
                            }
                            catch (e) {
                                console.warn("Error while handling audio event: " + e.toString());
                                if (context)
                                  context.close();
                            }
                        }
                    } catch (e) {
                        console.warn('SSE event issue: ' + e.message);
                    }
                }
                eventSource.onerror = function (event) {
                    console.error('SSE error, closing EventSource');
                    liveUpdatesEnabled = false;
                    this.close();
                    $timeout(loadItems, 5000);
                }
            }
        }

    }

    OH2StorageService.$inject = ['OH2ServiceConfiguration', '$rootScope', '$http', '$q', 'localStorageService'];
    function OH2StorageService(OH2ServiceConfiguration, $rootScope, $http, $q, localStorageService) {
        var SERVICE_NAME = 'org.openhab.habpanel';

        this.tryGetServiceConfiguration = tryGetServiceConfiguration;
        this.saveServiceConfiguration = saveServiceConfiguration;
        this.saveCurrentPanelConfig = saveCurrentPanelConfig;
        this.setCurrentPanelConfig = setCurrentPanelConfig;
        this.getCurrentPanelConfig = getCurrentPanelConfig;
        this.useCurrentPanelConfig = useCurrentPanelConfig;
        this.useLocalStorage = useLocalStorage;

        function tryGetServiceConfiguration() {
            var deferred = $q.defer();

            $http.get('/rest/services/' + SERVICE_NAME + '/config').then(function (resp) {
                console.log('openHAB 2 service configuration loaded');
                OH2ServiceConfiguration = resp.data;
                if (!OH2ServiceConfiguration.panelsRegistry) {
                    $rootScope.panelsRegistry = OH2ServiceConfiguration.panelsRegistry = {};
                } else {
                    $rootScope.panelsRegistry = JSON.parse(resp.data.panelsRegistry);
                }
                if (OH2ServiceConfiguration.lockEditing === true) {
                    $rootScope.lockEditing = true;
                }
                // iterate over the config to find widgets added there
                $rootScope.configWidgets = {};
                angular.forEach(OH2ServiceConfiguration, function (value, key) {
                    if (key.indexOf("widget.") === 0) {
                        var widgetname = key.replace("widget.", "");
                        console.log("Adding widget from configuration: " + widgetname);
                        $rootScope.configWidgets[widgetname] = JSON.parse(value);
                    }
                });

                deferred.resolve();

            }, function (err) {
                console.error('Cannot load openHAB 2 service configuration: ' + JSON.stringify(err));

                deferred.reject();
            });

            return deferred.promise;
        }

        function saveServiceConfiguration() {
            var deferred = $q.defer();

            if ($rootScope.panelsRegistry) {
                OH2ServiceConfiguration.panelsRegistry = JSON.stringify($rootScope.panelsRegistry, null, 4);
            }

            $http({
                method: 'PUT',
                url: '/rest/services/' + SERVICE_NAME + '/config',
                data: OH2ServiceConfiguration,
                headers: { 'Content-Type': 'application/json' }
            }).then (function (resp) {
                console.log('openHAB 2 service configuration saved');
                deferred.resolve();
            }, function (err) {
                console.error('Error while saving openHAB 2 service configuration: ' + JSON.stringify(err));
                deferred.reject();
            });

            return deferred.promise;

        }

        function saveCurrentPanelConfig() {
            var deferred = $q.defer();

            var lastUpdatedTime = $rootScope.panelsRegistry[getCurrentPanelConfig()].updatedTime; 

            // fetch the current configuration again (to perform optimistic concurrency on the current panel config only)
            tryGetServiceConfiguration().then(function () {
                var config = $rootScope.panelsRegistry[getCurrentPanelConfig()];
                if (!config) {
                    console.warn('Warning: creating new panel config!');
                    config = $rootScope.panelsRegistry[getCurrentPanelConfig()] = { };
                }
                var currentUpdatedTime = config.updatedTime;
                if (Date.parse(currentUpdatedTime) > Date.parse(lastUpdatedTime)) {
                    deferred.reject('Panel configuration has a newer version on the server updated on ' + currentUpdatedTime);
                    return;
                }
                config.updatedTime = new Date().toISOString();
                config.dashboards = angular.copy($rootScope.dashboards);
                config.menucolumns = $rootScope.menucolumns;
                config.settings = $rootScope.settings;
                config.customwidgets = $rootScope.customwidgets;
                return saveServiceConfiguration().then(function () {
                    deferred.resolve();
                }, function () {
                    deferred.reject();
                });
            });

            return deferred.promise;
        }

        function useLocalStorage() {
            $rootScope.currentPanelConfig = undefined;
            localStorageService.set("currentPanelConfig", $rootScope.currentPanelConfig);
        }

        function getCurrentPanelConfig() {
            if (!$rootScope.currentPanelConfig) {
                $rootScope.currentPanelConfig = localStorageService.get("currentPanelConfig");

                if (!$rootScope.currentPanelConfig) {
                    // if it's still not set and we have an initial panel config, switch to it
                    var initialPanelConfig = OH2ServiceConfiguration.initialPanelConfig;
                    if (initialPanelConfig && $rootScope.panelsRegistry[initialPanelConfig]) {
                        $rootScope.currentPanelConfig = initialPanelConfig;
                        localStorageService.set("currentPanelConfig", initialPanelConfig);
                    }
                }
            }

            return $rootScope.currentPanelConfig;
        }

        function useCurrentPanelConfig() {
            var currentPanelConfig = getCurrentPanelConfig();
            if (!currentPanelConfig || !$rootScope.panelsRegistry[currentPanelConfig]) {
                console.warn("Warning: current panel config not found, falling back to local storage!");
                useLocalStorage();
            } else {
                if ($rootScope.panelsRegistry[currentPanelConfig].dashboards)
                    $rootScope.dashboards = angular.copy($rootScope.panelsRegistry[currentPanelConfig].dashboards);
                else
                    $rootScope.dashboards = [];
                if ($rootScope.panelsRegistry[currentPanelConfig].menucolumns)
                    $rootScope.menucolumns = $rootScope.panelsRegistry[currentPanelConfig].menucolumns;
                else
                    $rootScope.menucolumns = 1;
                if ($rootScope.panelsRegistry[currentPanelConfig].settings)
                    $rootScope.settings = $rootScope.panelsRegistry[currentPanelConfig].settings;
                else
                    $rootScope.settings = {};
                if ($rootScope.panelsRegistry[currentPanelConfig].customwidgets)
                    $rootScope.customwidgets = $rootScope.panelsRegistry[currentPanelConfig].customwidgets;
                else
                    $rootScope.customwidgets = {};
            }
        }

        function setCurrentPanelConfig(name) {
            $rootScope.currentPanelConfig = name;
            localStorageService.set("currentPanelConfig", $rootScope.currentPanelConfig);
            useCurrentPanelConfig();
        }
    }
})();

1 Like

Thanks for sharing.

Can you explain where can I find the openhab.service.js file? Should I compile something manually or can I just edit the file?

(Currently, I’m using the default settings of openhabian).

I do not think you can edit this easily on your installation directly.

What I did was (following the documentation about contributing)

  • install eclipse
  • get the habpanel source code from github
  • figured out how the building works (documentation on github - angular stuff needs a few extra steps as I understand)
  • change the source, built a jar file
  • removed the original habpanel package using the karaf console
  • place my local build (jar file) in the add-ons folder of my openhab installation

This worked well enough for me, but I am not sure if this is the only/best way.
Ideally some “expert” will see this and decide how to move forward with it.

1 Like

Hi simonleeb.
Are there news about the runtime including the new written code of habpanel? Can i copy the code at the end of openhab.service.js?
Thanks and greetings.

I do not think there has been any progress on this.

The code I posted is the entire file, so I would rather be a replace - It will be best if you diff it with the version you have to see if there have been any other changes to it.

I am having the same problem…
Habpanel stops updating on my wall tab after a while (minutes, hours…)
closing the browser and restarting (FKB, habpanel viewer, chrome) fixes it
while the updates have stopped, pushing the “refresh” button on habpanel updates habpanel, but the automatic updates never resume… only solution is reloading habpanel page or restarting browser

Hi together.
Are there new infos to solve the problem without reloading the Page?
Greetings Markus

this should solve the problem: [SOLVED] HABPanel sluggish