pimatic / rfcontroljs

nodejs modul with protocol support for different 433mhz switches and weather stations for the RFControl Arduino library.
GNU General Public License v3.0
49 stars 54 forks source link

Add weather 19 Landmann BBQ Thermometer #112

Closed Flop2006 closed 7 years ago

Flop2006 commented 7 years ago

See here: https://forum.pimatic.org/topic/1768/protocol-for-thermometer weather19.js:

module.exports = function(helper) {
  var protocolInfo, pulsesToBinaryMapping;
  pulsesToBinaryMapping = {
    '01': '0',
    '02': '1',
    '03': ''
  };
  return protocolInfo = {
    name: 'weather19',
    type: 'weather',
    values: {
      temperature: {
        type: "number"
      },
      id: {
        type: "number"
      },
      channel: {
        type: "number"
      },
    },
    brands: ["Landmann BBQ Thermometer"],
    pulseLengths: [548, 1008, 1996, 3936],
    pulseCount: 66,
    decodePulses: function(pulses) {
     /*
      Pulse like:
      |020202010101|0101|01010101010101020101010102010201|0201010102010202|03|
      | 1 1 1 0 0 0| 0 0| 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0| 1 0 0 0 1 0 1 1|  |
      |ID          | CH |            Temperature         |?               |  |
      |56          |  1 |            266(26.6)           |?               |  |
    */
      var binary, result;
      binary = helper.map(pulses, pulsesToBinaryMapping);
      return result = {
        id: helper.binaryToNumber(binary, 0, 5),
        temperature: helper.binaryToSignedNumber(binary, 8, 23) / 10,
        channel: helper.binaryToNumber(binary, 6, 7) + 1,
      };
    }
  };
};

weather19.coffee:

module.exports = (helper) ->
  pulsesToBinaryMapping = {
    '01': '0' #binary 0
    '02': '1' #binary 1
    '03': ''  #footer
  }
  return protocolInfo = {
    name: 'weather19'
    type: 'weather'
    values:
      temperature:
        type: "number"
      id:
        type: "number"
      channel:
        type: "number"
    brands: ["Landmann BBQ Thermometer"]
    pulseLengths: [548, 1008, 1996, 3936]
    pulseCount: 66
    decodePulses: (pulses) ->
      # pulses is something like: '020202010101010101010101010101020101010102010201020101010201020203'
      # we first map the pulse sequences to binary
      binary = helper.map(pulses, pulsesToBinaryMapping)
      # binary is now something like: '11100000000000010000101010001011'
      # now we extract the temperature and humidity from that string
      #1110 0000 0000 0001 0000 1010 1000 1011
      # IIII IICC TTTT TTTT TTTT TTTT xxxx xxxx
      #0    4    8    12   16   20   24   28  
      # I: Device ID, 6-bit unsigned Int
      # C: Channel (2 bits + 1, 00=1, 01=2, 10=3)  
      # T: Temperature Value, 16-bit signed Int (divide decimal by 10)
      # x: Unused
      return result = {
        id: helper.binaryToNumber(binary, 0, 5)
        channel: helper.binaryToNumber(binary, 6, 7) + 1
        temperature: helper.binaryToSignedNumber(binary, 8, 23) / 10
      }
}

lib-controller.coffee part

      protocol: 'weather19'
      pulseLengths: [548, 1008, 1996, 3936]
      pulses: [
        '020202010101010101010101010101020101010102010201020101010201020203'
        '020102020102010101010101010101020101020101020201010102020201020203'
        '020102020102010101010101010101020101020201010101020102010102010203'
        '020102020102010101010101010101020202010102020101020102010101010203'
      ]
      values: [
        { id: 56, channel: 1, temperature: 26.6 }
        { id: 45, channel: 1, temperature: 29.4 }
        { id: 45, channel: 1, temperature: 30.4 }
        { id: 45, channel: 1, temperature: 46.0 }
      ]
    },

controller.js including changes for weather 17 and 18

var doesProtocolMatch, helper, protocols, sortIndices,
  __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

helper = require('./helper');

protocols = ['weather1', 'weather2', 'weather3', 'weather4', 'weather5', 'weather6', 'weather7', 'weather8', 'weather9', 'weather10', 'weather11', 'weather12', 'weather13', 'weather14', 'weather15', 'weather16', 'weather17', 'weather18', 'weather19', 'switch1', 'switch2', 'switch3', 'switch4', 'switch5', 'switch6', 'switch7', 'switch8', 'switch9', 'switch10', 'switch11', 'switch12', 'switch13', 'switch14', 'switch15', 'switch16', 'switch17', 'switch18', 'switch19', 'switch20', 'switch21', 'switch22', 'switch23', 'switch24', 'switch25', 'switch26', 'switch27', 'switch28', 'rolling1', 'dimmer1', 'dimmer2', 'pir1', 'pir2', 'pir3', 'pir4', 'pir5', 'contact1', 'contact2', 'contact3', 'generic', 'generic2', 'alarm1', 'alarm2', 'led1', 'led2', 'led3', 'led4', 'doorbell1', 'doorbell2', 'doorbell3', 'awning1', 'awning2', 'shutter1', 'shutter3', 'shutter4', 'shutter5', 'rawswitch'];

protocols = protocols.map((function(_this) {
  return function(p) {
    return require("./protocols/" + p)(helper);
  };
})(this));

doesProtocolMatch = function(pulseLengths, pulses, protocol) {
  var i, maxDelta, _ref;
  if (protocol.pulseCounts != null) {
    if (_ref = pulses.length, __indexOf.call(protocol.pulseCounts, _ref) < 0) {
      return false;
    }
  } else {
    if (pulses.length !== protocol.pulseCount) {
      return false;
    }
  }
  if (pulseLengths.length !== protocol.pulseLengths.length) {
    return false;
  }
  i = 0;
  while (i < pulseLengths.length) {
    maxDelta = pulseLengths[i] * 0.4;
    if (Math.abs(pulseLengths[i] - protocol.pulseLengths[i]) > maxDelta) {
      return false;
    }
    i++;
  }
  return true;
};

sortIndices = function(array) {
  var e, i, indices, j, tuple, tuples, _i, _j, _len, _len1;
  tuples = new Array(array.length);
  for (i = _i = 0, _len = array.length; _i < _len; i = ++_i) {
    e = array[i];
    tuples[i] = [e, i];
  }
  tuples.sort(function(left, right) {
    if (left[0] < right[0]) {
      return -1;
    } else {
      return 1;
    }
  });
  indices = new Array(array.length);
  for (j = _j = 0, _len1 = tuples.length; _j < _len1; j = ++_j) {
    tuple = tuples[j];
    indices[tuple[1]] = j;
  }
  return indices;
};

module.exports = {
  debug: false,
  compressTimings: function(timings) {
    var bucket, buckets, counts, hasMatch, i, j, pulses, sums, timing, _i, _j, _k, _len, _len1, _len2;
    pulses = '';
    buckets = [];
    sums = [];
    counts = [];
    for (i = _i = 0, _len = timings.length; _i < _len; i = ++_i) {
      timing = timings[i];
      hasMatch = false;
      for (j = _j = 0, _len1 = buckets.length; _j < _len1; j = ++_j) {
        bucket = buckets[j];
        if (Math.abs(bucket - timing) < bucket * 0.5) {
          pulses += j;
          sums[j] += timing;
          counts[j]++;
          hasMatch = true;
        }
      }
      if (!hasMatch) {
        pulses += buckets.length;
        buckets.push(timing);
        sums.push(timing);
        counts.push(1);
      }
    }
    for (j = _k = 0, _len2 = buckets.length; _k < _len2; j = ++_k) {
      bucket = buckets[j];
      buckets[j] = Math.round(sums[j] / counts[j]);
    }
    return {
      buckets: buckets,
      pulses: pulses
    };
  },
  prepareCompressedPulses: function(input) {
    var parts, pulseLengths, pulses;
    parts = input.split(' ');
    pulseLengths = parts.slice(0, 8);
    pulses = parts[8];
    pulseLengths = pulseLengths.filter(function(puls) {
      return puls !== '0';
    }).map(function(puls) {
      return parseInt(puls, 10);
    });
    return this.sortCompressedPulses(pulseLengths, pulses);
  },
  sortCompressedPulses: function(pulseLengths, pulses) {
    var sortedIndices;
    sortedIndices = sortIndices(pulseLengths);
    pulseLengths.sort(function(l, r) {
      return l - r;
    });
    pulses = helper.mapByArray(pulses, sortedIndices);
    return {
      pulseLengths: pulseLengths,
      pulses: pulses
    };
  },
  fixPulses: function(pulseLengths, pulses) {
    var i, newPulseLength, newPulseLengths, newPulses;
    if (pulseLengths.length <= 3) {
      return null;
    }
    i = 1;
    while (i < pulseLengths.length) {
      if (pulseLengths[i - 1] * 2 < pulseLengths[i]) {
        i++;
        continue;
      }
      newPulseLength = Math.floor((pulseLengths[i - 1] + pulseLengths[i]) / 2);
      newPulseLengths = pulseLengths.slice();
      newPulseLengths.splice(i - 1, 2, newPulseLength);
      break;
    }
    if (i === pulseLengths.length) {
      return null;
    }
    newPulses = pulses;
    while (i < pulseLengths.length) {
      newPulses = newPulses.replace(new RegExp("" + i, 'g'), "" + (i - 1));
      i++;
    }
    return {
      pulseLengths: newPulseLengths,
      pulses: newPulses
    };
  },
  decodePulses: function(pulseLengths, pulses) {
    var err, fixed, p, results, values, _i, _len;
    results = [];
    for (_i = 0, _len = protocols.length; _i < _len; _i++) {
      p = protocols[_i];
      if (doesProtocolMatch(pulseLengths, pulses, p)) {
        try {
          values = p.decodePulses(pulses);
          results.push({
            protocol: p.name,
            values: values
          });
        } catch (_error) {
          err = _error;
          if (this.debug) {
            if (err instanceof helper.ParsingError) {
              console.log("Warning trying to parse message with protocol " + p.name + ": " + err.message);
              console.log("" + (err.stack.split("\n")[2]));
            } else {
              throw err;
            }
          }
        }
      }
    }
    fixed = this.fixPulses(pulseLengths, pulses);
    if (fixed == null) {
      return results;
    }
    return results.concat(this.decodePulses(fixed.pulseLengths, fixed.pulses));
  },
  encodeMessage: function(protocolName, message) {
    var p, protocol, _i, _len;
    protocol = null;
    for (_i = 0, _len = protocols.length; _i < _len; _i++) {
      p = protocols[_i];
      if (p.name === protocolName) {
        protocol = p;
        break;
      }
    }
    if (protocol == null) {
      throw new Error("Could not find a protocol named " + protocolName);
    }
    if (protocol.encodeMessage == null) {
      throw new Error("The protocol has no send report.");
    }
    return {
      pulses: protocol.encodeMessage(message),
      pulseLengths: protocol.pulseLengths
    };
  },
  getAllProtocols: function() {
    return protocols;
  },
  getProtocol: function(protocolName) {
    var p, _i, _len;
    for (_i = 0, _len = protocols.length; _i < _len; _i++) {
      p = protocols[_i];
      if (p.name === protocolName) {
        return p;
      }
    }
    return null;
  }
};
mwittig commented 7 years ago

Excellent! Thank you very much for your contribution. I am going to integrate this asap.

mwittig commented 7 years ago

released rfcontroljs@0.0.56, homeduino@0.0.62, pimatic-homeduino@0.9.10