Homey.util - Joolee/Homey-unofficial-documentation GitHub Wiki

An undocumented feature of the Homey class is the util property.

Using API v2, it has these functions:

Note: As these methods are undocumented, Athom is free to change or remove them at any time. Use these at your own risk or duplicate the functions in your own app.js.

callbackAfterPromise

function(self, func, args, cb) {
  args = Array.prototype.slice.apply(args);
  const lastarg = args.pop();
  if (!cb) cb = lastarg;
  func.apply(self, args).then((res) => {
    try {
      cb(null, res);
    } catch (e) {
      process.nextTick(() => {
        throw e;
      });
    }
  }).catch((err) => {
    try {
      cb(err);
    } catch (e) {
      process.nextTick(() => {
        throw e;
      });
    }
  });
}

capitalizeFirstLetter

function(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

deepCompare

function() {
  let i,
    l,
    leftChain,
    rightChain;
  
  function compare2Objects(x, y) {
    let p;
  
    // remember that NaN === NaN returns false
    // and isNaN(undefined) returns true
    if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
      return true;
    }
  
    // Compare primitives and functions.
    // Check if both arguments link to the same object.
    // Especially useful on the step where we compare prototypes
    if (x === y) {
      return true;
    }
  
    // Works in case when functions are created in constructor.
    // Comparing dates is a common scenario. Another built-ins?
    // We can even handle functions passed across iframes
    if ((typeof x === 'function' && typeof y === 'function') ||
      (x instanceof Date && y instanceof Date) ||
      (x instanceof RegExp && y instanceof RegExp) ||
      (x instanceof String && y instanceof String) ||
      (x instanceof Number && y instanceof Number)) {
      return x.toString() === y.toString();
    }
  
    // At last checking prototypes as good as we can
    if (!(x instanceof Object && y instanceof Object)) {
      return false;
    }
  
    if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
      return false;
    }
  
    if (x.constructor !== y.constructor) {
      return false;
    }
  
    if (x.prototype !== y.prototype) {
      return false;
    }
  
    // Check for infinitive linking loops
    if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
      return false;
    }
  
    // Quick checking of one object being a subset of another.
    // todo: cache the structure of arguments[0] for performance
    for (p in y) {
      if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
        return false;
      } else if (typeof y[p] !== typeof x[p]) {
        return false;
      }
    }
  
    for (p in x) {
      if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
        return false;
      } else if (typeof y[p] !== typeof x[p]) {
        return false;
      }
  
      switch (typeof (x[p])) {
        case 'object':
        case 'function':
  
          leftChain.push(x);
          rightChain.push(y);
  
          if (!compare2Objects(x[p], y[p])) {
            return false;
          }
  
          leftChain.pop();
          rightChain.pop();
          break;
  
        default:
          if (x[p] !== y[p]) {
            return false;
          }
          break;
      }
    }
  
    return true;
  }
  
  if (arguments.length < 1) {
    return true; // Die silently? Don't know how to handle such case, please help...
    // throw "Need two or more arguments to compare";
  }
  
  for (i = 1, l = arguments.length; i < l; i++) {
    leftChain = []; // Todo: this can be cached
    rightChain = [];
  
    if (!compare2Objects(arguments[0], arguments[i])) {
      return false;
    }
  }
  
  return true;
}

protectClass

function(c, map) {
  Object.getOwnPropertyNames(c.prototype).forEach((key) => {
    if (key === '__debug') return;
    if (key.indexOf('__') !== 0) return;
  
    const fn = c.prototype[key];
    delete c.prototype[key];
  
    map[key] = Symbol();
    c.prototype[map[key]] = fn;
  });
}

recursiveDeepCopy

function(o, visited = new Set()) {
  if (typeof o !== 'object') return o;
  if (!o) return o;
  if (visited.has(o)) return null;
  visited.add(o);
  if (o.constructor === Array) return o.map(e => Homey.utilRecursiveDeepCopy(e, visited));
  if (o.constructor === Date) return new Date(o);
  if (o instanceof Error) return o;
  const r = {};
  if (o.constructor === Object) {
    for (const i in o) r[i] = Homey.utilRecursiveDeepCopy(o[i], visited);
  }
  return r;
}

uuid

Note: This method does not generate a real UUID, just a string that looks like one, which will be enough for most use cases.

function() {
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1);
  }
  
  return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
}