docs: function connectws(arg1, arg2, arg3) {
      let onconnect_func, ondisconnect_func, hostname;
      if (typeof arg1 == 'object') {
          onconnect_func = arg1.onconnect;
          ondisconnect_func = arg1.ondisconnect;
          hostname = arg1.hostname;
      } else {
          onconnect_func = arg1;
          ondisconnect_func = arg2;
          hostname = arg3;
      }
      if (onconnect_func == null) onconnect_func = ()=>{};
      if (ondisconnect_func == null) ondisconnect_func = ()=>{};
      if (hostname == null) hostname = location.host;
  
      console.log('Trying to connect to '+hostname+'...');
      start_spinner();
  
      let connection = new WebSocket('ws://'+hostname, ['picogw']);
  
      let tid = 0;
      let waitlist = {};
      let sublist = {};
  
      connection.onopen = function() {
          let picogw = {
              callproc: (args)=>{
                  return new Promise((ac, rj)=>{
                      start_spinner();
                      if (typeof args == 'string') {
                          args = {method: 'GET', path: args};
                      }
                      args.tid = tid;
  
                      let timerId;
  
                      function cleanUp(){
                          if( timerId == undefined ) return false;
                          timerId = undefined;
                          stop_spinner();
                          return true;
                      }
                      timerId = setTimeout(()=>{
                          if(!cleanUp()) return;
                          rj.apply({},[].slice.call(arguments));
                      },CALLPROC_TIMEOUT);
  
                      waitlist[tid] = [ //ac,rj];
                          function(){
                              if(!cleanUp()) return;
                              ac.apply({},[].slice.call(arguments));
                          }, function(){
                              if(!cleanUp()) return;
                              rj.apply({},[].slice.call(arguments));
                          }];
                      tid++;
                      connection.send(JSON.stringify(args));
                  });
              },
              sub: (path, callback) => {
                  if (typeof path == 'object') {
                      callback = path.callback;
                      path = path.path;
                  }
                  if (path.slice(-1) == '/') path = path.slice(0, -1);
                  const path_low = path.toLowerCase();
                  if (sublist[path_low] == undefined) {
                      sublist[path_low]={
                          single_callback: function(re) {
                              if (re[path_low] != null) {
                              }
                              if (re[path_low] == null) {
                                  this.callbacks.forEach((so)=>{
                                      so.callback(re);
                                  });
                              } else {
                                  const payload = re[path_low];
                                  delete re[path_low];
                                  this.callbacks.forEach((so)=>{
                                      re[so.origpath] = payload;
                                      so.callback(re);
                                      delete re[so.origpath];
                                  });
                              }
                          },
  						 callbacks: [],
                      };
                      connection.send(JSON.stringify({method: 'SUB', path: path_low, tid: 4649}));
                  }
                  sublist[path_low].callbacks.push({origpath: path, callback: callback});
              },
              unsub: (path, callback) => {
                  if (typeof path == 'object') {
                      callback = path.callback;
                      path = path.path;
                  } else if (typeof path == 'function') {
                      callback = path;
                      path = null;
                  }
  
                  if (path != null) {
                      if (path.slice(-1) == '/') path = path.slice(0, -1);
                      const path_low = path.toLowerCase();
                      if (sublist[path_low]==undefined) return;
  
                      sublist[path_low].callbacks =
                          sublist[path_low].callbacks.filter((so)=>{
                              return !(so.origpath == path
                                       && (callback==null || so.callback == callback));
                          });
  
                      if (sublist[path_low].callbacks.length == 0) {
                          connection.send(JSON.stringify({method: 'UNSUB', path: path_low}));
                          delete sublist[path_low];
                      }
                  } else if (callback != null) {
                      for (path_low in sublist) {
                          sublist[path_low].callbacks =
                              sublist[path_low].callbacks.filter((so)=>{
                                  return so.callback != callback;
                              });
  
                          if (sublist[path_low].callbacks.length == 0) {
                              connection.send(JSON.stringify({method: 'UNSUB', path: path_low}));
                              delete sublist[path_low];
                          }
                      }
                  } else {
                      for (path_low in sublist) {
                          connection.send(JSON.stringify({method: 'UNSUB', path: path_low}));
                      }
                      sublist = {};
                  }
              },
          };
          stop_spinner();
  	    console.log('Connected to '+location.host+'.');
          onconnect_func(picogw);
      };
      connection.onmessage = function(e) {
          // console.log('Server: ' + JSON.stringify(JSON.parse(e.data),null,"\t")) ;
          try {
              let ret = JSON.parse(e.data);
              if (ret.method == 'PUB') {
                  for (let path in ret) {
                      if (sublist[path] != undefined) {
                          sublist[path].single_callback(ret);
                      }
                  }
              } else if (waitlist[ret.tid] != undefined) {
                  let tid = ret.tid;
                  delete ret.tid;
                  waitlist[tid][0](ret);
                  delete waitlist[ret.tid];
              }
          } catch (e) {
              console.error(e);
          }
      };
  
      /* connection.onerror = function(){
  		start_spinner();
  	} ;*/
      connection.onclose = function() {
          start_spinner();
          for (let tid in waitlist) {
              waitlist[tid][1]({error: 'Connection closed.'});
          }
      	waitlist = {};
      	sublist = {};
      	// picogw = undefined ;
          console.log('Websocket disconnected. Retrying in 3 secs.');
          setTimeout(()=>{
              connectws({
                  onconnect: onconnect_func,
  				 ondisconnect: ondisconnect_func,
  				 hostname: hostname,
              });
          }, 3000);
      };
  }