Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Hardware
    4. Buderus 4121 - Einbindung über EcoCan [läuft]

    NEWS

    • Neues Video "KI im Smart Home" - ioBroker plus n8n

    • Neues Video über Aliase, virtuelle Geräte und Kategorien

    • Wir empfehlen: Node.js 22.x

    Buderus 4121 - Einbindung über EcoCan [läuft]

    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      DaBibo last edited by

      Hallo zusammen,

      ich habe meine Buderus 4121 selbst über einen Raspberry zero 2 W und einem CAN-Hat (Habe den von waveshare genutzt, würde aber im erneuten Fall ein anderes nehmen, mit 8/16 Mhz - das von waveshare hat 12MHz und die Abfragen auf auf dem PI zeigen was anderes an.. war ein wenig Fehlersuche..) angebunden über zusammen mit chatgpt ein JavaScript geschrieben um die Daten per MQTT zu übertragen.

      #!/usr/bin/env node
      /**
       * Buderus System 4000 CAN -> MQTT Decoder (ALL MODULES, FULL BITS, RAW)
       * ---------------------------------------------------------------------
       * - SocketCAN (ID 0x400 Monitor), 60-Byte-Reassembly je Typ 0x80..0x9E
       * - Sprechende Felder + vollständige Bit-Dekodierung gemäß Doku
       * - Zusätzlich immer: Block-JSON, RAW-Bytes b00..b59, Wörter w00..w58 (u16be),
       *   Triplets t00..t57 (u24). Keine Wertebegrenzung/Clamps.
       *
       * ENV:
       *   CAN_IF=can0
       *   MQTT_URL=mqtt://USER:PASS@<BROKER_IP>:1883
       *   MQTT_BASE=buderus/eco
       *   PUBLISH_ONLY_SEEN=1   # nur publizieren, wenn Block wirklich empfangen (Default 1)
       *   STALE_MS=0            # >0: unterdrückt Veröffentlichung je Typ nach Inaktivität
       *   PUBLISH_BLOCK_JSON=1  # Block-JSON an/aus
       *   PUBLISH_RAW_BYTES=1   # b00..b59
       *   PUBLISH_WORDS=1       # w00,w02,...,w58
       *   PUBLISH_TRIPLETS=1    # t00..t57
       */
      
      var mqtt = require('mqtt');
      var can  = require('socketcan');
      
      // ---------- Konfig ----------
      var CAN_IF   = process.env.CAN_IF   || 'can0';
      var MQTT_URL = process.env.MQTT_URL || 'mqtt://127.0.0.1:1883';
      var MQTT_BASE = (process.env.MQTT_BASE || 'buderus/eco').replace(/\/+$/,'');
      var RETAIN = true, QOS = 0;
      
      var PUBLISH_ONLY_SEEN   = (process.env.PUBLISH_ONLY_SEEN   || '1') === '1';
      var STALE_MS            = parseInt(process.env.STALE_MS || '0', 10);
      var PUBLISH_BLOCK_JSON  = (process.env.PUBLISH_BLOCK_JSON  || '1') === '1';
      var PUBLISH_RAW_BYTES   = (process.env.PUBLISH_RAW_BYTES   || '1') === '1';
      var PUBLISH_WORDS       = (process.env.PUBLISH_WORDS       || '1') === '1';
      var PUBLISH_TRIPLETS    = (process.env.PUBLISH_TRIPLETS    || '1') === '1';
      
      // ---------- Helper ----------
      function u8(buf,i){ return (buf[i]!==undefined)?buf[i]:0; }
      function i8(buf,i){ var v=u8(buf,i); return v>=128?(v-256):v; }
      function u16be(buf,i){ return ((u8(buf,i)<<8)|u8(buf,i+1))>>>0; }
      function i16be(buf,i){ var v=u16be(buf,i); return (v&0x8000)?(v-0x10000):v; }
      function u24(b3,b2,b1){ return (((b3&0xFF)<<16)|((b2&0xFF)<<8)|(b1&0xFF))>>>0; }
      function bits(byte, names){
        var o={}, b;
        for(b=0;b<8;b++){
          var key = names && names[b] ? names[b] : ('b'+(b+1));
          o[key] = !!(byte & (1<<b));
        }
        return o;
      }
      function put(obj, path, val){
        var p=path.split('/'),cur=obj;
        for (var i=0;i<p.length-1;i++){ cur[p[i]]=cur[p[i]]||{}; cur=cur[p[i]]; }
        cur[p[p.length-1]] = val;
      }
      function tname(t){
        var map = {
          0x80:'hk1',0x81:'hk2',0x82:'hk3',0x83:'hk4',0x8A:'hk5',0x8B:'hk6',0x8C:'hk7',0x8D:'hk8',0x8E:'hk9',
          0x84:'warmwasser',0x88:'kessel',0x89:'konfig',0x8F:'strategie_boden',0x90:'lap',
          0x92:'uba1',0x93:'uba2',0x94:'uba3',0x95:'uba4',0x96:'uba5',0x97:'uba6',0x98:'uba7',0x99:'uba8',
          0x9B:'waermemenge',0x9C:'stoermodul',0x9D:'unterstation',0x9E:'solar'
        };
        return map[t] || ('type_'+t.toString(16));
      }
      function ts(){ return new Date().toISOString(); }
      
      // ---------- Bit-Labels aus Doku ----------
      var HK_BW1 = ['ausschaltopt','einschaltopt','automatik','ww_vorrang','estrichtrocknung','ferien','frostschutz','manuell'];
      var HK_BW2 = ['sommer','tag','fb_keine_kommunikation','fb_fehlerhaft','fehler_vorlauf','max_vorlauf','ext_stoereingang','party_pause'];
      var HK_EIN = ['eingang_wf2','eingang_wf3',null,null,null,'schalter_0','schalter_hand','schalter_aut'];
      
      var WW_BW1 = ['automatik','desinfektion','nachladung','ferien','fehler_desinfektion','fehler_fuehler','ww_bleibt_kalt','fehler_anode'];
      var WW_BW2 = ['laden','manuell','nachladen','ausschaltopt','einschaltopt','tag','warm','vorrang'];
      var WW_PUMP = ['ladepumpe','zirkulationspumpe','absenkung_solar',null,null,null,null,null];
      var WW_ZIRK = ['tag','automatik','ferien','einmallauf_3min',null,null,null,null];
      
      var SOL_BW1 = ['fehler_einst_hysterese','sp2_auf_max_temp','sp1_auf_max_temp','kollektor_auf_max_temp',null,null,null,null];
      var SOL_BW2 = ['fehler_fuehler_arl_bypass','fehler_fuehler_sp_mitte_bypass','fehler_volumenstrom_wz','fehler_fuehler_rl_wz','fehler_fuehler_vl_wz','fehler_fuehler_sp2_unten','fehler_fuehler_sp1_unten','fehler_fuehler_kollektor'];
      var SOL_BW3 = ['usv_sp2_zu','usv_sp2_auf_oder_ladepumpe2','usv_bypass_zu','usv_bypass_auf','sekundaerpumpe_sp2_betrieb',null,null,null];
      
      // UBA/EMS Bits (wandhängend)
      var UBA_STATUS = ['untergruppe_b0','untergruppe_b1','untergruppe_b2','hauptgruppe_b0','hauptgruppe_b1','hauptgruppe_b2','hauptgruppe_b3','blockierender_fehler_uba'];
      var UBA_HD     = ['ww_anforderung','ein_aus_raumthermostat','anforderung_schnittstelle','frostschutz','pumpennachlauf_wegen_ww','ww_anforderung_fuehler','ww_anforderung_durchfluss','brenner_an'];
      var UBA_BETRZ  = ['heizanforderung','ww_anforderung','jumper_11kw_entfernt','betriebstemperatur_betrieb','kesselschutz_taupunkt','verriegelt_service','blockiert','servicemeldung'];
      var UBA_REL1   = ['magnetventil_stufe1','magnetventil_stufe2','geblaese','zuendung','oelvorwaermung_abgasklappe','kesselkreispumpe_heizkreispumpe','3_wegeventil','ww_zirkulationspumpe'];
      var UBA_REL2   = ['ww_ladepumpe','fluessiggasventil','gwp_umwaelzpumpe',null,null,null,null,null];
      var UBA_EMS_FEHLER = ['luftfuehler_fa_defekt','betriebstemperatur_nicht_erreicht','oelvorwaermer_dauersignal','oelvorwaermer_ohne_signal',null,null,null,null];
      var UBA_FEHL_EINST = ['jumper_11kw_kaskade','bc10_notbetrieb','ww_poti_nicht_aut','kesselpoti_nicht_aut_90c','anforderung_klemme_wa',null,'kommunikation_vorhanden_fm458','keine_kommunikation_fm458'];
      
      // Kessel bodenstehend (0x88) – Brennerstatusbits (34)
      var KES_BRENNERSTATUS = ['abgastest','brenner_runter_aus','brenner_auto','brenner_1_stufe','unter_betrieb','leistung_frei','leistung_hoch','betriebsstunden_2_stufe'];
      
      // ---------- Schema-Bausteine ----------
      function F(name, byte, type, unit, scale){ return {name:name, byte:byte, type:type, unit:unit, scale:scale}; }
      function Fb(name, byte, names){ return {name:name, byte:byte, type:'u8_bits', bitnames:names}; }
      function Fu24(name, b3,b2,b1, unit){ return {name:name, bytes:[b3,b2,b1], type:'u24', unit:unit}; }
      function Fu16be(name, b, unit, scale){ return {name:name, bytes:[b,b+1], type:'u16be', unit:unit, scale:scale}; }
      
      // ---------- Schemata ----------
      // Heizkreise 0x80..0x83,0x8A..0x8E
      function HK_SCHEMA(){
        return [
          Fb('betriebswerte1',0,HK_BW1),
          Fb('betriebswerte2',1,HK_BW2),
          F('vorlauf_soll',2,'u8','°C'),
          F('vorlauf_ist',3,'u8','°C'),
          F('raum_soll',4,'u8','°C',0.5),
          F('raum_ist',5,'u8','°C',0.5),
          F('einschaltopt_min',6,'u8','min'),
          F('ausschaltopt_min',7,'u8','min'),
          F('pumpe',8,'u8','%'),
          F('stellglied',9,'u8','%'),
          Fb('hk_eingang',10,HK_EIN),
          // 11..17 „FREI/Heizkennlinie“ -> RAW deckt ab
        ];
      }
      
      // Warmwasser 0x84
      var SCHEMA_84 = [
        Fb('betriebswerte1',0,WW_BW1),
        Fb('betriebswerte2',1,WW_BW2),
        F('ww_soll',2,'u8','°C'),
        F('ww_ist',3,'u8','°C'),
        F('einschaltopt_min',4,'u8','min'),
        Fb('pumpen_bits',5,WW_PUMP),
        // 6..7 reserviert/RAW
        Fb('zirkulationspumpe',8,WW_ZIRK),
        Fb('wf_eingaenge',9,['eingang2','eingang3',null,null,null,'handschalter_0','handschalter_hand','handschalter_aut']),
        F('start_ladung',10,'u8','°C'),
        F('ende_ladung',11,'u8','°C'),
        F('t_speicher_unten',12,'u8','°C'),
        F('t_waermetauscher',13,'u8','°C'),
        F('mischer_soll',14,'u8','%'),
        F('mischer_ist',15,'u8','%'),
        F('prim_pumpe',16,'u8','%'),
        F('sek_pumpe',17,'u8','%')
      ];
      
      // Kessel bodenstehend 0x88 (repräsentative/benannte Felder + Brenner-Statusbits)
      var SCHEMA_88 = [
        F('kessel_vl_soll',0,'u8','°C'),
        F('kessel_vl_ist',1,'u8','°C'),
        // 2..5 RAW / spezifisch pro ZM
        F('brenner_ansteuerung',8,'u8'),
        F('abgastemperatur',9,'u8','°C'),
        F('mod_brenner_stellglied',10,'u8','%'),
        F('mod_brenner_ist_leistung',11,'u8','%'),
        // 12..33 diverse Laufzeiten/Starts – RAW deckt Zahlen vollständig ab
        Fb('brenner_statusbits',34,KES_BRENNERSTATUS)
      ];
      
      // Strategie Boden 0x8F (Auszug + RAW)
      var SCHEMA_8F = [
        F('anlagen_vl_soll',0,'u8','°C'),
        F('vl_ist',1,'u8','°C'),
        F('rl_soll',2,'u8','°C'),
        F('rl_ist',3,'u8','°C')
      ];
      
      // Konfiguration 0x89 (vollständig laut Tabelle)
      var SCHEMA_89 = [
        F('aussentemperatur',0,'i8','°C'),
        F('aussentemperatur_gedaempft',1,'i8','°C'),
        // 2,3 Versionen (Direktmodus) -> RAW
        F('slot1',6,'u8'),
        F('slot2',7,'u8'),
        F('slot3',8,'u8'),
        F('slot4',9,'u8'),
        F('slotA',10,'u8'),
        // 12..16 Fehlermeldungen je Slot -> RAW zusätzlich vorhanden
        F('anlagen_vl_soll',18,'u8','°C'),
        F('anlagen_vl_ist',19,'u8','°C'),
        Fb('anlagen_flags',20,['puffer_bleibt_kalt','fuehler_ust_fk_defekt','wartezeit_laeuft',null,null,null,null,null]),
        F('max_pumpen_ansteuerung',21,'u8','%'),
        F('max_stellglied',22,'u8','%'),
        F('rg_vorlauf_ist',23,'u8','°C')
      ];
      
      // LAP 0x90
      var SCHEMA_90 = [
        F('ww_soll',4,'u8','°C'),
        F('ww_ist',6,'u8','°C'),
        F('einschaltopt_min',8,'u8','min'),
        F('start_ladung',20,'u8','°C'),
        F('ende_ladung',21,'u8','°C'),
        F('ist_speicher_unten',24,'u8','°C'),
        F('ist_waermetauscher',26,'u8','°C'),
        F('mischer_soll',28,'u8','%'),
        F('mischer_ist',30,'u8','%'),
        F('primaerpumpe',32,'u8','%'),
        F('sekundaerpumpe',34,'u8','%')
      ];
      
      // UBA (wandhängend) 0x92..0x99
      var SCHEMA_UBA = [
        F('soll_modulation',0,'u8','%'),
        F('ist_modulation',1,'u8','%'),
        Fu24('brennerstunden_h',2,3,4,'h'),
        F('brennerminuten',5,'u8','min'),
        F('kessel_vl_soll',6,'u8','°C'),
        F('kessel_vl_ist',7,'u8','°C'),
        F('ww_soll',8,'u8','°C'),
        F('ww_ist',9,'u8','°C'),
        F('antipendel_min',10,'u8','min'),
        Fb('betriebsflag_regelgeraet',11,['antipendel','keine_komm_kse',null,null,null,null,null,null]),
        Fb('betriebsflags_uba_kse',12,['umwaelzpumpe','schornsteinfeger','keine_komm_uba','keine_komm_kse','antipendel','umschaltventil_ww','abgaswaechter','pumpenschalter']),
        Fb('status_uba',13,UBA_STATUS),
        Fb('hd_mode_uba',14,UBA_HD),
        Fu24('brennerstarts',15,16,17,'count'),
        F('version_uba',18,'u8'),
        F('kim_nummer',19,'u8'),
        F('ruecklauf',20,'u8','°C'),
        F('pumpen_mod_uba',21,'u8','%'),
        Fb('anlagenfehler_ems_kessel',22,UBA_EMS_FEHLER),
        // 23: „ems ww“ in älterer Tabelle – als RAW ohnehin da
        {name:'ems_code1', byte:24, type:'ascii'},
        {name:'ems_code2', byte:25, type:'ascii'},
        F('fehlernummer_hi',26,'u8'),
        F('fehlernummer_lo',27,'u8'),
        Fb('brennertyp',28,['bit1','bit2','bit3','bit4','bit5','bit6','bit7','bit8']),
        // 29 zusammen mit 54 als u16be
        {name:'max_leistung_kw', bytes:[54,29], type:'u16be', unit:'kW'},
      
        F('min_leistung_pct',30,'u8','%'),
        F('flammenstrom_uA',31,'u8','µA'),
        F('abgas_temp',32,'u8','°C'),
        F('ansaugluft',33,'i8','°C'),
        F('wasserdruck',34,'u8','bar'),
        Fb('brennerzustand',35,UBA_BETRZ),
        Fb('relais1',36,UBA_REL1),
        Fb('relais2',37,UBA_REL2),
        F('vl_soll_feuerungsautomat',38,'u8','°C'),
        F('ww_ladeart',39,'u8'),
        Fb('fehleinstellungen_ems_kessel',40,UBA_FEHL_EINST),
        Fb('ems_servicemeldungen',41,['keine_meldung',null,null,null,null,null,null,null]),
        // 42..53 Versions-/Kennbytes -> RAW
        F('betriebstemperatur_konst',55,'u8','°C')
      ];
      
      // Wärmemenge 0x9B (Triplets/Zähler kommen zusätzlich als RAW/Triplets)
      var SCHEMA_9B = [
        // einzelne Byte-Zähler exemplarisch – RAW (t..) liefert komplette Zahlen
        F('impulse_heute_hi',5,'u8'),
        F('impulse_heute_lo',6,'u8'),
        F('impulse_woche_hi',11,'u8'),
        F('impulse_woche_lo',12,'u8')
      ];
      
      // Störmodul 0x9C / Unterstation 0x9D – Grundfelder + RAW
      var SCHEMA_9C = [ Fu16be('vl_soll_0_10v',0,'°C') ];
      var SCHEMA_9D = [ Fu16be('anlagen_vorlauf',0,'°C'), Fu16be('vl_soll_0_10v',2,'°C'), F('zubringerpumpe',3,'u8','%') ];
      
      // Solar 0x9E
      var SCHEMA_9E = [
        Fb('betriebswerte1',0,SOL_BW1),
        Fb('betriebswerte2',1,SOL_BW2),
        Fb('betriebswerte3',2,SOL_BW3),
        {name:'kollektor_temp', bytes:[3,4], type:'u16be', unit:'°C', scale:0.1},
        F('pumpe_speicher_mod',5,'u8','%'),
        F('t_sp1_unten',6,'u8','°C'),
        F('status_sp1',7,'u8'),
        F('t_sp2_unten',8,'u8','°C'),
        F('status_sp2',9,'u8'),
        F('t_sp_mitte_bypass',10,'u8','°C'),
        F('t_anl_rl_bypass',11,'u8','°C'),
        F('t_wmz_vorlauf',12,'u8','°C'),
        F('t_wmz_ruecklauf',13,'u8','°C'),
        Fu16be('volumenstrom_lh',14,'l/h'),
        Fu16be('leistung_w',16,'W'),
        Fu24('wm_sp1_100wh',18,19,20,'100Wh'),
        Fu24('wm_sp2_100wh',21,22,23,'100Wh'),
        Fu24('bh_sp1_min',24,25,26,'min'),
        F('ww_sollabsenkung_solar',27,'u8','K'),
        F('ww_sollabsenkung_kapaz',28,'u8','K'),
        F('kollektor_temp_1C',29,'u8','°C'),
        Fu24('bh_sp2_min',30,31,32,'min')
      ];
      
      // Zuordnung Typ -> (Prefix, Schema)
      var TYPE_SCHEMAS = new Map([
        [0x80,{prefix:'hk1', schema:HK_SCHEMA()}],
        [0x81,{prefix:'hk2', schema:HK_SCHEMA()}],
        [0x82,{prefix:'hk3', schema:HK_SCHEMA()}],
        [0x83,{prefix:'hk4', schema:HK_SCHEMA()}],
        [0x8A,{prefix:'hk5', schema:HK_SCHEMA()}],
        [0x8B,{prefix:'hk6', schema:HK_SCHEMA()}],
        [0x8C,{prefix:'hk7', schema:HK_SCHEMA()}],
        [0x8D,{prefix:'hk8', schema:HK_SCHEMA()}],
        [0x8E,{prefix:'hk9', schema:HK_SCHEMA()}],
        [0x84,{prefix:'warmwasser', schema:SCHEMA_84}],
        [0x88,{prefix:'kessel',      schema:SCHEMA_88}],
        [0x8F,{prefix:'strategie_boden', schema:SCHEMA_8F}],
        [0x89,{prefix:'konfig',      schema:SCHEMA_89}],
        [0x90,{prefix:'lap',         schema:SCHEMA_90}],
        [0x92,{prefix:'uba1', schema:SCHEMA_UBA}],
        [0x93,{prefix:'uba2', schema:SCHEMA_UBA}],
        [0x94,{prefix:'uba3', schema:SCHEMA_UBA}],
        [0x95,{prefix:'uba4', schema:SCHEMA_UBA}],
        [0x96,{prefix:'uba5', schema:SCHEMA_UBA}],
        [0x97,{prefix:'uba6', schema:SCHEMA_UBA}],
        [0x98,{prefix:'uba7', schema:SCHEMA_UBA}],
        [0x99,{prefix:'uba8', schema:SCHEMA_UBA}],
        [0x9B,{prefix:'waermemenge', schema:SCHEMA_9B}],
        [0x9C,{prefix:'stoermodul',  schema:SCHEMA_9C}],
        [0x9D,{prefix:'unterstation',schema:SCHEMA_9D}],
        [0x9E,{prefix:'solar',       schema:SCHEMA_9E}]
      ]);
      
      // ---------- Decoder ----------
      function decodeByField(buf, f){
        var val;
        if (f.bytes){
          if (f.type==='u24'){
            val = u24(u8(buf,f.bytes[0]), u8(buf,f.bytes[1]), u8(buf,f.bytes[2]));
          } else if (f.type==='u16be'){
            val = u16be(buf, f.bytes[0]);
          } else { val = 0; }
        } else {
          if (f.type==='u8')      val = u8(buf,f.byte);
          else if (f.type==='i8') val = i8(buf,f.byte);
          else if (f.type==='ascii'){ var b=u8(buf,f.byte); val=(b>=32&&b<=126)?String.fromCharCode(b):''; }
          else if (f.type==='u8_bits'){ val = bits(u8(buf,f.byte), f.bitnames); }
          else if (f.type==='i16be'){ val = i16be(buf,f.byte); }
          else if (f.type==='u16be'){ val = u16be(buf,f.byte); }
          else val = u8(buf,f.byte);
        }
        if (typeof f.scale==='number') val = Number((val * f.scale).toFixed(2));
        if (f.unit) return {v:val, unit:f.unit};
        return val;
      }
      function decodeBlock(typ, buf){
        var t = TYPE_SCHEMAS.get(typ); if (!t) return {};
        var out = {}, i, f, decoded, key;
        for (i=0;i<t.schema.length;i++){
          f = t.schema[i]; decoded = decodeByField(buf, f); key = f.name;
          if (f.type==='u8_bits')      put(out, t.prefix+'/'+key, decoded);
          else if (decoded && typeof decoded==='object' && decoded.v!==undefined && f.unit){
            put(out, t.prefix+'/'+key, decoded.v); put(out, t.prefix+'/'+key+'@unit', f.unit);
          } else {
            put(out, t.prefix+'/'+key, decoded); if (f.unit) put(out, t.prefix+'/'+key+'@unit', f.unit);
          }
        }
        return out;
      }
      
      // ---------- Reassembly ----------
      var blocks = new Map();
      var seen = new Map(); // typ -> lastTs
      TYPE_SCHEMAS.forEach(function(_, typ){ blocks.set(typ, new Uint8Array(60)); });
      
      // ---------- CAN ----------
      var channel;
      try{
        channel = can.createRawChannel(CAN_IF, true);
        channel.addListener('onMessage', function (msg){
          if ((msg.id & 0x7FF) !== 0x400) return;
          var d = msg.data; if (!d || d.length<2) return;
          var typ = d[0], off = d[1], buf = blocks.get(typ);
          if (!buf) return;
          for (var i=2, j=0; i<d.length && (off+j)<buf.length; i++, j++) buf[off+j]=d[i];
          seen.set(typ, Date.now());
        });
        channel.start();
        console.error('[CAN] listening on '+CAN_IF+' (ID 0x400)');
      }catch(e){
        console.error('[CAN] open failed: '+e.message);
        process.exit(1);
      }
      
      // ---------- MQTT ----------
      var client = mqtt.connect(MQTT_URL, { reconnectPeriod:3000 });
      client.on('connect', function(){ console.error('[MQTT] connected '+MQTT_URL); });
      client.on('error',   function(err){ console.error('[MQTT] error: '+err.message); });
      
      setInterval(function(){
        TYPE_SCHEMAS.forEach(function(tdef, typ){
          if (PUBLISH_ONLY_SEEN){
            var last = seen.get(typ);
            if (!last) return;
            if (STALE_MS>0 && (Date.now()-last)>STALE_MS) return;
          }
          var buf = blocks.get(typ); if (!buf) return;
      
          // 1) Sprechende Felder
          publishTree(decodeBlock(typ, buf));
      
          var name = tname(typ);
      
          // 2) Block JSON
          if (PUBLISH_BLOCK_JSON){
            var payload = {
              ts: ts(),
              type: typ, type_name: name,
              length: buf.length,
              bytes: Array.from(buf)
            };
            client.publish(MQTT_BASE+'/blocks/'+typ.toString(16), JSON.stringify(payload), {retain:RETAIN, qos:QOS});
          }
      
          // 3) RAW bytes
          if (PUBLISH_RAW_BYTES){
            for (var b=0;b<60;b++){
              client.publish(MQTT_BASE+'/raw/'+name+'/b'+String(b).padStart(2,'0'), String(buf[b]), {retain:RETAIN, qos:QOS});
            }
          }
      
          // 4) Wörter u16be
          if (PUBLISH_WORDS){
            for (var w=0; w<=58; w+=2){
              client.publish(MQTT_BASE+'/raw/'+name+'/w'+String(w).padStart(2,'0'), String(u16be(buf,w)), {retain:RETAIN, qos:QOS});
            }
          }
      
          // 5) Triplets u24
          if (PUBLISH_TRIPLETS){
            for (var t=0; t<=57; t++){
              client.publish(MQTT_BASE+'/raw/'+name+'/t'+String(t).padStart(2,'0'), String(u24(buf[t],buf[t+1],buf[t+2])), {retain:RETAIN, qos:QOS});
            }
          }
        });
      
        client.publish(MQTT_BASE+'/heartbeat', String(Date.now()), {retain:RETAIN, qos:QOS});
      }, 2000);
      
      function publishTree(tree, base){
        base = base || MQTT_BASE;
        for (var k in tree){
          if (!Object.prototype.hasOwnProperty.call(tree,k)) continue;
          var v = tree[k];
          if (v && typeof v==='object' && !Array.isArray(v)) publishTree(v, base+'/'+k);
          else client.publish(base+'/'+k, String(v), {retain:RETAIN, qos:QOS});
        }
      }
      
      process.on('SIGINT', function(){
        try{ channel.stop(); }catch(e){}
        try{ client.end(); }catch(e){}
        process.exit(0);
      });
      
      

      Funkioniert, wenn man die Can Verbindung zum EcoCan Anschluss unter Can0 hergestellt hat.

      1 Reply Last reply Reply Quote 0
      • First post
        Last post

      Support us

      ioBroker
      Community Adapters
      Donate

      530
      Online

      32.1k
      Users

      80.7k
      Topics

      1.3m
      Posts

      1
      1
      31
      Loading More Posts
      • Oldest to Newest
      • Newest to Oldest
      • Most Votes
      Reply
      • Reply as topic
      Log in to reply
      Community
      Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
      The ioBroker Community 2014-2023
      logo