Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Tester
    4. Adapter Hyundai (Bluelink) oder KIA (UVO)

    NEWS

    • Wir empfehlen: Node.js 22.x

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    • ioBroker goes Matter ... Matter Adapter in Stable

    Adapter Hyundai (Bluelink) oder KIA (UVO)

    This topic has been deleted. Only users with topic management privileges can see it.
    • I
      irdeto @Meister Mopper last edited by irdeto

      Gelöscht

      1 Reply Last reply Reply Quote 0
      • J
        jwartenb last edited by

        Hallo,
        bin neu bei iobroker.
        Nur zur Info, wenn schon bekannt oder nutzlos bitte einfach ignorieren.

        Auto: Hyundai IONIQ 6
        Adapterversion: 3.1.3

        Wie schon oben erwähnt, scheint Hyundai derzeit wild zu ändern. Bei evcc hat man sich damit geholfen, dass man auf den alten Login-Mechanismus zurückgegangen ist. Seitdem funktioniert es wieder.

        Ich habe in der Adapterkonfiguration als Sprache DE eingestellt. Im Log stand dann sinngemäß "DE ist not supportet ... available only ... de ...". Also zumindest bei DE case-sensitive.

        Ich habe dann mal auf EN umgestellt und seitdem bekomme ich Daten.

        VG
        Jochen

        arteck 1 Reply Last reply Reply Quote 0
        • arteck
          arteck Developer Most Active @jwartenb last edited by

          @jwartenb sagte in Adapter Hyundai (Bluelink) oder KIA (UVO):

          Ich habe dann mal auf EN umgestellt und seitdem bekomme ich Daten.

          interessant

          @ilovegym kannst das mal verifizieren ?

          ilovegym 1 Reply Last reply Reply Quote 0
          • ilovegym
            ilovegym @arteck last edited by

            @arteck sagte in Adapter Hyundai (Bluelink) oder KIA (UVO):

            @jwartenb sagte in Adapter Hyundai (Bluelink) oder KIA (UVO):

            Ich habe dann mal auf EN umgestellt und seitdem bekomme ich Daten.

            interessant

            @ilovegym kannst das mal verifizieren ?

            @jwartenb vielen Dank fuer den Tip!!

            @arteck bin zurueck auf die 3.1.6, Sprache auf EN, und siehe da, alle Werte vorhanden.. als waere nie was gewesen.. kein Fehler alles gut..

            Ioniq 5 N Hyundai...

            1 Reply Last reply Reply Quote 0
            • arteck
              arteck Developer Most Active @Peter V. last edited by

              @peter-v installiermal von GIT nochmal

              Peter V. 1 Reply Last reply Reply Quote 0
              • R
                RISSN last edited by

                
                bluelink.0
                2025-08-12 07:18:38.530	error	next auto login attempt in 1 hour or restart adapter manual
                
                bluelink.0
                2025-08-12 07:18:38.530	error	Server is not available or login credentials are wrong
                
                bluelink.0
                2025-08-12 07:18:38.530	error	Error: @EuropeController.login: Could not manage to get token: {"retId":"532d44c3-910c-46f0-8000-68c849590654","retCode":4000,"retMsg":"Bad Request","retSubMsg":"\"clientSecret is wrong. requested clientId=6d477c38-3ca4-4cf3-9557-2a1929a94654 requested clientSecret=secret\"","retValue":null}
                

                bei mir leider noch kein Login möglich beim Hyundai Tucson

                W 1 Reply Last reply Reply Quote 0
                • Peter V.
                  Peter V. @arteck last edited by

                  @arteck
                  Danke, sah erst gut aus, nach einer aktualisierung kommt jetzt:

                  bluelink.0
                  	2025-08-12 18:28:20.363	error	Cannot read properties of undefined (reading 'Odometer')
                  bluelink.0
                  	2025-08-12 18:28:20.362	error	Error on API-Request Status, ErrorCount:1
                  bluelink.0
                  	2025-08-12 18:28:19.503	debug	{"chassis":{"hoodOpen":false,"trunkOpen":false,"locked":true,"openDoors":{"frontRight":false,"frontLeft":false,"backLeft":false,"backRight":false},"tirePressureWarningLamp":{"rearLeft":false,"frontLeft":false,"frontRight":false,"rearRight":false,"all":false}},"climate":{"active":false,"steeringwheelHeat":false,"sideMirrorHeat":false,"rearWindowHeat":false,"defrost":false,"temperatureSetpoint":14,"temperatureUnit":0},"engine":{"ignition":false,"accessory":false,"rangeGas":561,"range":561,"plugedTo":0,"batteryCharge12v":87},"lastupdate":"2025-08-12T12:14:53.000Z"}
                  bluelink.0
                  	2025-08-12 18:28:19.503	debug	Set Status for U5YH5815GSL196934
                  bluelink.0
                  	2025-08-12 18:28:19.378	info	Error on fullStatus - new try with Status Request
                  bluelink.0
                  	2025-08-12 18:28:16.826	debug	RAW {"vehicleLocation":{"coord":{"lat":47.973894,"lon":10.26625,"alt":0,"type":0},"head":290,"speed":{"value":0,"unit":0},"accuracy":{"hdop":0,"pdop":0},"time":"20250812141452"},"vehicleStatus":{"airCtrlOn":false,"engine":false,"doorLock":true,"doorOpen":{"frontLeft":0,"frontRight":0,"backLeft":0,"backRight":0},"trunkOpen":false,"airTemp":{"value":"00H","unit":0,"hvacTempType":1},"defrost":false,"lowFuelLight":false,"acc":false,"hoodOpen":false,"transCond":false,"steerWheelHeat":0,"sideBackWindowHeat":0,"dte":{"value":561,"unit":1},"tirePressureLamp":{"tirePressureLampAll":0,"tirePressureLampFL":0,"tirePressureLampFR":0,"tirePressureLampRL":0,"tirePressureLampRR":0},"seatHeaterVentState":{"frSeatHeatState":2,"flSeatHeatState":2,"rlSeatHeatState":2,"rrSeatHeatState":2},"battery":{"batSoc":87,"batState":0},"lampWireStatus":{"stopLamp":{"leftLamp":false,"rightLamp":false},"headLamp":{"headLampStatus":false,"leftLowLamp":false,"rightLowLamp":false,"leftHighLamp":false,"rightHighLamp":false,"leftBifuncLamp":false,"rightBifuncLamp":false},"turnSignalLamp":{"leftFrontLamp":false,"rightFrontLamp":false,"leftRearLamp":false,"rightRearLamp":false}},"windowOpen":{"frontLeft":0,"frontRight":0,"backLeft":0,"backRight":0},"smartKeyBatteryWarning":false,"fuelLevel":83,"washerFluidStatus":false,"breakOilStatus":false,"engineOilStatus":false,"sleepModeCheck":true,"time":"20250812141453","remoteWaitingTimeAlert":{"remoteControlAvailable":1,"remoteControlWaitingTime":168,"elapsedTime":"00:03:30"},"systemCutOffAlert":0,"tailLampStatus":0,"hazardStatus":0},"odometer":{"value":68115.2,"unit":1}}
                  bluelink.0
                  	2025-08-12 18:28:16.825	debug	Set fullStatus for U5YH5815GSL196934
                  bluelink.0
                  	2025-08-12 18:28:16.624	info	Read new update for U5YH5815GSL196934 from the server
                  bluelink.0
                  	2025-08-12 18:28:16.617	debug	Read new status from api for U5YH5815GSL196934
                  bluelink.0
                  	2025-08-12 18:28:16.260	debug	[{"vehicleConfig":{"nickname":"CEED","name":"CEED","regDate":"2024-07-22 16:25:33.470","brandIndicator":"H","id":"xxxxxxxxxxxxxxxxx","vin":"U5YH5815GSL196934","generation":"2025","ccuCCS2ProtocolSupport":false},"controller":{"userConfig":{"username":"xxxxxxxxxxxxxxxxx","password":"xxxxxxxxxxxx,"region":"EU","brand":"kia","autoLogin":true,"pin":"xxxx","vin":"","language":"de"},"session":{"accessToken":"Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","refreshToken":"xxxxxxxxxxxxxxxxx","deviceId":"xxxxxxxxxxxxxxxxxxx","tokenExpiresAt":1755019696,"controlTokenExpiresAt":0},"_environment":{"brand":"kia","host":"prd.eu-ccapi.kia.com:8080","baseUrl":"https://prd.eu-ccapi.kia.com:8080","clientId":"xxxxxxxxxxxxxxxxx","appId":"xxxxxxxxxxxxxxxxxx","endpoints":{"session":"https://prd.eu-ccapi.kia.com:8080/api/v1/user/oauth2/authorize?response_type=code&state=test&client_id=xxxxxxxxxxxxxxxxx&redirect_uri=https://prd.eu-ccapi.kia.com:8080/api/v1/user/oauth2/redirect","login":"https://prd.eu-ccapi.kia.com:8080/api/v1/user/signin","language":"https://prd.eu-ccapi.kia.com:8080/api/v1/user/language","redirectUri":"https://prd.eu-ccapi.kia.com:8080/api/v1/user/oauth2/redirect","token":"https://prd.eu-ccapi.kia.com:8080/api/v1/user/oauth2/token","integration":"https://prd.eu-ccapi.kia.com:8080/api/v1/user/integrationinfo","silentSignIn":"https://prd.eu-ccapi.kia.com:8080/api/v1/user/silentsignin"},"basicToken":"Basic xxxxxxxxxxxxxxxxxxxxxxxxxxx==","GCMSenderID":"xxxxxxxxxxxxx"},"authStrategies":{"main":{"language":"de"},"fallback":{"language":"de"}}},"_fullStatus":null,"_status":null,"_location":null,"_odometer":null,"region":"EU","serverRates":{"max":-1,"current":-1}}]
                  bluelink.0
                  	2025-08-12 18:28:16.259	info	1 Vehicles found
                  
                  arteck 1 Reply Last reply Reply Quote 0
                  • arteck
                    arteck Developer Most Active @Peter V. last edited by arteck

                    @peter-v installier nochmal..immer diese Sonderlocken

                    Peter V. 1 Reply Last reply Reply Quote 0
                    • W
                      wm20320 @RISSN last edited by

                      @rissn sagte in Adapter Hyundai (Bluelink) oder KIA (UVO):

                      
                      bluelink.0
                      2025-08-12 07:18:38.530	error	next auto login attempt in 1 hour or restart adapter manual
                      
                      bluelink.0
                      2025-08-12 07:18:38.530	error	Server is not available or login credentials are wrong
                      
                      bluelink.0
                      2025-08-12 07:18:38.530	error	Error: @EuropeController.login: Could not manage to get token: {"retId":"532d44c3-910c-46f0-8000-68c849590654","retCode":4000,"retMsg":"Bad Request","retSubMsg":"\"clientSecret is wrong. requested clientId=6d477c38-3ca4-4cf3-9557-2a1929a94654 requested clientSecret=secret\"","retValue":null}
                      

                      bei mir leider noch kein Login möglich beim Hyundai Tucson

                      Gleiches bei mir - Hyundai i10 Benziner

                      VG Marcus

                      arteck 1 Reply Last reply Reply Quote 0
                      • arteck
                        arteck Developer Most Active @wm20320 last edited by arteck

                        @wm20320 dann geh zurück auf die 3.1.6 läufts dann... wenn ja dann lass laufen

                        W Meister Mopper 2 Replies Last reply Reply Quote 0
                        • W
                          wm20320 @arteck last edited by

                          @arteck 👍 3.1.6 läuft. Danke.

                          1 Reply Last reply Reply Quote 1
                          • Meister Mopper
                            Meister Mopper @arteck last edited by

                            @arteck

                            Jetzt haben sie offenbar an der Abfrage der Geo-Daten geschraubt. Diese API ist aktuell wirklich ein Graus.

                            bluelink.0	2025-08-13 15:58:00.349	error	Error on API-Request Status, ErrorCount:1
                            bluelink.0	2025-08-13 15:58:00.350	error	Cannot read properties of undefined (reading 'GeoCoord')
                            
                            arteck 1 Reply Last reply Reply Quote 0
                            • Peter V.
                              Peter V. @arteck last edited by

                              @arteck
                              Danke, werde es morgen mal testen, Kaffeespende ist schon mal unterwegs 😉

                              1 Reply Last reply Reply Quote 1
                              • arteck
                                arteck Developer Most Active @Meister Mopper last edited by

                                @meister-mopper hast du die neue von GIT geladen ?

                                Meister Mopper 1 Reply Last reply Reply Quote 0
                                • Meister Mopper
                                  Meister Mopper @arteck last edited by

                                  @arteck sagte in Adapter Hyundai (Bluelink) oder KIA (UVO):

                                  @meister-mopper hast du die neue von GIT geladen ?

                                  Danke, mit v3.1.20 ist der Fehler weg.

                                  1 Reply Last reply Reply Quote 1
                                  • R
                                    RISSN last edited by

                                    aber bei der Version 3.1.20 geht der Login beim Hyundai noch nicht? Ich bin ja von 3.1.19 auf 3.1.17 zurück, da geht es alles

                                    ilovegym arteck 2 Replies Last reply Reply Quote 0
                                    • ilovegym
                                      ilovegym @RISSN last edited by

                                      @rissn

                                      bei mir ist die letzte die 3.1.16, alles was danach kam, geht nicht.. 🙂

                                      Und nein, @arteck ist nicht Merlin 🙂 auch wenn er hier schon viel zaubert 🙂
                                      Ueberlegt mal, wieviel verschiedene Fahrzeugtypen es bei Hyundai/Kia gibt, mit zig verschiedenen Software und Hardware variationen..

                                      1 Reply Last reply Reply Quote 1
                                      • arteck
                                        arteck Developer Most Active @RISSN last edited by

                                        @rissn sagte in Adapter Hyundai (Bluelink) oder KIA (UVO):

                                        aber bei der Version 3.1.20 geht der Login beim Hyundai noch nicht? Ich bin ja von 3.1.19 auf 3.1.17 zurück, da geht es alles

                                        dann lass laufen .. hab schon mal gesagt.. es ändert sich nix an dem adapter ausser für KIA Fahrzeuge die mal wieder was anderes senden..
                                        wenn eine ältere Version geht (login funktioniert) dann nutze diese .. ich werde nicht eine alternative einbauen ala "welches login willst du haben neu oder alt" das ist zu viel Arbeit.. und so wie es aussieht passiert da gerade sehr viel in der API ..

                                        ilovegym 1 Reply Last reply Reply Quote 1
                                        • Peter V.
                                          Peter V. last edited by

                                          @arteck
                                          Danke für deine ganzen Mühen, Version 3.1.20 läuft bei Kia wieder ohne Fehlermeldungen und tut was es soll.
                                          Hoffen wir mal, das die an der Api/Servern nicht mehr zuviel rumschrauben.

                                          1 Reply Last reply Reply Quote 0
                                          • ilovegym
                                            ilovegym @arteck last edited by ilovegym

                                            @arteck

                                            Hab mal meinen digitalen Codierknecht 🤖 solange gequaelt, bis das hier rauskam:
                                            Screenshot 2025-08-16 at 15.03.35.png

                                            Ist n einfaches Dashboard, kann in jeder Vis (Vis, Vis-NG, MinuVis, Jarvis und was es noch so gibt) angezeigt werden, legt die Daten in einen State ab.
                                            Hier das Script dazu, das Hintergrundbild kann natuerlich angepasst werden, muss nich meins von der Nordschleife sein 🙂
                                            Eingebaut sind mir alle sinvoll vorgekommenen Werte des Adapters Bluelink, sowie von meiner Wallbox Go-E und ein paar selbst definierte (12V laedt), desweiteren eine Ladehistorie graphisch und tabellar.. 🙂 - responsive Design, kann man auch aufm Smartphone ansehen

                                            /******************************************************
                                            * IONIQ 5 N – Bluelink Dashboard (HTML) for ioBroker
                                            * Version: 1.6.6  (SVG-Chart responsive, volle Kartenbreite & ~Tabelle-Höhe)
                                            * (c) 2025 Bernd / ilovegym – privat
                                            ******************************************************/
                                            
                                            /* =================== KONFIG =================== */
                                            const BLUELINK_INST = 'bluelink.0';
                                            let   VEHICLE_ID    = '';          // leer = Auto-Detect
                                            const OUT_HTML_DP   = '0_userdata.0.vis.IoniqDashboardHTML';
                                            
                                            // Eigene Zusatz-DPs
                                            const DP_12V_LADEN  = '0_userdata.0.Zaehler.Hyundai_12V';   // true = 12V lädt
                                            const DP_KM_YESTER  = '0_userdata.0.Zaehler.Hyundai_KM_';   // km gestern
                                            
                                            // Wallbox (go-e)
                                            const WB = {
                                             energy:  'go-e.0.loaded_energy_kwh',
                                             state:   'go-e.0.car',                // 1 Standby, 2 Laden, 3 Warten auf Auto, 4 Fertig
                                             power:   'go-e.0.energy.power',
                                             temp1:   'go-e.0.temperatures.temperature1',
                                             temp2:   'go-e.0.temperatures.temperature2',
                                             allow:   'go-e.0.allow_charging',
                                             amp:     'go-e.0.ampere'
                                            };
                                            const WB_STATE_TXT = {1:'Standby',2:'Laden',3:'Warte auf Auto',4:'Fertig'};
                                            
                                            // Ladehistorie (Samples werden hier gespeichert)
                                            const HIST_SAMPLES_DP = '0_userdata.0.Ioniq.History.samples';   // string (JSON Array: {t,soc,wb})
                                            const HIST_LAST_TS_DP = '0_userdata.0.Ioniq.History.lastSample'; // number (ms)
                                            const SAMPLE_INTERVAL_MS = 5 * 60 * 1000; // alle 5 Minuten
                                            const MAX_SAMPLES = 3000; // ~7 Tage bei 5-Minuten-Samples
                                            
                                            // Debug
                                            const DEBUG = false;
                                            
                                            /* =================== UTIL =================== */
                                            function ensureState(id, def = '', common = {name:'IONIQ 5 N Dashboard HTML', type:'string', role:'html', read:true, write:false}) {
                                             try { if (!existsObject(id)) createState(id, def, true, common); } catch (e) { log('ensureState error ' + e, 'warn'); }
                                            }
                                            function ensureDataPoint(id, def, common){ try{ if(!existsObject(id)) createState(id, def, true, common); }catch(e){} }
                                            function JP(...parts){ return parts.filter(p => p !== '' && p != null).join('.'); }
                                            function es(id){ try { return !!(id && existsState(id)); } catch(_) { return false; } }
                                            function gs(id){ try { return es(id)? getState(id).val:undefined; } catch(_) { return undefined; } }
                                            function ss(id, val){ try { setState(id, val, true); } catch(_){} }
                                            function firstExisting(paths){ if(!Array.isArray(paths)) return {path:null,val:undefined}; for(const p of paths){ if(es(p)){ const v=gs(p); if(v!==undefined && v!==null) return {path:p,val:v}; } } return {path:null,val:undefined}; }
                                            function P(...parts){ return JP(BLUELINK_INST, VEHICLE_ID, ...parts); }
                                            
                                            /* =================== VIN-AUTODETECT =================== */
                                            function detectVehicleId(){
                                             try{
                                               const rows = getObjectView('system','state',{ startkey: BLUELINK_INST+'.', endkey: BLUELINK_INST+'.\u9999' }).rows;
                                               const seen = {};
                                               for(const r of rows){
                                                 const id = r.id || '';
                                                 const seg = id.split('.');
                                                 if(seg.length>=3){
                                                   const veh = seg[2];
                                                   if(veh && !['info','remote','vehicles'].includes(veh)) seen[veh]=true;
                                                 }
                                               }
                                               const list = Object.keys(seen);
                                               return list.length ? list[0] : '';
                                             }catch(e){ log('VIN-Detect: '+e,'warn'); return ''; }
                                            }
                                            
                                            /* =================== KANDIDATEN =================== */
                                            function candidates(){
                                             return {
                                               // Identität / Fahrt
                                               carName: [ P('general.carName'), P('general.modelName') ],
                                               vin:     [ P('general.vin') ],
                                               odometer_km: [ P('odometer.value') ],
                                               speed:       [ P('vehicleLocation.speed') ],
                                            
                                               // Standort (mit Fallbacks)
                                               lat: [ P('vehicleLocation.lat'), P('location.coord.lat') ],
                                               lon: [ P('vehicleLocation.lon'), P('location.coord.lon') ],
                                               position_text: [ P('vehicleLocation.position_text'), P('location.formattedAddress') ],
                                               position_url:  [ P('vehicleLocation.position_url') ],
                                            
                                               // HV & 12V
                                               soc_pct:            [ P('vehicleStatus.battery.soc') ],
                                               charge_active:      [ P('vehicleStatus.battery.charge') ],
                                               minutes_to_charged: [ P('vehicleStatus.battery.minutes_to_charged') ],
                                               plugin_code:        [ P('vehicleStatus.battery.plugin') ],
                                               soc12v:             [ P('vehicleStatus.battery.soc-12V') ],
                                               state12v:           [ P('vehicleStatus.battery.state-12V') ],
                                               soh:                [ P('vehicleStatus.battery.soh') ],
                                               charge12v:          [ DP_12V_LADEN ],
                                            
                                               // Klima & Komfort
                                               hvacOn:     [ P('vehicleStatus.airCtrlOn') ],
                                               insideTemp: [ P('vehicleStatus.airTemp') ],
                                               airClean:   [ P('vehicleStatusRaw.vehicleStatus.airCleaning.airPurifierStatus') ],
                                               defrost:    [ P('vehicleStatusRaw.vehicleStatus.defrost') ],
                                               seatFL:     [ P('vehicleStatusRaw.vehicleStatus.seatHeaterVentState.flSeatHeatState') ],
                                               seatFR:     [ P('vehicleStatusRaw.vehicleStatus.seatHeaterVentState.frSeatHeatState') ],
                                               seatRL:     [ P('vehicleStatusRaw.vehicleStatus.seatHeaterVentState.rlSeatHeatState') ],
                                               seatRR:     [ P('vehicleStatusRaw.vehicleStatus.seatHeaterVentState.rrSeatHeatState') ],
                                               steerHeat:  [ P('vehicleStatus.steerWheelHeat') ],
                                            
                                               // Öffnungen / Fenster
                                               doorFL: [ P('vehicleStatus.doorOpen.frontLeft') ],
                                               doorFR: [ P('vehicleStatus.doorOpen.frontRight') ],
                                               doorRL: [ P('vehicleStatus.doorOpen.backLeft') ],
                                               doorRR: [ P('vehicleStatus.doorOpen.backRight') ],
                                               trunk:  [ P('vehicleStatus.trunkOpen') ],
                                               frunk:  [ P('vehicleStatus.hoodOpen') ],
                                               winFL:  [ P('vehicleStatusRaw.vehicleStatus.windowOpen.frontLeft') ],
                                               winFR:  [ P('vehicleStatusRaw.vehicleStatus.windowOpen.frontRight') ],
                                               winRL:  [ P('vehicleStatusRaw.vehicleStatus.windowOpen.backLeft') ],
                                               winRR:  [ P('vehicleStatusRaw.vehicleStatus.windowOpen.backRight') ],
                                            
                                               // Reifen-Warnlampen
                                               tireFL: [ P('vehicleStatusRaw.vehicleStatus.tirePressureLamp.tirePressureLampFL') ],
                                               tireFR: [ P('vehicleStatusRaw.vehicleStatus.tirePressureLamp.tirePressureLampFR') ],
                                               tireRL: [ P('vehicleStatusRaw.vehicleStatus.tirePressureLamp.tirePressureLampRL') ],
                                               tireRR: [ P('vehicleStatusRaw.vehicleStatus.tirePressureLamp.tirePressureLampRR') ],
                                            
                                               // Flüssigkeiten & Warnungen
                                               dte:        [ P('vehicleStatusRaw.vehicleStatus.dte.value') ],
                                               washer:     [ P('vehicleStatus.washerFluidStatus') ],         // false=OK, true=leer
                                               smartKeyBat:[ P('vehicleStatus.smartKeyBatteryWarning') ],     // true=Warnung
                                               breakOil:   [ P('vehicleStatus.breakOilStatus') ],             // false=OK, true=Niedrig
                                            
                                               // Wallbox
                                               wb_energy: [ WB.energy ],
                                               wb_state:  [ WB.state ],
                                               wb_power:  [ WB.power ],
                                               wb_temp1:  [ WB.temp1 ],
                                               wb_temp2:  [ WB.temp2 ],
                                               wb_allow:  [ WB.allow ],
                                               wb_amp:    [ WB.amp ],
                                            
                                               // Zeit / extra
                                               lastUpdate:  [ P('info.lastUpdate'), P('vehicleStatus.updatedAt') ],
                                               kmYesterday: [ DP_KM_YESTER ]
                                             };
                                            }
                                            
                                            /* =================== CSS =================== */
                                            function css(){ return `
                                            <style>
                                            :root{
                                             --bg:#0a0c10; --card:rgba(18,21,28,0.88); --muted:#8a93a6; --text:#e7ecf6;
                                             --ok:#33d17a; --warn:#ffbf3c; --err:#ff5c5c; --accent:#60a5fa; --chip:#1b2030; --chipText:#cfe0ff;
                                            }
                                            *{box-sizing:border-box}
                                            .wrap{
                                             font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,sans-serif;
                                             color:var(--text); padding:16px; min-height:100vh;
                                             background:url('http://10.1.1.2:8081/files/0_userdata.0/background/IMG_2721.jpeg') center/cover no-repeat fixed;
                                            }
                                            .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px;align-items:stretch}
                                            .card{background:var(--card);border-radius:12px;padding:12px;box-shadow:0 4px 14px rgba(0,0,0,.25);height:100%;display:flex;flex-direction:column}
                                            .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
                                            .title{display:flex;align-items:center;gap:10px;font-weight:700;font-size:18px;margin-bottom:10px}
                                            .kpi{font-size:26px;font-weight:800}
                                            .sub{color:var(--muted);font-size:13px}
                                            .badge{background:#1b2030;color:#cfe0ff;padding:6px 12px;border-radius:10px;font-size:14px;display:inline-block;max-width:100%;white-space:normal;word-break:break-word;text-align:center}
                                            .stat{display:flex;justify-content:space-between;margin:4px 0;font-size:13px}
                                            .meter{height:10px;background:#0f1220;border-radius:8px;overflow:hidden}
                                            .meter>span{display:block;height:100%}
                                            .kv{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin-top:8px}
                                            .tile{background:#0f1220;border-radius:8px;padding:6px;font-size:12px;display:flex;align-items:center;gap:6px}
                                            .ok{color:var(--ok)} .warn{color:var(--warn)} .err{color:var(--err)} .acc{color:var(--accent)}
                                            .footer{margin-top:8px;color:var(--muted);font-size:12px;text-align:right}
                                            table{width:100%;border-collapse:collapse;font-size:12px}
                                            th,td{padding:6px 8px;border-bottom:1px solid #222}
                                            th{text-align:left;color:#cfe0ff}
                                            /* Chart Box: gleiche Höhe wie Tabelle (~280px), passt sich Breite an */
                                            .chartBox{width:100%;height:280px;display:block}
                                            .chartBox svg{width:100%;height:100%;display:block}
                                            </style>`; }
                                            
                                            /* =================== SVG CHART BUILDER =================== */
                                            function buildHistorySVG(labels, soc, kwh){
                                             // Basisgröße für viewBox (skaliert über CSS auf 100%x100% in .chartBox)
                                             const W=720, H=280; // größer als vorher
                                             const padL=44, padR=14, padT=14, padB=34;
                                             const x0=padL, y0=padT, x1=W-padR, y1=H-padB;
                                             const w=x1-x0, h=y1-y0;
                                             const n = Math.max(1, labels.length||1);
                                             const xAt = (i)=> x0 + (n<=1 ? 0 : (w * i/(n-1)));
                                             const ySoc = (v)=> y0 + (1 - (Math.max(0,Math.min(100, +v||0))/100)) * h;
                                            
                                             // Bars scale
                                             let maxK=0; for (let i=0;i<kwh.length;i++){ const v=+kwh[i]||0; if (v>maxK) maxK=v; }
                                             if (maxK<1) maxK=1;
                                             const barW = Math.max(8, Math.min(28, w / (n*1.8)));
                                             const bars = [];
                                             for (let i=0;i<n;i++){
                                               const xx = xAt(i);
                                               const kh = ( ( (+kwh[i]||0)/maxK ) * h );
                                               const x = xx - barW/2;
                                               const y = y1 - kh;
                                               const bw = barW;
                                               const bh = kh;
                                               bars.push(`<rect x="${x.toFixed(1)}" y="${y.toFixed(1)}" width="${bw.toFixed(1)}" height="${bh.toFixed(1)}" rx="2" fill="#60a5fa" />`);
                                             }
                                            
                                             // SOC path
                                             let d='';
                                             for (let i=0;i<n;i++){
                                               const xx = xAt(i), yy = ySoc(soc[i]||0);
                                               d += (i===0? 'M':' L') + xx.toFixed(1) + ' ' + yy.toFixed(1);
                                             }
                                             const points = soc.map((v,i)=>{
                                               const xx=xAt(i), yy=ySoc(v||0);
                                               return `<circle cx="${xx.toFixed(1)}" cy="${yy.toFixed(1)}" r="3" fill="#33d17a"/>`;
                                             }).join('');
                                            
                                             // grid & labels
                                             const grid=[];
                                             for (let gy=0; gy<=5; gy++){
                                               const yy = y0 + h*(gy/5);
                                               grid.push(`<line x1="${x0}" y1="${yy.toFixed(1)}" x2="${x1}" y2="${yy.toFixed(1)}" stroke="rgba(255,255,255,0.15)" stroke-width="1"/>`);
                                             }
                                             const ylab=[];
                                             for (let gy=0; gy<=5; gy++){
                                               const val = 100 - gy*20;
                                               const yy = y0 + h*(gy/5);
                                               ylab.push(`<text x="${x0-8}" y="${yy+4}" fill="#cfe0ff" font-size="12" text-anchor="end">${val}</text>`);
                                             }
                                             const xlab = labels.map((t,i)=>{
                                               const xx = xAt(i);
                                               return `<text x="${xx}" y="${y1+18}" fill="#cfe0ff" font-size="12" text-anchor="middle">${String(t)}</text>`;
                                             }).join('');
                                            
                                             return `
                                             <svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Ladehistorie">
                                               <rect x="0" y="0" width="${W}" height="${H}" fill="transparent"/>
                                               <rect x="${x0}" y="${y0}" width="${w}" height="${h}" rx="6" fill="rgba(255,255,255,0.04)"/>
                                               ${grid.join('')}
                                               ${ylab.join('')}
                                               ${xlab}
                                               ${bars.join('')}
                                               <path d="${d}" fill="none" stroke="#33d17a" stroke-width="2.5"/>
                                               ${points}
                                               <!-- Legende -->
                                               <g>
                                                 <rect x="${x0+6}" y="${y0+6}" width="12" height="3" fill="#33d17a"/><text x="${x0+24}" y="${y0+14}" font-size="13" fill="#cfe0ff">SOC %</text>
                                                 <rect x="${x0+92}" y="${y0+6}" width="12" height="12" fill="#60a5fa"/><text x="${x0+110}" y="${y0+16}" font-size="13" fill="#cfe0ff">kWh/Tag</text>
                                               </g>
                                             </svg>`;
                                            }
                                            
                                            /* =================== RENDER =================== */
                                            function renderHTML(d, hist, lastSamples){
                                             if (!d || d.__noVin){
                                               return `${css()}<div class="wrap"><div class="card"><div class="kpi">Bluelink</div><div class="sub">Bitte VIN (VEHICLE_ID) setzen oder Auto-Detect abwarten.</div></div></div>`;
                                             }
                                            
                                             const hvColor = p=>p<75?'#ff5c5c':(p<80?'#ffbf3c':'#33d17a');
                                             const socBar = p => (p!=null ? `
                                               <div class="meter" style="height:14px;border-radius:7px;overflow:hidden">
                                                 <span style="width:${Math.max(0,Math.min(100,p))}%;background:${hvColor(p)}"></span>
                                               </div>
                                               <div style="font-size:12px;text-align:right;margin-top:2px">${p}%</div>` : '–');
                                            
                                             const seatTxt = v => (v===1?'<span class="heatAnim">🔥 Heizen</span>':(v===0?'<span class="coolAnim">❄️ Kühlen</span>':'Aus'));
                                             const flag = (v, ok='geschlossen', bad='offen') => v===true?`<b class="err">${bad}</b>`:(v===false?`<b class="ok">${ok}</b>`:'–');
                                             const tireTxt = v => (v===true ? '<b class="warn">Warn</b>' : (v===false ? '<b class="ok">OK</b>' : '–'));
                                            
                                             const locStr = d.address || '–';
                                             const coords = (d.lat!=null && d.lon!=null) ? `${(+d.lat).toFixed(5)}, ${(+d.lon).toFixed(5)}` : '–';
                                             const wbStateTxt = (d.wb_stateNum!=null? (WB_STATE_TXT[d.wb_stateNum]||String(d.wb_stateNum)) : (d.wb_stateText||'–'));
                                            
                                             // Historie-Daten (letzte 7 Tage)
                                             const labels = hist.labels || ['Mo','Di','Mi','Do','Fr','Sa','So'];
                                             const socLine = hist.dailySoc || [0,0,0,0,0,0,0];
                                             const kwhBars = hist.dailyKwh || [0,0,0,0,0,0,0];
                                            
                                             // Tabelle der letzten 10 Einträge
                                             let tableRows = '';
                                             if (lastSamples && lastSamples.length){
                                               const last10 = lastSamples.slice(-10);
                                               tableRows = last10.map(s=>{
                                                 const dt = new Date(s.t).toLocaleString();
                                                 const soc = (typeof s.soc==='number') ? (Math.round(s.soc*10)/10)+' %' : '–';
                                                 const wb  = (typeof s.wb ==='number') ? (Math.round(s.wb*100)/100).toFixed(2)+' kWh' : '–';
                                                 return `<tr><td>${dt}</td><td>${soc}</td><td>${wb}</td></tr>`;
                                               }).join('');
                                             }
                                            
                                             const svgChart = buildHistorySVG(labels, socLine, kwhBars);
                                            
                                             return `
                                             ${css()}
                                             <div class="wrap">
                                               <div class="title"><span class="kpi">${d.carName||'IONIQ 5 N'}</span> <span class="badge">${d.vin||''}</span></div>
                                            
                                               <div class="grid">
                                                 <!-- HV Akku -->
                                                 <div class="card">
                                                   <div class="row">🔋 <b>State of Charge</b></div>
                                                   <div class="kpi" style="margin:6px 0">${d.soc!=null? d.soc+' %' : '–'}</div>
                                                   ${socBar(d.soc)}
                                                   <div class="stat"><span>Laden</span><span class="${d.charging?'chargeAnim':''}"><b>${d.charging===true?'aktiv':(d.charging===false?'nein':'–')}</b></span></div>
                                                   <div class="stat"><span>Min. bis voll</span><span><b>${d.minToFull!=null? d.minToFull : '–'}</b></span></div>
                                                   <div class="stat"><span>Reichweite</span><span><b>${d.dte!=null? d.dte+' km':'–'}</b></span></div>
                                                 </div>
                                            
                                                 <!-- 12V / SOH -->
                                                 <div class="card">
                                                   <div class="row">🔋 <b>12V & SOH</b></div>
                                                   <div class="stat"><span>12V SoC</span><span style="flex:1;margin-left:10px">${socBar(d.soc12v)}</span></div>
                                                   <div class="stat"><span>12V Status</span><span><b>${d.state12v ?? '–'}</b></span></div>
                                                   <div class="stat"><span>12V lädt</span><span><b>${d.charge12v===true?'Ja':(d.charge12v===false?'Nein':'–')}</b></span></div>
                                                   <div class="stat"><span>SOH (HV)</span><span><b>${d.soh!=null? d.soh+' %':'–'}</b></span></div>
                                                 </div>
                                            
                                                 <!-- Fahrzeug -->
                                                 <div class="card">
                                                   <div class="row">🚗 <b>Fahrzeug</b></div>
                                                   <div class="stat"><span>Odometer</span><span><b>${d.odoKm!=null? d.odoKm+' km':'–'}</b></span></div>
                                                   <div class="stat"><span>Gestern gefahren</span><span><b>${d.kmYesterday!=null? d.kmYesterday+' km':'–'}</b></span></div>
                                                 </div>
                                            
                                                 <!-- Standort -->
                                                 <div class="card">
                                                   <div class="row">📍 <b>Standort</b></div>
                                                   <div class="badge">${locStr}</div>
                                                   <div class="sub" style="margin-top:8px">Koordinaten: <b>${coords}</b></div>
                                                 </div>
                                            
                                                 <!-- Türen -->
                                                 <div class="card">
                                                   <div class="row">🚪 <b>Öffnungen</b></div>
                                                   <div class="kv">
                                                     <div class="tile">VL: ${flag(d.doorFL)}</div>
                                                     <div class="tile">VR: ${flag(d.doorFR)}</div>
                                                     <div class="tile">HL: ${flag(d.doorRL)}</div>
                                                     <div class="tile">HR: ${flag(d.doorRR)}</div>
                                                     <div class="tile">Kofferraum: ${flag(d.trunk)}</div>
                                                     <div class="tile">Frunk: ${flag(d.frunk)}</div>
                                                   </div>
                                                   <div class="sub" style="margin-top:6px">Gesamt: ${ [d.doorFL,d.doorFR,d.doorRL,d.doorRR,d.trunk,d.frunk].some(v=>v===true) ? '<b class="err">offen</b>' : '<b class="ok">alle zu</b>' }</div>
                                                 </div>
                                            
                                                 <!-- Fenster -->
                                                 <div class="card">
                                                   <div class="row">🪟 <b>Fenster</b></div>
                                                   <div class="kv">
                                                     <div class="tile">VL: ${flag(d.winFL,'zu','offen')}</div>
                                                     <div class="tile">VR: ${flag(d.winFR,'zu','offen')}</div>
                                                     <div class="tile">HL: ${flag(d.winRL,'zu','offen')}</div>
                                                     <div class="tile">HR: ${flag(d.winRR,'zu','offen')}</div>
                                                   </div>
                                                 </div>
                                            
                                                 <!-- Reifen -->
                                                 <div class="card">
                                                   <div class="row">🛞 <b>Reifen</b></div>
                                                   <div class="kv">
                                                     <div class="tile">VL: ${tireTxt(d.tireFL)}</div>
                                                     <div class="tile">VR: ${tireTxt(d.tireFR)}</div>
                                                     <div class="tile">HL: ${tireTxt(d.tireRL)}</div>
                                                     <div class="tile">HR: ${tireTxt(d.tireRR)}</div>
                                                   </div>
                                                 </div>
                                            
                                                 <!-- Klima & Komfort -->
                                                 <div class="card">
                                                   <div class="row">❄️ <b>Klima & Komfort</b></div>
                                                   <div class="stat"><span>Klima</span><span class="${d.hvacOn?'fanAnim':''}"><b>${d.hvacOn===true?'AN':(d.hvacOn===false?'AUS':'–')}</b></span></div>
                                                   <div class="stat"><span>Innen</span><span><b>${d.tIn!=null? d.tIn+' °C':'–'}</b></span></div>
                                                   <div class="stat"><span>Luftreiniger</span><span><b>${d.airClean===true?'AN':(d.airClean===false?'AUS':'–')}</b></span></div>
                                                   <div class="stat"><span>Defrost</span><span><b>${d.defrost===true?'AN':(d.defrost===false?'AUS':'–')}</b></span></div>
                                                   <div class="stat"><span>Lenkrad</span><span>${d.steerHeat===true?'<span class="heatAnim">🔥</span>':'AUS'}</span></div>
                                                   <div class="sub" style="margin-top:6px">Sitze:</div>
                                                   <div class="kv">
                                                     <div class="tile">VL: <b>${seatTxt(d.seatFL)}</b></div>
                                                     <div class="tile">VR: <b>${seatTxt(d.seatFR)}</b></div>
                                                     <div class="tile">HL: <b>${seatTxt(d.seatRL)}</b></div>
                                                     <div class="tile">HR: <b>${seatTxt(d.seatRR)}</b></div>
                                                   </div>
                                                 </div>
                                            
                                                 <!-- Fahrzeugstatus -->
                                                 <div class="card">
                                                   <div class="row">ℹ️ <b>Fahrzeugstatus</b></div>
                                                   <div class="kv">
                                                     <div class="tile">Wischerwasser: ${d.washer===true?'<b class="err">LEER</b>':(d.washer===false?'<b class="ok">OK</b>':'–')}</div>
                                                     <div class="tile">Bremsöl: ${d.breakOil===true?'<b class="err">NIEDRIG</b>':(d.breakOil===false?'<b class="ok">OK</b>':'–')}</div>
                                                     <div class="tile">Smartkey: ${d.smartKeyBat===true?'<b class="warn">WARNUNG</b>':(d.smartKeyBat===false?'<b class="ok">OK</b>':'–')}</div>
                                                   </div>
                                                 </div>
                                            
                                                 <!-- Wallbox -->
                                                 <div class="card">
                                                   <div class="row">🔌 <b>Wallbox</b></div>
                                                   <div class="kv">
                                                     <div class="tile">Status: <b>${wbStateTxt}</b></div>
                                                     <div class="tile">Leistung: <b>${d.wb_powerFmt || '–'}</b></div>
                                                     <div class="tile">Energie: <b>${d.wb_energyFmt || '–'} kWh</b></div>
                                                     <div class="tile">Ampere: <b>${d.wb_ampere!=null? d.wb_ampere+' A':'–'}</b></div>
                                                     <div class="tile">Freigabe: <b>${d.wb_allow===true?'Ja':(d.wb_allow===false?'Nein':'–')}</b></div>
                                                     <div class="tile">Temp1: <b>${d.wb_temp1!=null? d.wb_temp1+' °C':'–'}</b></div>
                                                     <div class="tile">Temp2: <b>${d.wb_temp2!=null? d.wb_temp2+' °C':'–'}</b></div>
                                                   </div>
                                                 </div>
                                            
                                                 <!-- Ladehistorie (7 Tage) – reines SVG, volle Breite/Höhe -->
                                                 <div class="card">
                                                   <div class="row">📈 <b>Ladehistorie (7 Tage)</b></div>
                                                   <div class="chartBox">
                                                     ${svgChart}
                                                   </div>
                                                 </div>
                                            
                                                 <!-- Ladehistorie Tabelle (letzte 10 Samples) -->
                                                 <div class="card">
                                                   <div class="row">⚡ <b>Ladehistorie – letzte 10 Werte</b></div>
                                                   ${ (lastSamples && lastSamples.length)
                                                       ? `<table><tr><th>Zeitpunkt</th><th>SOC</th><th>Wallbox</th></tr>${tableRows}</table>`
                                                       : `<div class="sub">Keine Daten vorhanden.</div>` }
                                                 </div>
                                               </div>
                                            
                                               <div class="footer">Zuletzt aktualisiert: ${d.lastUpdate || new Date().toLocaleString()}</div>
                                             </div>`;
                                            }
                                            
                                            /* =================== HISTORIE: DPs + Verarbeitung =================== */
                                            function ensureHistoryDPs(){
                                             ensureDataPoint(HIST_SAMPLES_DP, '[]', {name:'Ioniq History Samples', type:'string', role:'json'});
                                             ensureDataPoint(HIST_LAST_TS_DP, 0,    {name:'Ioniq History Last Sample', type:'number', role:'value.time'});
                                            }
                                            function _normalizeSamples(arr){
                                             const now = Date.now();
                                             const twelveH = 12*3600*1000;
                                             return arr.map(s=>{
                                               if(!s) return null;
                                               let t = Number(s.t);
                                               if (!isFinite(t)) return null;
                                               if (t < 1e12) t = t * 1000; // Sekunden -> ms
                                               if (t > now + twelveH) return null; // Zukunft verwerfen
                                               const out = { t };
                                               if (typeof s.soc === 'number' && isFinite(s.soc)) out.soc = s.soc;
                                               if (typeof s.wb  === 'number' && isFinite(s.wb )) out.wb  = s.wb;
                                               return out;
                                             }).filter(Boolean);
                                            }
                                            function loadSamples(){
                                             try{
                                               const txt=gs(HIST_SAMPLES_DP);
                                               if(!txt) return [];
                                               const parsed = JSON.parse(txt);
                                               const arr = Array.isArray(parsed)? parsed : [];
                                               return _normalizeSamples(arr);
                                             }catch(_){ return []; }
                                            }
                                            function saveSamples(arr){
                                             try{
                                               if (arr.length>MAX_SAMPLES) arr = arr.slice(arr.length-MAX_SAMPLES);
                                               ss(HIST_SAMPLES_DP, JSON.stringify(arr));
                                             }catch(_){}
                                            }
                                            function trySample(nowMs, data){
                                             const lastTs = Number(gs(HIST_LAST_TS_DP) || 0);
                                             if (nowMs - lastTs < SAMPLE_INTERVAL_MS) return;
                                            
                                             const soc = data._hist_soc;
                                             const wb  = data._hist_wb_energy;
                                            
                                             if (soc==null && wb==null) {
                                               ss(HIST_LAST_TS_DP, nowMs);
                                               if (DEBUG) log('[IONIQ5N] Sample SKIP: keine Werte (soc/wb beide leer)', 'info');
                                               return;
                                             }
                                            
                                             let arr = loadSamples();
                                             arr.push({ t: nowMs, soc: soc, wb: wb });
                                             saveSamples(arr);
                                             ss(HIST_LAST_TS_DP, nowMs);
                                            
                                             if (DEBUG) {
                                               const socTxt = (soc==null)?'—':String(soc);
                                               const wbTxt  = (wb==null)?'—':String(wb);
                                               log(`[IONIQ5N] Sample OK @ ${new Date(nowMs).toLocaleString()} | SOC=${socTxt} | WB=${wbTxt}`, 'info');
                                             }
                                            }
                                            function computeDailyFromSamples(nowMs){
                                             const arr = loadSamples();
                                             // Labels & Tageskeys (letzte 7 Tage inkl. heute)
                                             const labels=[], dayKeys=[];
                                             for(let i=6;i>=0;i--){
                                               const d=new Date(nowMs - i*24*3600*1000);
                                               labels.push(d.toLocaleDateString(undefined,{weekday:'short'}));
                                               dayKeys.push(`${d.getFullYear()}-${('0'+(d.getMonth()+1)).slice(-2)}-${('0'+d.getDate()).slice(-2)}`);
                                             }
                                             if(!arr.length) return {labels, dailySoc:[0,0,0,0,0,0,0], dailyKwh:[0,0,0,0,0,0,0]};
                                             const byDay={};
                                             for(const s of arr){
                                               const d=new Date(s.t);
                                               const key=`${d.getFullYear()}-${('0'+(d.getMonth()+1)).slice(-2)}-${('0'+d.getDate()).slice(-2)}`;
                                               if(!byDay[key]) byDay[key]={soc:[], wb:[]};
                                               if(typeof s.soc==='number') byDay[key].soc.push(s.soc);
                                               if(typeof s.wb ==='number') byDay[key].wb.push(s.wb);
                                             }
                                             const dailySoc=[], dailyKwh=[];
                                             for(const key of dayKeys){
                                               const g=byDay[key];
                                               if(!g){ dailySoc.push(0); dailyKwh.push(0); continue; }
                                               const avgSoc = g.soc.length ? Math.round(g.soc.reduce((a,b)=>a+b,0)/g.soc.length) : 0;
                                               let kwh=0;
                                               if(g.wb.length){
                                                 const mn=Math.min.apply(null,g.wb), mx=Math.max.apply(null,g.wb);
                                                 kwh = mx-mn; if(!isFinite(kwh)||kwh<0) kwh=0;
                                                 kwh = Math.round(kwh*100)/100;
                                               }
                                               dailySoc.push(avgSoc);
                                               dailyKwh.push(kwh);
                                             }
                                             return {labels, dailySoc, dailyKwh};
                                            }
                                            
                                            /* =================== DATEN SAMMELN =================== */
                                            function readAll(){
                                             const cand = candidates();
                                             const pick = (name, map=v=>v)=>{
                                               const {val} = firstExisting(cand[name]||[]);
                                               if (val===undefined) return undefined;
                                               try{
                                                 if (typeof val === 'string' && !isNaN(val)) return map(Number(val));
                                                 return map(val);
                                               }catch(_){ return val; }
                                             };
                                             const toBool = (v) => (typeof v==='boolean') ? v : (v!=null ? Number(v)>0 : undefined);
                                            
                                             // Fahrwerte
                                             const odoKm = (function(){
                                               const v = pick('odometer_km', x=>x);
                                               if (v===undefined) return undefined;
                                               const num = typeof v==='string' ? parseFloat(v.replace(/[^\d.,]/g,'').replace(',','.')) : Number(v);
                                               return isNaN(num) ? undefined : Math.round(num);
                                             })();
                                            
                                             // Wallbox Zahlen
                                             const wb_stateNum = pick('wb_state', Number);
                                             const wb_powerNum = pick('wb_power', Number);
                                             const wb_energyNum= pick('wb_energy', Number);
                                            
                                             const data = {
                                               // Kopf
                                               carName: pick('carName'),
                                               vin: pick('vin'),
                                            
                                               // HV / 12V
                                               soc: pick('soc_pct', v => Math.round(Number(v))),
                                               charging: pick('charge_active', toBool),
                                               minToFull: pick('minutes_to_charged', Number),
                                               soc12v: pick('soc12v', v => Math.round(Number(v))),
                                               state12v: pick('state12v'),
                                               soh: pick('soh', v => Math.round(Number(v))),
                                               charge12v: pick('charge12v', v => v === true || v === 'true' || Number(v) === 1),
                                            
                                               // Reichweite & Klima
                                               dte: pick('dte', v => Math.round(Number(v))),
                                               hvacOn: pick('hvacOn', toBool),
                                               tIn: pick('insideTemp', v => Math.round(Number(v)*10)/10),
                                               airClean: pick('airClean', toBool),
                                               defrost: pick('defrost', toBool),
                                               seatFL: pick('seatFL', Number),
                                               seatFR: pick('seatFR', Number),
                                               seatRL: pick('seatRL', Number),
                                               seatRR: pick('seatRR', Number),
                                               steerHeat: pick('steerHeat', toBool),
                                            
                                               // Öffnungen / Fenster
                                               doorFL: pick('doorFL', toBool),
                                               doorFR: pick('doorFR', toBool),
                                               doorRL: pick('doorRL', toBool),
                                               doorRR: pick('doorRR', toBool),
                                               trunk: pick('trunk', toBool),
                                               frunk: pick('frunk', toBool),
                                               winFL: pick('winFL', toBool),
                                               winFR: pick('winFR', toBool),
                                               winRL: pick('winRL', toBool),
                                               winRR: pick('winRR', toBool),
                                            
                                               // Reifenlampen
                                               tireFL: pick('tireFL', toBool),
                                               tireFR: pick('tireFR', toBool),
                                               tireRL: pick('tireRL', toBool),
                                               tireRR: pick('tireRR', toBool),
                                            
                                               // Flüssigkeiten & Warnungen
                                               washer: pick('washer', toBool),
                                               breakOil: pick('breakOil', toBool),
                                               smartKeyBat: pick('smartKeyBat', toBool),
                                            
                                               // Standort
                                               lat: pick('lat', Number),
                                               lon: pick('lon', Number),
                                               address: pick('position_text', String),
                                               positionUrl: pick('position_url', String),
                                            
                                               // Wallbox – formatierte Strings
                                               wb_stateNum,
                                               wb_stateText: (wb_stateNum!=null ? (WB_STATE_TXT[wb_stateNum] || String(wb_stateNum)) : undefined),
                                               wb_powerFmt:  (wb_powerNum!=null ? (wb_powerNum >= 1000 ? (Math.round(wb_powerNum/100)/10)+' kW' : Math.round(wb_powerNum)+' W') : undefined),
                                               wb_energyFmt: (wb_energyNum!=null ? wb_energyNum.toFixed(2) : undefined),
                                               wb_ampere: pick('wb_amp', v => Math.round(Number(v))),
                                               wb_allow: pick('wb_allow', toBool),
                                               wb_temp1: pick('wb_temp1', v => Math.round(Number(v))),
                                               wb_temp2: pick('wb_temp2', v => Math.round(Number(v))),
                                            
                                               // Zeit / extra
                                               lastUpdate: (function(){
                                                 const raw = pick('lastUpdate');
                                                 if (!raw) return '';
                                                 try{ const d=new Date(raw); return isNaN(d)? String(raw) : d.toLocaleString(); }catch(_){ return String(raw); }
                                               })(),
                                               kmYesterday: pick('kmYesterday', v=> (v==null? undefined : Math.round(Number(v)))),
                                            
                                               // Rohwerte für Historie-Sampling
                                               _hist_soc: pick('soc_pct', Number),
                                               _hist_wb_energy: wb_energyNum,
                                            
                                               // Odometer
                                               odoKm
                                             };
                                            
                                             return data;
                                            }
                                            
                                            /* =================== MAIN =================== */
                                            ensureState(OUT_HTML_DP);
                                            ensureHistoryDPs();
                                            let vinAnnounced = false;
                                            
                                            function update(){
                                             try{
                                               const nowMs = Date.now();
                                            
                                               if (!VEHICLE_ID){
                                                 VEHICLE_ID = detectVehicleId();
                                                 if (VEHICLE_ID && !vinAnnounced){ log('[IONIQ5N] Auto-Detected VIN: '+VEHICLE_ID,'info'); vinAnnounced=true; }
                                               }
                                               if (!VEHICLE_ID){
                                                 ss(OUT_HTML_DP, renderHTML({__noVin:true}, {labels:[],dailySoc:[],dailyKwh:[]}, []));
                                                 return;
                                               }
                                            
                                               const data = readAll();
                                               trySample(nowMs, data);
                                               const hist = computeDailyFromSamples(nowMs);
                                               const lastSamples = loadSamples();
                                               const html = renderHTML(data, hist, lastSamples);
                                               ss(OUT_HTML_DP, html);
                                             }catch(e){ log('Update error: '+e, 'error'); }
                                            }
                                            
                                            // Initial + Intervall
                                            update();
                                            schedule('*/30 * * * * *', update);
                                            

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

                                            Support us

                                            ioBroker
                                            Community Adapters
                                            Donate

                                            390
                                            Online

                                            32.0k
                                            Users

                                            80.4k
                                            Topics

                                            1.3m
                                            Posts

                                            134
                                            2108
                                            748739
                                            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