<TaskerData sr="" dvi="1" tv="6.6.20">
	<Task sr="task17">
		<cdate>1788370000000</cdate>
		<edate>1788556740626</edate>
		<id>17</id>
		<nme>ZOE READ v25</nme>
		<pri>100</pri>
		<Share sr="Share">
			<b>false</b>
			<d>zoe read</d>
			<g></g>
			<p>false</p>
			<t></t>
		</Share>
		<Action sr="act0" ve="7">
			<code>474</code>
			<se>false</se>
			<Str sr="arg0" ve="3">
/*
 * ZOE READ v25
 * Renault ZOE ZE50 Phase 2 / 2021
 *
 * v25:
 * - protect against invalid 22900D raw=0000 (would otherwise produce -1200 A / huge power)
 * - detailed single LBC debug variable
 * - 22900D battery current (4 bytes raw, scale 0.025, offset 48000)
 * - power calculated directly from V*I
 * - 222003 EVC speed
 * - 222B85 EVC plug state
 * - isCharging = plugged + stopped + charging current
 * - driving always forces isCharging=0
 * - no use of 229019 for isCharging
 * - telemetry: 3s driving/not charging, 30s charging
 * - telemetry also sends speed (km/h), lat and lon from Tasker %LOC
 *
 * Existing Java global:
 *     zOE_SOCKET
 */

import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

String cr=String.valueOf((char)13);
String MAC="10:21:3E:4F:6B:47";
String SPP="00001101-0000-1000-8000-00805F9B34FB";

/* LBC */
Pattern pV=Pattern.compile("(?i)629005([0-9A-F]{4})");
Pattern pI=Pattern.compile("(?i)62900D([0-9A-F]{8})");
Pattern pS=Pattern.compile("(?i)629002([0-9A-F]{4})");
Pattern pH=Pattern.compile("(?i)629003([0-9A-F]{4})");
Pattern pT=Pattern.compile("(?i)629012([0-9A-F]{4})");
Pattern pC=Pattern.compile("(?i)629018([0-9A-F]{4})");

/* EVC */
Pattern pSpeed=Pattern.compile("(?i)622003([0-9A-F]{4})");
Pattern pPlug=Pattern.compile("(?i)622B85([0-9A-F]{2})");

tasker.setVariable("ZOE_LIVE_ERROR","");
tasker.setVariable("ZOE_DEBUG_SPEED","");
tasker.setVariable("ZOE_DEBUG_PLUG","");
tasker.setVariable("ZOE_EVC_SPEED_OK","0");
tasker.setVariable("ZOE_EVC_SPEED_LEN","0");
tasker.setVariable("ZOE_EVC_PLUG_OK","0");
tasker.setVariable("ZOE_EVC_PLUG_LEN","0");
tasker.setVariable("ZOE_ABRP_SEND_OK","0");
tasker.setVariable("ZOE_ABRP_SKIP_REASON","");
tasker.setVariable("ZOE_ABRP_HTTP_CODE","");
tasker.setVariable("ZOE_ABRP_RESPONSE","");
tasker.setVariable("ZOE_ABRP_ERROR","");

/* ---------- reconnect ---------- */
void reconnectZoe() throws Exception {
    Object old=tasker.getJavaVariable("zOE_SOCKET");
    try{ if(old!=null)((BluetoothSocket)old).close(); }catch(Exception ignored){}
    tasker.setJavaVariable("zOE_SOCKET",null);

    BluetoothAdapter a=BluetoothAdapter.getDefaultAdapter();
    if(a==null)throw new Exception("Bluetooth indisponible");

    BluetoothDevice d=a.getRemoteDevice(MAC);
    a.cancelDiscovery();

    BluetoothSocket s=d.createRfcommSocketToServiceRecord(UUID.fromString(SPP));
    s.connect();

    InputStream i=s.getInputStream();
    OutputStream o=s.getOutputStream();
    tasker.setJavaVariable("zOE_SOCKET",s);

    o.write(("ATZ"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(2200);
    o.write(("ATE0"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(400);
    o.write(("ATL0"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(300);
    o.write(("ATS0"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(300);
    o.write(("ATH0"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(300);
    o.write(("ATSP7"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(600);

    o.write(("ATSHDADBF1"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(300);
    o.write(("ATFCSH18DADBF1"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(300);
    o.write(("ATCRA18DAF1DB"+cr).getBytes("US-ASCII")); o.flush(); Thread.sleep(500);

    tasker.setVariable("ZOE_LIVE_CONNECTED","1");
    tasker.setVariable("ZOE_LIVE_STATE","Connected");
}

/* ---------- send AT command and consume its OK/prompt ---------- */
void sendAtAndDrain(InputStream in,OutputStream out,String command,int waitMs) throws Exception {
    out.write((command+cr).getBytes("US-ASCII"));
    out.flush();
    if(waitMs&gt;0)Thread.sleep(waitMs);
    long end=System.currentTimeMillis()+1500L;
    StringBuffer r=new StringBuffer();
    while(System.currentTimeMillis()&lt;end){
        int av=in.available();
        if(av&lt;=0){ Thread.sleep(20); continue; }
        byte[] b=new byte[Math.min(av,512)];
        int n=in.read(b);
        if(n&gt;0){
            r.append(new String(b,0,n,"US-ASCII"));
            if(r.indexOf("&gt;")&gt;=0)break;
        }
    }
}

/* ---------- clear all pending ELM bytes ---------- */
void clearElm(InputStream in) throws Exception {
    for(int k=0;k&lt;20;k++){
        int av=in.available();
        if(av&lt;=0)break;
        byte[] dump=new byte[Math.min(av,512)];
        int n=in.read(dump);
        if(n&lt;=0)break;
        Thread.sleep(10);
    }
}

/* ---------- read one ELM response ---------- */
String readElmResponse(InputStream in,String command) throws Exception {
    OutputStream out=((BluetoothSocket)tasker.getJavaVariable("zOE_SOCKET")).getOutputStream();

    clearElm(in);
    out.write((command+cr).getBytes("US-ASCII"));
    out.flush();

    StringBuffer r=new StringBuffer();
    long end=System.currentTimeMillis()+3000L;

    while(System.currentTimeMillis()&lt;end){
				try{
	int av = in.available();

				    if (av &lt;= 0) {
					        Thread.sleep(20);
					        continue;
					    }
            byte[] b=new byte[256];
            int n=in.read(b);
            if(n&lt;0)throw new Exception("Bluetooth EOF");
            if(n&gt;0){
                r.append(new String(b,0,n,"US-ASCII"));
                if(r.indexOf("&gt;")&gt;=0)break;
            }
        }catch(java.net.SocketTimeoutException e){}
    }
    String result=r.toString();
    if(result.length()==0){
        return "&lt;NO_DATA&gt;";
    }
    if(r.indexOf("&gt;")&lt;0){
        return "&lt;NO_PROMPT&gt;\n"+result;
    }
    return result;
}

/* ---------- one/two attempt loop ---------- */
boolean success=false;
Exception lastError=null;

for(int attempt=0;attempt&lt;2 &amp;&amp; !success;attempt++){
    try{
        Object so=tasker.getJavaVariable("zOE_SOCKET");
        if(so==null){
            reconnectZoe();
            so=tasker.getJavaVariable("zOE_SOCKET");
        }

        BluetoothSocket socket=(BluetoothSocket)so;
        if(!socket.isConnected()){
            reconnectZoe();
            socket=(BluetoothSocket)tasker.getJavaVariable("zOE_SOCKET");
        }

        InputStream in=socket.getInputStream();
        OutputStream out=socket.getOutputStream();

        /* Clear pending bytes */
        try{
            int av=in.available();
            while(av&gt;0){
                byte[] dump=new byte[Math.min(av,512)];
                int dn=in.read(dump);
                if(dn&lt;=0)break;
                av=in.available();
            }
        }catch(Exception ignored){}

        /* =====================================================
         * LBC FAST
         * ===================================================== */
        sendAtAndDrain(in,out,"ATSHDADBF1",80);
        sendAtAndDrain(in,out,"ATFCSH18DADBF1",80);
        sendAtAndDrain(in,out,"ATCRA18DAF1DB",80);
        clearElm(in);

        /* Voltage */
        String vText=readElmResponse(in,"229005");
        Matcher mv=pV.matcher(vText.replaceAll("\\s+",""));
        if(!mv.find())throw new Exception("229005 decode RX="+vText);

        int rawV=Integer.parseInt(mv.group(1),16);
        double volts=rawV*0.1;

        tasker.setVariable("ZOE_VOLTAGE",
            String.format(java.util.Locale.US,"%.1f",volts));

        /* Current: signed, with explicit validity check */
        String iText=readElmResponse(in,"22900D");
        String iClean=iText.replaceAll("\\s+","");
        Matcher mi=pI.matcher(iClean);
        if(!mi.find())throw new Exception("22900D decode RX="+iText);

        long rawI=Long.parseLong(mi.group(1),16);

        /*
         * IMPORTANT: 22900D is a 32-bit raw field (4 bytes).
         * The response observed on the Zoe is for example:
         *   62900D0000BB6F &gt;
         * so the raw value is 0x0000BB6F, NOT 0x0000.
         * CanZE: scale=0.025, offset=48000.
         */
        boolean currentValid=(rawI!=0);
        double amps=currentValid ? (rawI-48000.0)*0.025 : 0.0;

        tasker.setVariable("ZOE_CURRENT",
            String.format(java.util.Locale.US,"%.2f",amps));
        tasker.setVariable("ZOE_CURRENT_RAW",
            String.format(java.util.Locale.US,"%08X",rawI));
        tasker.setVariable("ZOE_CURRENT_VALID",currentValid?"1":"0");

        /*
         * Single diagnostic variable: easier to expose reliably in Tasker.
         */
        tasker.setVariable("ZOE_DEBUG_LBC",
            "V_RX="+vText+
            " | V_RAW="+String.format(java.util.Locale.US,"%04X",rawV)+
            " | V="+String.format(java.util.Locale.US,"%.1f",volts)+
            " | I_RX="+iText+
            " | I_RAW="+String.format(java.util.Locale.US,"%08X",rawI)+
            " | I_VALID="+(currentValid?"1":"0")+
            " | I="+String.format(java.util.Locale.US,"%.2f",amps));

        /*
         * Power from battery voltage/current.
         * Invalid current =&gt; zero power, never bogus -456.84 kW.
         */
        double rawPower=currentValid ? volts*amps/1000.0 : 0.0;

        /*
         * ABRP convention used by this task:
         * battery input/charging = negative
         * battery output/driving = positive
         *
         * Therefore invert the physical battery power.
         */
        double abrpPower=-rawPower;

        tasker.setVariable("ZOE_POWER_RAW",
            String.format(java.util.Locale.US,"%.2f",rawPower));

        tasker.setVariable("ZOE_POWER_ABRP",
            String.format(java.util.Locale.US,"%.2f",abrpPower));

        tasker.setVariable("ZOE_DEBUG_POWER",
            "V="+String.format(java.util.Locale.US,"%.1f",volts)+
            " | I="+String.format(java.util.Locale.US,"%.2f",amps)+
            " | I_VALID="+(currentValid?"1":"0")+
            " | P_RAW="+String.format(java.util.Locale.US,"%.2f",rawPower)+
            " | P_ABRP="+String.format(java.util.Locale.US,"%.2f",abrpPower));

        tasker.setVariable("ZOE_LIVE_LAST_FAST",
            String.valueOf(System.currentTimeMillis()/1000L));

        /* =====================================================
         * EVC
         * ===================================================== */
        sendAtAndDrain(in,out,"ATSHDADAF1",80);
        sendAtAndDrain(in,out,"ATFCSH18DADAF1",80);
        sendAtAndDrain(in,out,"ATCRA18DAF1DA",80);
        clearElm(in);

        /* Speed */
        String speedText=readElmResponse(in,"222003");
        tasker.setVariable("ZOE_EVC_SPEED_LEN",String.valueOf(speedText.length()));
        String speedClean=speedText.replaceAll("\\s+","");
        Matcher mSpeed=pSpeed.matcher(speedClean);

        double speedKmh=-1.0;

        if(mSpeed.find()){
            int rawSpeed=Integer.parseInt(mSpeed.group(1),16);
            speedKmh=rawSpeed*0.01;
            tasker.setVariable("ZOE_SPEED",
                String.format(java.util.Locale.US,"%.2f",speedKmh));
            tasker.setVariable("ZOE_EVC_SPEED_OK","1");
            tasker.setVariable("ZOE_DEBUG_SPEED",
                "RX="+speedText+" | LEN="+speedText.length()+
                " | RAW="+String.format(java.util.Locale.US,"%04X",rawSpeed)+
                " | SPEED="+String.format(java.util.Locale.US,"%.2f",speedKmh));
        }else{
            tasker.setVariable("ZOE_EVC_SPEED_OK","0");
            tasker.setVariable("ZOE_DEBUG_SPEED",
                "RX="+speedText+" | LEN="+speedText.length()+" | DECODE=FAIL");
            String oldSpeed=tasker.getVariable("ZOE_SPEED");
            if(oldSpeed!=null &amp;&amp; oldSpeed.length()&gt;0){
                try{speedKmh=Double.parseDouble(oldSpeed);}catch(Exception ignored){}
            }
        }

        /* Plug */
        String plugText=readElmResponse(in,"222B85");
        tasker.setVariable("ZOE_EVC_PLUG_LEN",String.valueOf(plugText.length()));
        String plugClean=plugText.replaceAll("\\s+","");
        Matcher mPlug=pPlug.matcher(plugClean);

        boolean plugged=false;

        if(mPlug.find()){
            int plugRaw=Integer.parseInt(mPlug.group(1),16);

            /*
             * ZE50 Phase 2: the first data byte is the byte
             * called B4 by the WiCAN expression.  evDash reads
             * bit 31, i.e. the MSB of this byte.
             * Therefore do NOT compare the byte to literal 0xB4.
             */
            plugged=(plugRaw &amp; 0x80)!=0;

            tasker.setVariable("ZOE_PLUGGED",plugged?"1":"0");
            tasker.setVariable("ZOE_DEBUG_PLUG",
                "RX="+plugText+" | LEN="+plugText.length()+
                " | RAW="+String.format(java.util.Locale.US,"%02X",plugRaw)+
                " | PLUGGED="+(plugged?"1":"0"));
            tasker.setVariable("ZOE_EVC_PLUG_OK","1");
        }else{
            tasker.setVariable("ZOE_EVC_PLUG_OK","0");
            tasker.setVariable("ZOE_DEBUG_PLUG",
                "RX="+plugText+" | LEN="+plugText.length()+" | DECODE=FAIL");
            String oldPlug=tasker.getVariable("ZOE_PLUGGED");
            if(oldPlug!=null &amp;&amp; oldPlug.length()&gt;0)
                plugged=oldPlug.equals("1");
        }

        /* =====================================================
         * isCharging
         * ===================================================== */
        boolean charging=false;

        /*
         * If moving: NEVER charging.
         * This protects against regenerative braking.
         */
        if(speedKmh&gt;=0.0 &amp;&amp; speedKmh&gt;1.0){
            charging=false;
        }else if(speedKmh&gt;=0.0){
            /*
             * Stopped:
             * cable must be plugged and battery current must
             * indicate battery input.
             *
             * NOTE: sign convention of 22900D should be
             * confirmed with one real charge sample.
             */
            charging=plugged &amp;&amp; amps&gt;2.0;
        }else{
            /*
             * Speed unavailable:
             * conservative choice = do not declare charging
             * from current alone.
             */
            charging=false;
        }

        tasker.setVariable("ZOE_IS_CHARGING",charging?"1":"0");

        tasker.setVariable("ZOE_LIVE_SPEED",
            String.format(java.util.Locale.US,"%.2f",speedKmh));

        tasker.setVariable("ZOE_LIVE_PLUGGED",plugged?"1":"0");

        /* =====================================================
         * SLOW LBC - max once/minute
         * ===================================================== */
        String lastSlow=tasker.getVariable("ZOE_LAST_SLOW_MS");
        long now=System.currentTimeMillis();

        if(lastSlow==null || lastSlow.length()==0 ||
           now-Long.parseLong(lastSlow)&gt;=60000L){

            out.write(("ATSHDADBF1"+cr).getBytes("US-ASCII")); out.flush(); Thread.sleep(80);
            out.write(("ATFCSH18DADBF1"+cr).getBytes("US-ASCII")); out.flush(); Thread.sleep(80);
            out.write(("ATCRA18DAF1DB"+cr).getBytes("US-ASCII")); out.flush(); Thread.sleep(80);

            /* SOC */
            String rs=readElmResponse(in,"229002");
            Matcher ms=pS.matcher(rs.replaceAll("\\s+",""));
            if(!ms.find())throw new Exception("229002 decode RX="+rs);
            int sraw=Integer.parseInt(ms.group(1),16);
            tasker.setVariable("ZOE_SOC",
                String.format(java.util.Locale.US,"%.2f",sraw*0.01));

            /* SOH */
            rs=readElmResponse(in,"229003");
            Matcher mh=pH.matcher(rs.replaceAll("\\s+",""));
            if(!mh.find())throw new Exception("229003 decode RX="+rs);
            int hraw=Integer.parseInt(mh.group(1),16);
            tasker.setVariable("ZOE_SOH",
                String.format(java.util.Locale.US,"%.2f",hraw*0.01));

            /* Temperature */
            rs=readElmResponse(in,"229012");
            Matcher mt=pT.matcher(rs.replaceAll("\\s+",""));
            if(!mt.find())throw new Exception("229012 decode RX="+rs);
            int traw=Integer.parseInt(mt.group(1),16);
            tasker.setVariable("ZOE_BATT_TEMP",
                String.format(java.util.Locale.US,"%.2f",
                    (traw-640.0)*0.0625));

            /* Max charge power */
            rs=readElmResponse(in,"229018");
            Matcher mc=pC.matcher(rs.replaceAll("\\s+",""));
            if(!mc.find())throw new Exception("229018 decode RX="+rs);
            int craw=Integer.parseInt(mc.group(1),16);
            tasker.setVariable("ZOE_CHG_MAX",
                String.format(java.util.Locale.US,"%.2f",craw*0.01));

            /*
             * 229019 intentionally NOT used for isCharging.
             */

            tasker.setVariable("ZOE_LAST_SLOW_MS",
                String.valueOf(System.currentTimeMillis()));
        }

        /* =====================================================
         * TELEMETRY
         * ===================================================== */
        String ch2=tasker.getVariable("ZOE_IS_CHARGING");
        if(ch2==null)ch2="0";
        boolean chargeNow=ch2.equals("1");

        long period=chargeNow?30000L:3000L;

        String lastSend=tasker.getVariable("ZOE_ABRP_LAST_SEND_MS");
        long ts=System.currentTimeMillis();

        if(lastSend==null || lastSend.length()==0 ||
           ts-Long.parseLong(lastSend)&gt;=period){

            String soc=tasker.getVariable("ZOE_SOC");
            String soh=tasker.getVariable("ZOE_SOH");
            String pwr=tasker.getVariable("ZOE_POWER_ABRP");
            String temp=tasker.getVariable("ZOE_BATT_TEMP");
            String voltage=tasker.getVariable("ZOE_VOLTAGE");
            String current=tasker.getVariable("ZOE_CURRENT");

            if(soc!=null&amp;&amp;soc.length()&gt;0&amp;&amp;soh!=null&amp;&amp;soh.length()&gt;0&amp;&amp;
               pwr!=null&amp;&amp;pwr.length()&gt;0&amp;&amp;temp!=null&amp;&amp;temp.length()&gt;0&amp;&amp;
               voltage!=null&amp;&amp;voltage.length()&gt;0&amp;&amp;current!=null&amp;&amp;current.length()&gt;0){

                StringBuilder tlm=new StringBuilder();
                tlm.append("{\"utc\":")
                   .append(System.currentTimeMillis()/1000L);
                tlm.append(",\"soc\":").append(soc);
                tlm.append(",\"soh\":").append(soh);
                tlm.append(",\"power\":").append(pwr);
                tlm.append(",\"batt_temp\":").append(temp);
                tlm.append(",\"voltage\":").append(voltage);
                tlm.append(",\"current\":").append(current);
                tlm.append(",\"is_charging\":")
                   .append(chargeNow?"1":"0");
                tlm.append(",\"is_dcfc\":0");

                boolean parked=(speedKmh&gt;=0.0 &amp;&amp; speedKmh&lt;=1.0);

                /* Tasker %LOC is normally "latitude,longitude". */
                String loc=tasker.getVariable("LOC");
                double lat=Double.NaN;
                double lon=Double.NaN;
                if(loc!=null){
                    try{
                        String[] lp=loc.trim().split(",");
                        if(lp.length&gt;=2){
                            lat=Double.parseDouble(lp[0].trim());
                            lon=Double.parseDouble(lp[1].trim());
                        }
                    }catch(Exception ignored){}
                }

                tlm.append(",\"is_parked\":")
                   .append(parked?"1":"0");

                /* Position/speed: ABRP speed is km/h, lat/lon are degrees. */
                if(speedKmh&gt;=0.0)
                    tlm.append(",\"speed\":")
                       .append(String.format(java.util.Locale.US,"%.2f",speedKmh));
                if(!Double.isNaN(lat) &amp;&amp; !Double.isNaN(lon)){
                    tlm.append(",\"lat\":")
                       .append(String.format(java.util.Locale.US,"%.6f",lat));
                    tlm.append(",\"lon\":")
                       .append(String.format(java.util.Locale.US,"%.6f",lon));
                }
                tlm.append("}");

                /* ABRP */
                HttpURLConnection ac=null;
                try{
                    URL u=new URL("https://api.iternio.com/1/tlm/send");
                    ac=(HttpURLConnection)u.openConnection();
                    ac.setRequestMethod("POST");
                    ac.setConnectTimeout(5000);
                    ac.setReadTimeout(5000);
                    ac.setDoOutput(true);
                    ac.setRequestProperty(
                        "Content-Type","application/json; charset=utf-8");
                    ac.setRequestProperty(
                        "Authorization",
                        "APIKEY 78f9e662-cbcc-4064-8fa9-1c8198564dd3");

                    String body="{\"tlm\":"+tlm.toString()+
                        ",\"token\":\"15e7573f-fa2b-49f8-a5dc-65014de88341\"}";

                    OutputStream ao=ac.getOutputStream();
                    ao.write(body.getBytes("UTF-8"));
                    ao.flush();
                    ao.close();

                    int code=ac.getResponseCode();
                    tasker.setVariable("ZOE_ABRP_HTTP_CODE",
                        String.valueOf(code));

                    InputStream ai=(code&gt;=200&amp;&amp;code&lt;400)?
                        ac.getInputStream():ac.getErrorStream();

                    String resp="";
                    if(ai!=null){
                        BufferedReader br=new BufferedReader(
                            new InputStreamReader(ai,"UTF-8"));
                        String line;
                        while((line=br.readLine())!=null)resp+=line;
                        br.close();
                    }

                    tasker.setVariable("ZOE_ABRP_RESPONSE",resp);
                    tasker.setVariable("ZOE_ABRP_ERROR",
                        code&gt;=200&amp;&amp;code&lt;300?"":"HTTP "+code+": "+resp);

                }catch(Exception ae){
                    tasker.setVariable("ZOE_ABRP_ERROR",ae.toString());
                }finally{
                    if(ac!=null)ac.disconnect();
                }

                /* RED */
                HttpURLConnection rc=null;
                try{
                    URL u2=new URL("https://red.engles.fr/openapi/zoe");
                    rc=(HttpURLConnection)u2.openConnection();
                    rc.setRequestMethod("POST");
                    rc.setConnectTimeout(5000);
                    rc.setReadTimeout(5000);
                    rc.setDoOutput(true);
                    rc.setRequestProperty(
                        "Content-Type","application/json; charset=utf-8");
                    rc.setRequestProperty("Accept","application/json");

                    tasker.setVariable(
                        "ZOE_RED_SENT_JSON",tlm.toString());

                    OutputStream ro=rc.getOutputStream();
                    ro.write(tlm.toString().getBytes("UTF-8"));
                    ro.flush();
                    ro.close();

                    int code2=rc.getResponseCode();
                    tasker.setVariable("ZOE_RED_HTTP_CODE",
                        String.valueOf(code2));

                    InputStream ri=(code2&gt;=200&amp;&amp;code2&lt;400)?
                        rc.getInputStream():rc.getErrorStream();

                    String rr="";
                    if(ri!=null){
                        BufferedReader br2=new BufferedReader(
                            new InputStreamReader(ri,"UTF-8"));
                        String line2;
                        while((line2=br2.readLine())!=null)rr+=line2;
                        br2.close();
                    }

                    tasker.setVariable("ZOE_RED_RESPONSE",rr);
                    tasker.setVariable("ZOE_RED_ERROR",
                        code2&gt;=200&amp;&amp;code2&lt;300?"":"HTTP "+code2+": "+rr);
                    tasker.setVariable("ZOE_RED_LAST_SEND",
                        String.valueOf(System.currentTimeMillis()/1000L));

                }catch(Exception re){
                    tasker.setVariable("ZOE_RED_ERROR",re.toString());
                }finally{
                    if(rc!=null)rc.disconnect();
                }

                tasker.setVariable("ZOE_ABRP_POWER_SENT",pwr);
                tasker.setVariable("ZOE_ABRP_PERIOD",
                    String.valueOf(period/1000L));
                tasker.setVariable("ZOE_ABRP_LAST_SEND",
                    String.valueOf(System.currentTimeMillis()/1000L));
                tasker.setVariable("ZOE_ABRP_LAST_SEND_MS",
                    String.valueOf(System.currentTimeMillis()));
                tasker.setVariable("ZOE_ABRP_SEND_OK","1");
                tasker.setVariable("ZOE_ABRP_SKIP_REASON","");
            }else{
                tasker.setVariable("ZOE_ABRP_SEND_OK","0");
                tasker.setVariable("ZOE_ABRP_SKIP_REASON",
                    "missing telemetry variable: soc="+String.valueOf(soc)+
                    " soh="+String.valueOf(soh)+
                    " power="+String.valueOf(pwr)+
                    " temp="+String.valueOf(temp)+
                    " voltage="+String.valueOf(voltage)+
                    " current="+String.valueOf(current));
            }
        }

        success=true;
        tasker.setVariable("ZOE_LIVE_CONNECTED","1");
        tasker.setVariable("ZOE_LIVE_STATE",
            chargeNow?"Charging":"Live");
        tasker.setVariable("ZOE_LIVE_ERROR","");

    }catch(Exception e){

        lastError=e;

        tasker.setVariable(
            "ZOE_LIVE_ERROR",
            "READ attempt "+(attempt+1)+": "+
            e.getClass().getName()+": "+
            String.valueOf(e.getMessage()));

        try{
            Object s=tasker.getJavaVariable("zOE_SOCKET");
            if(s!=null)((BluetoothSocket)s).close();
        }catch(Exception ignored){}

        tasker.setJavaVariable("zOE_SOCKET",null);
        tasker.setVariable("ZOE_LIVE_CONNECTED","0");

        if(attempt==0)Thread.sleep(1000L);
    }
}

if(!success){
    tasker.setVariable("ZOE_LIVE_STATE","Disconnected");
    throw new Exception(
        "ZOE READ failed after reconnect: "+
        String.valueOf(lastError.getMessage()));
}
</Str>
			<Str sr="arg1" ve="3"/>
			<Int sr="arg2" val="0"/>
		</Action>
	</Task>
</TaskerData>
