#DZ_VERSION_@24.7.15.3@

var ms_to_use_for_calibration = 5000

import updater
tasmota.add_driver(updater)

var c_orange = 64512

var wifi_name
var wifi_pass

var tft_flash_url = "http://bin.hidravlika-consult.com/dozator4/nima/hmi_project_nima.tft"
var berry_script_url = "http://bin.hidravlika-consult.com/dozator4/nima/autoexec.be"
var updater_script_url = "http://bin.hidravlika-consult.com/dozator4/nima/updater.be"

def d_init_hostname(cmd,idx,id)    
    tasmota.cmd('backlog hostname dozator4-'+str(id))
    tasmota.resp_cmnd_done()
end

def d_set_device_id(cmd,idx,devid)
    import persist
    persist.setmember('device_id',str(devid))
    persist.save()
    tasmota.cmd('d_init_hostname '+str(devid))
    tasmota.resp_cmnd_done()
end


tasmota.add_cmd('d_init_hostname', d_init_hostname)
tasmota.add_cmd('d_set_device_id', d_set_device_id)
tasmota.add_cmd('d_update_the_updater', def() tasmota.cmd("Backlog UrlFetch "+ updater_script_url +"; Restart 1") end  )

def update_autoexec_()
    tasmota.cmd("Backlog UrlFetch "+ berry_script_url +"; Restart 1")
end

def update_autoexec()
    tasmota.set_timer(50,/->update_autoexec_())
    tasmota.resp_cmnd_done()
end

tasmota.add_cmd('updme', update_autoexec)
tasmota.add_cmd('d_update_script', update_autoexec)

def split_multi_json(instr)    
    import string
    var ret = []
    var pos = string.find(instr,'}{')
    if pos>-1
        var sp = string.split(instr,pos+1)
        ret.push(sp[0]) 
        var rest = sp[1]
        var notfinished = true
        while notfinished
            pos = string.find(rest,'}{')
            if pos>-1
                sp = string.split(rest,pos+1)
                ret.push(sp[0]) 
                rest = sp[1]
            else
                ret.push(rest)
                notfinished = false
            end
        end
    else 
        return [instr]
    end
    return ret
end

def bullshit_nextion_float_represent(in_real,digits_in_divisor)
    if digits_in_divisor==nil digits_in_divisor=2 end
    import string    
    import math
    return str(int(in_real*math.pow(10,2)))    
end

class Nextion : Driver
    static VERSION = "1.1.4"
    static header = bytes().fromstring("PS")
    static flash_block_size = 4096 


    var prev_switch_state 
    
    static base_teleperiod = 600

    var calibration_raw_ml
    var calibration_raw_koef
    var page
    var page_previous
    var page_beep_on_boot
    var zadanie_ml 
    var koef_ml_to_ms
    var koef_cycle_to_kg  
    var totaliser_kgs_pumped     
    var totaliser_kgs_pumped_all_time
    var ms_pumped_current_left 
    var calibration_raw_ms
    var panel_key_val_map
    var persist_key_val_map
    var zadanie_ms_to_pump_total
    var pump_current_state
    var pump_active_off_timer
    var flash_mode
    var flash_size
    var flash_written
    var flash_buff
    var flash_offset
    var awaiting_offset
    var tcp
    var ser
    var last_per
    var auto_update_flag
    var device_id
    var sys_menu

    var relay_used 

    var totalizer_timer

    #var fast_loop_turn_off 
    #var fast_loop_closure

    var pump_turn_off_after

    var manual_pump_request

    def off_relay_0()
        #used as callback for tasmota.set_timer with ms precision
        #set power 0 index based
        tasmota.set_power(0,false)
        # log(str(tasmota.millis()))
        self.pump_current_state = false
        log('DOZATOR: Pump off')
    end
    
    def off_relay_1()
        #used as callback for tasmota.set_timer with ms precision        
        tasmota.set_power(1,false)
        self.pump_current_state = false
        log('DOZATOR: Pump off second relay')
    end

    #def pump_for_ms_exact_impl(ms_amt)
    #   
    #end
    #def pump_for_ms_exact_cmd(a,b,ms_amt_)
    #    self.pump_for_ms_exact_impl(int(ms_amt_))
    #    
    #end

    def pump_for_ms_r0()
        log('DOZATOR: MS to pump ' + str(self.zadanie_ms_to_pump_total))
        # log(str(tasmota.millis()))
        #0 because relays are 0 indexed on .set_power

        var pump_timer_id = 100
        self.pump_active_off_timer = pump_timer_id

        tasmota.set_power(0,true)
        self.pump_current_state = true
        #@@@@@@@@@@@@@@@@@@@
        tasmota.set_timer(self.zadanie_ms_to_pump_total, /->self.off_relay_0(),pump_timer_id)
        #tasmota.set_timer(5*1000, /->self.off_relay_0(),pump_timer_id)
    end

    def pump_actuate_callback()                        
        
        #var cur_switch_state = tasmota.get_switches()[0]

        var cur_switch_state = !gpio.digital_read(23)
        # cur_switch_state = true

        #log('DOZATOR: actuate! s'+str(cur_switch_state))
        if !self.manual_pump_request
            if !cur_switch_state 
                return
            end
        end

        if !self.pump_current_state
            self.pump_current_state = true
            tasmota.set_power(self.relay_used,true)
            tasmota.delay(self.zadanie_ms_to_pump_total)
            tasmota.set_power(self.relay_used,false)
            self.pump_current_state = false
            self.manual_pump_request = false
            tasmota.set_timer(20, /->self.count_kg_cycle_one())
            #self.count_kg_cycle_one()
        end      
        
    end    

    #def fast_loop()
        #-
       
        var cur_switch_state = tasmota.get_switches()[0]
        
        if self.manual_pump_request || (cur_switch_state && !self.pump_current_state )   
            self.manual_pump_request = false
            self.pump_current_state = true            
            tasmota.set_power(self.relay_used,true)
            self.pump_turn_off_after = tasmota.millis(  self.zadanie_ms_to_pump_total )
            
            tasmota.set_timer(self.zadanie_ms_to_pump_total, /->self.count_kg_cycle_one())
            
        end

        if self.pump_current_state
            if tasmota.time_reached(self.pump_turn_off_after)
                tasmota.set_power(self.relay_used,false)
                self.pump_current_state = false
            end
        end
        
        if self.fast_loop_turn_off
            tasmota.remove_fast_loop(self.fast_loop_closure)
            if self.pump_current_state
                tasmota.set_power(0,false)
                tasmota.set_power(1,false)
            end
        end
        self.prev_switch_state = cur_switch_state
        -#
    #end

    # kraen izklichvatel
    #def hndl_key_gpio23(value, trigger, data)        
    #    if tasmota.get_switches()[0] && self.pump_current_state == false
    #        self.zadanie_ms_to_pump_total = int(self.zadanie_ml * self.koef_ml_to_ms)
    #        self.pump_for_ms_r0()
    #        tasmota.set_timer(self.zadanie_ms_to_pump_total, /->self.count_kg_cycle_one())
    #        
    #        #if self.page=="main"
    #        #    self.sendnx("main.j0.val=100")
    #        #    self.sendnx("tmpmp.en=1")
    #        #end
    #        log("DOZATOR: handle key_gpio23 state")            
    #    else             
    #        log("DOZATOR: handle key_gpio23 state not pressed")
    #    end         
    #end

    def hndl_btn1_press(state,b,c,d)    
        var p = self.page
        log("DOZATOR: btn1 state "+str(state))
        log("DOZATOR: "+str(b))
        log("DOZATOR: "+str(c))
        log("DOZATOR: "+str(d))
        if state != 10
            return
        end        
        if p=='main' || p == 'tot'
            self.sendnx('page cmenu_nowifi')
        elif p=='cmenu_nowifi'
            self.sendnx('page main')
        elif p == 'conf' || p == 'cali' || p=='wfc' || p=='tot'
            self.sendnx('click b0,1')
            self.sendnx('click b0,0')
        elif p == "kbd1"
            self.sendnx('click b200,1')
            self.sendnx('click b200,0')
        end
        log("DOZATOR: handle btn1_press_state")
    end

    def hndl_btn2_press(state,b,c,d)
        var p = self.page
        log("DOZATOR: btn2 state "+str(state))
        log("DOZATOR: "+str(b))
        log("DOZATOR: "+str(c))
        log("DOZATOR: "+str(d))

        if p == "cmenu_nowifi"
            if self.sys_menu <10 
                self.sys_menu += 1
                log('DOZATOR: sys btn ' + str(self.sys_menu))
            else
                self.sys_menu = 0
                self.sendnx('page sys1menu')
            end
        end

        if state != 10
            return
        end

        
        
        if p == "main" || p == 'conf' || p == 'cali' || p=='tot'
            self.sendnx('click b1,1')
            self.sendnx('click b1,0')
        elif p == "kbd1"
            self.sendnx('click b210,1')
            self.sendnx('click b210,0')
        end
        log("DOZATOR: handle btn2_press_state")
    end
    
    def count_kg_cycle_one()
        import math
        log("DOZATOR: count_kg_cycle_one()")
        self.totaliser_kgs_pumped += 1 * self.koef_cycle_to_kg
        self.totaliser_kgs_pumped_all_time += 1 * self.koef_cycle_to_kg
        self.sendnx('main.n1.val='+str(math.floor(self.totaliser_kgs_pumped)))
        
        #every 15 seconds save totalizer unless another cycle happaned
        tasmota.remove_timer(self.totalizer_timer)
        tasmota.set_timer(100, /->self.save_totalizer(),self.totalizer_timer) 
        #self.save_totalizer()
        
    end

    def save_totalizer()
        log("DOZATOR: save_totalizer()")
        import persist
        persist.setmember( "totaliser_kgs_pumped" , str(self.totaliser_kgs_pumped)  )
        persist.setmember( "totaliser_kgs_pumped_all_time",str(self.totaliser_kgs_pumped_all_time))
        persist.save()

    end

    def init_dozator()

        self.sys_menu=0

        self.manual_pump_request=false
        self.prev_switch_state=false
        self.calibration_raw_ml=100
        self.calibration_raw_koef=87.85    
        self.totalizer_timer ='totalizertimer'

        log("DOZATOR: init dozator")
        import persist

        tasmota.delay(100)

        self.pump_current_state = false
        
        log("DOZATOR: loading persist data")

        self.device_id = persist.find('device_id','T000')
        self.sendnx('cmenu_nowifi.tdid.txt="SN: '+str(self.device_id) + '"')
        self.sendnx('cmenu.tdid.txt="SN: '+str(self.device_id) + '"')
        self.zadanie_ml = int( persist.find("zadanie_ml","100") )
        self.koef_ml_to_ms = real(  persist.find("koef_ml_to_ms","100") )
        self.totaliser_kgs_pumped = int(persist.find("totaliser_kgs_pumped","0"))
        self.totaliser_kgs_pumped_all_time = int(persist.find('totaliser_kgs_pumped_all_time','0'))
        self.koef_cycle_to_kg = real(persist.find("koef_cycle_to_kg","1.00"))
        self.calibration_raw_ms = self.calibration_raw_ml*self.calibration_raw_koef

        # 0 or 1
        self.relay_used = int(persist.find('relay_used','0'))

        if self.relay_used == 0
            self.sendnx('sys1menu.b2.bco='+str(c_orange))
            self.sendnx('sys1menu.b3.bco='+str(0))
        elif self.relay_used == 1
            self.sendnx('sys1menu.b2.bco='+str(0))
            self.sendnx('sys1menu.b3.bco='+str(c_orange))
        end


        log("DOZATOR: set n0: "+str(self.zadanie_ml))
        self.sendnx('main.n0.val='+str(self.zadanie_ml))
        log("DOZATOR: set n1: "+str(self.totaliser_kgs_pumped))
        self.sendnx('main.n1.val='+str(self.totaliser_kgs_pumped))
        log("DOZATOR: set x0:"+bullshit_nextion_float_represent(self.koef_ml_to_ms))
        #self.sendnx('conf.x0.val='+bullshit_nextion_float_represent(self.koef_ml_to_ms))
        log("DOZATOR: set x1: "+bullshit_nextion_float_represent(self.koef_cycle_to_kg))
        self.sendnx('conf.x1.val='+bullshit_nextion_float_represent(self.koef_cycle_to_kg))

        log("DOZATOR: set cal x0 x1 ")
        self.sendnx('calibration.x0.val='+bullshit_nextion_float_represent(self.calibration_raw_ml))
        self.sendnx('calibration.x1.val='+bullshit_nextion_float_represent(self.calibration_raw_koef))


        
        self.zadanie_ms_to_pump_total = int(self.zadanie_ml * self.koef_ml_to_ms)

        # self.panel_key_val_map ={
        #      'n0':self.zadanie_ml,
        #      'n1':self.totaliser_kgs_pumped,
        #      'cx0':self.koef_ml_to_ms,
        #      'cx1':self.koef_cycle_to_kg
        # }

        self.persist_key_val_map ={
            'n0':'zadanie_ml',
            'n1':'totaliser_kgs_pumped',
            'cx0':'koef_ml_to_ms',
            'cx1':'koef_cycle_to_kg'
        }

        # @@@@@@@ 21-08-2024    
        #tasmota.cmd("Sleep 1")

        tasmota.cmd("Sleep 1") 
        
        self.page_beep_on_boot = false
        self.page='initpage'
        self.page_previous='initpage'
        self.pump_active_off_timer = -1
        
        

        tasmota.cmd("SetOption0 0",true) 
        tasmota.cmd("SetOption1 0",true)

        #PowerOnState	Control relay state after powering up the device.
        #0 / OFF = keep relay(s) OFF after power up
        tasmota.cmd("PowerOnState 0",true)

        # Detach buttons from relays and send multi-press and hold MQTT messages instead of controling relays
        tasmota.cmd("SetOption73 1",true)
        #When SetOption114 1 all switches are detached from their respective relays and will send MQTT messages instead in the form of {"Switch<x>":{"Action":"<state>"}}.
        tasmota.cmd("SetOption114 1",true)



        tasmota.cmd("SwitchMode3 1",true)


        tasmota.add_rule("Button1#state", / state,b,c,d -> self.hndl_btn1_press(state,b,c,d))
        tasmota.add_rule("Button2#state", / state,b,c,d -> self.hndl_btn2_press(state,b,c,d))

        # end switch state is inverted 
        #tasmota.add_rule('Switch3#state', /-> self.hndl_key_gpio23()) 

        tasmota.add_rule('Switch3#state', /-> self.pump_actuate_callback()) 
        


        # we are using fast_loop instead - 15-07-2024

        #self.fast_loop_closure = def () self.fast_loop() end
        #tasmota.add_fast_loop(self.fast_loop_closure)

        
        var curr_teleperiod = tasmota.cmd('teleperiod',true)['TelePeriod']
        if curr_teleperiod != self.base_teleperiod
            tasmota.cmd('teleperiod '+str(self.base_teleperiod),true)
        end
        
        var tasmota_wifi_config_mode = tasmota.cmd('WifiConfig')['WifiConfig']
        if !tasmota_wifi_config_mode.has('4')
            log('DOZATOR: !! Wifi config mode is not == 4 !! consider executing:  WifiConfig 4')
            #tasmota.cmd('WifiConfig 4')
        end
         
        wifi_name = persist.find('wfn','config')     
        wifi_pass = persist.find('wfp','12345678')                

        #@todo: enable wifi check when wifi immplemented
        #self.sendnx("tm4wfchk.en=1")

        var wifi_toggle_current = persist.find('wifitoggle','true')
        if wifi_toggle_current == 'false'
            tasmota.cmd('Wifi 0')
            #turn off fast reboot to clear settings
            if tasmota.cmd('SetOption65')['SetOption65'] == "OFF"
                tasmota.cmd("SetOption65 1",true) 
            end
        end

        if wifi_toggle_current == 'true'
            if tasmota.cmd('Wifi')['Wifi'] != "ON"
                tasmota.cmd("Wifi 1",true) 
            end
            
            #turn on fast reboot to clear settings
            if tasmota.cmd('SetOption65')['SetOption65'] == "ON"
                tasmota.cmd("SetOption65 0",true) 
            end
        end

        tasmota.cmd('Time 1724234702')
           
        log("DOZATOR: init dozator finished")

        def goto_main_page() self.sendnx('varstoload.val=0') end
        tasmota.set_timer(1000, goto_main_page)                

        tasmota.cmd('BuzzerPWM 1',true) # enable buzzer pwm

    end 


    def split_msg(b)   
        import string
        var ret = []
        var i = 0
        while i < size(b)-1
            if b[i] == 0x55 && b[i+1] == 0xAA
                if i > 0
                    var nb = b[0..i-1];
                    ret.push(nb)
                end
                b = b[i+2..]
                i = 0
            else
                i+=1
            end
        end
        if size(b) > 0
            ret.push(b)
        end
        return ret
    end

    def crc16(data, poly)
      if !poly  poly = 0xA001 end
      # CRC-16 MODBUS HASHING ALGORITHM
      var crc = 0xFFFF
      for i:0..size(data)-1
        crc = crc ^ data[i]
        for j:0..7
          if crc & 1
            crc = (crc >> 1) ^ poly
          else
            crc = crc >> 1
          end
        end
      end
      return crc
    end

    def encode(payload)
      var b = bytes()
      b += self.header
      var nsp_type = 0 # not used
      b.add(nsp_type)       # add a single byte
      var b1 = bytes().fromstring(payload)
      var b2 = bytes()
      for i: 0..size(b1)-1
        if (b1[i]!=0xC2)
            b2.add(b1[i])
        end
      end
      b.add(size(b2), 2)   # add size as 2 bytes, little endian
      b += b2
      var msg_crc = self.crc16(b)
      b.add(msg_crc, 2)       # crc 2 bytes, little endian
      return b
    end

    def encodenx(payload)
        var b = bytes().fromstring(payload)
        b += bytes('FFFFFF')
        return b
    end

    def sendnx(payload)
        import string
        var payload_bin = self.encodenx(payload)
        self.ser.write(payload_bin)
        log(string.format("NXP: Nextion command sent = %s",str(payload_bin)), 3)       
    end

    def send(payload)
        var payload_bin = self.encode(payload)
        if self.flash_mode==1
            log("NXP: skipped command becuase still flashing", 3)
        else 
            self.ser.write(payload_bin)
            log("NXP: payload sent = " + str(payload_bin), 3)
        end
    end

    def write_to_nextion(b)
        self.ser.write(b)
    end

    def screeninit()
        log("NXP: Screen Initialized") 
        tasmota.delay(150)
        self.init_dozator()
    end

    def write_block()
        
        import string
        log("FLH: Read block",3)
        while size(self.flash_buff)<self.flash_block_size && self.tcp.connected()
            if self.tcp.available()>0
                self.flash_buff += self.tcp.readbytes()
            else
                tasmota.delay(50)
                log("FLH: Wait for available...",3)
            end
        end
        log("FLH: Buff size "+str(size(self.flash_buff)),3)
        var to_write
        if size(self.flash_buff)>self.flash_block_size
            to_write = self.flash_buff[0..self.flash_block_size-1]
            self.flash_buff = self.flash_buff[self.flash_block_size..]
        else
            to_write = self.flash_buff
            self.flash_buff = bytes()
        end
        log("FLH: Writing "+str(size(to_write)),3)
        var per = (self.flash_written*100)/self.flash_size
        if (self.last_per!=per) 
            self.last_per = per
            tasmota.publish_result(string.format("{\"Flashing\":{\"complete\": %d}}",per), "RESULT") 
        end
        if size(to_write)>0
            self.flash_written += size(to_write)
            if self.flash_offset==0 || self.flash_written>self.flash_offset
                self.ser.write(to_write)
                self.flash_offset = 0
            else
                tasmota.set_timer(10,/->self.write_block())
            end
        end
        log("FLH: Total "+str(self.flash_written),3)
        if (self.flash_written==self.flash_size)
            log("FLH: Flashing complete")
            self.flash_mode = 0
        end

    end

    def set_relay_to_use(relay_to_use)
        log('DOZATOR: set relay to use ' +str(relay_to_use))
        self.relay_used = int(relay_to_use)
        import persist     
        persist.setmember('relay_used',str(relay_to_use))
        persist.save()
        tasmota.set_power(0,false)
        tasmota.set_power(1,false)
        if self.relay_used == 0
            self.sendnx('sys1menu.b2.bco='+str(c_orange))
            self.sendnx('sys1menu.b3.bco='+str(0))
        elif self.relay_used == 1
            self.sendnx('sys1menu.b2.bco='+str(0))
            self.sendnx('sys1menu.b3.bco='+str(c_orange))
        end
    end

    #every_100ms
    def every_100ms()
        import json
        import string
        if self.ser.available() > 0
            var msg = self.ser.read()
            if size(msg) > 0
                log(string.format("NXP: Received Raw = %s",str(msg)), 3)
                if (self.flash_mode==1)
                    var strv = msg[0..-4].asstring()
                    if string.find(strv,"comok 2")>=0
                        log("FLH: Send (High Speed) flash start")
                        self.sendnx(string.format("whmi-wris %d,115200,res0",self.flash_size))
                    elif size(msg)==1 && msg[0]==0x08
                        log("FLH: Waiting offset...",3)
                        self.awaiting_offset = 1
                    elif size(msg)==4 && self.awaiting_offset==1
                        self.awaiting_offset = 0
                        self.flash_offset = msg.get(0,4)
                        log("FLH: Flash offset marker "+str(self.flash_offset),3)
                        self.write_block()
                    elif size(msg)==1 && msg[0]==0x05
                        self.write_block()
                    else
                        log("FLH: Something has gone wrong flashing nxpanel ["+str(msg)+"]",2)
                    end
                else
                    #reading serial from nspanel
                    var msg_list = self.split_msg(msg)
                    for i:0..size(msg_list)-1
                        msg = msg_list[i]
                        if size(msg) > 0
                            if (msg == bytes('000000FFFFFF88FFFFFF') ) || (msg == bytes('1AFFFFFF000000FFFFFF88') ) || (msg == bytes('88FFFFFF000000FFFFFF') ) 
                                self.screeninit()
                            elif msg[0]==0x7B # JSON, starting with "{"
                                var jm = string.format("%s",msg[0..-1].asstring())
                                log("NXP: received "+jm)
                                var j_arr = split_multi_json(jm)
                                for j:0..size(j_arr)-1
                                    var jl_parsed = json.load(j_arr[j])
                                    if jl_parsed.contains('set')
                                        import persist
                                        log("NXP: set " + jl_parsed['set'] +" to " + str(jl_parsed['val']) )       

                                        if jl_parsed['set'] == 'cx0'
                                            var _t_nv_ = real(  jl_parsed['val'] )/100 
                                            self.koef_ml_to_ms = _t_nv_
                                            self.zadanie_ms_to_pump_total = int(self.zadanie_ml * self.koef_ml_to_ms)
                                            persist.setmember('koef_ml_to_ms',str(_t_nv_))
                                            persist.save()
                                        elif jl_parsed['set'] == 'cx1'
                                            var _t_nv_ = real(  jl_parsed['val'] )/100 
                                            self.koef_cycle_to_kg = _t_nv_                                            
                                            persist.setmember('koef_cycle_to_kg',str(_t_nv_))
                                            persist.save()                                        
                                        elif jl_parsed['set'] == 'n0'
                                            self.zadanie_ml = int(jl_parsed['val'])
                                            self.zadanie_ms_to_pump_total = int(self.zadanie_ml * self.koef_ml_to_ms)
                                            persist.setmember( "zadanie_ml" , jl_parsed['val']  )
                                            persist.save()
                                        elif jl_parsed['set'] == 'n1'
                                            self.totaliser_kgs_pumped = int(jl_parsed['val'])
                                            persist.setmember('totaliser_kgs_pumped',jl_parsed['val'])
                                            persist.save()
                                        elif jl_parsed['set'] == 'cxcalreal0'

                                            var calreal0 = int( real(  jl_parsed['val'] )/100 )

                                            #var a = 

                                            self.koef_ml_to_ms = int(ms_to_use_for_calibration / calreal0)

                                            #var b = int( self.zadanie_ml * a )

                                            self.zadanie_ms_to_pump_total = int( self.zadanie_ml * self.koef_ml_to_ms )
                                            persist.setmember('koef_ml_to_ms',str(self.koef_ml_to_ms))
                                            persist.save()     
                                            log("DOZATOR: set conf.x0 koef_ml_to_ms :"+bullshit_nextion_float_represent(self.koef_ml_to_ms))
                                            #self.sendnx('conf.x0.val='+bullshit_nextion_float_represent(self.koef_ml_to_ms))
                                            self.sendnx('calibration.x1.val='+bullshit_nextion_float_represent(self.koef_ml_to_ms))
                                                
                                            #-
                                            log('DOZATOR:what')
                                            
                                            self.calibration_raw_ml = real(jl_parsed['x0']) /100
                                            self.calibration_raw_koef = real(jl_parsed['x1']) /100
                                            self.calibration_raw_ms = self.calibration_raw_ml*self.calibration_raw_koef
                                            self.koef_ml_to_ms  = self.calibration_raw_koef *  (self.calibration_raw_ml/calreal0  )  
                                            log('DOZATOR: new calibration '+str(calreal0))
                                            self.zadanie_ms_to_pump_total = int(self.zadanie_ml * self.koef_ml_to_ms)
                                            persist.setmember('koef_ml_to_ms',str(self.koef_ml_to_ms))
                                            persist.save()     
                                            log("DOZATOR: set conf.x0 koef_ml_to_ms :"+bullshit_nextion_float_represent(self.koef_ml_to_ms))
                                            self.sendnx('conf.x0.val='+bullshit_nextion_float_represent(self.koef_ml_to_ms))
                                            self.sendnx('calibration.x1.val='+bullshit_nextion_float_represent(self.koef_ml_to_ms))
                                            #self.sendnx('page main')

                                            -#

                                        elif jl_parsed['set']=='wfn'
                                            wifi_name = jl_parsed['val']
                                            persist.setmember('wfn',wifi_name)
                                            persist.save()
                                            #tasmota.cmd("SSID2 "+wifi_name)
                                        elif jl_parsed['set']=='wfp'                                                                                                
                                            wifi_pass = jl_parsed['val']
                                            persist.setmember('wfp',wifi_pass)
                                            persist.save()
                                            tasmota.cmd("Backlog SSID1 "+wifi_name+" ; Password1 "+wifi_pass)
                                            self.sendnx('page main')
                                        end                         
                                    end

                                    if jl_parsed.contains('pg')
                                        var wifi_ssid_active = nil
                                        self.page_previous = self.page
                                        self.page = jl_parsed['pg']
                                        log('Display: page set '+self.page)
                                        if !self.page_beep_on_boot
                                            self.page_beep_on_boot = true
                                            tasmota.cmd("buzzer 2,1",true)
                                        end

                                        if self.page_previous=='main' && self.page=='cmenu_nowifi'
                                            import math    
                                            self.sendnx('tot.n0.val='+str(math.floor(self.totaliser_kgs_pumped))) 
                                            self.sendnx('tot.n2.val='+str(math.floor(self.totaliser_kgs_pumped_all_time))) 
                                        end

                                        #@todo:handle  network loss better
                                        #currently network connected msg still shown after network loss!!!
                                        #@todo: consider adding wifi into AP mode switch 
                                        if self.page=='cmenu'
                                            import math    
                                            self.sendnx('tot.n0.val='+str(math.floor(self.totaliser_kgs_pumped)))
                                            self.sendnx('tot.n2.val='+str(math.floor(self.totaliser_kgs_pumped_all_time))) 

                                            var bt_svarzana=bytes().fromhex("7766636F6E662E74302E7478743D22F3D7DFD2DAC1CEC122ffffff")
                                            var bt_nocon=bytes().fromhex("7766636F6E662E74302E7478743D22F3D7DFD2DAC1CEC122ffffff")
                                            var ip = tasmota.wifi().find('ip')

                                            self.sendnx('wfconf.t2.txt="' + wifi_name + '"' )
                                            self.sendnx('wfconf.t4.txt="' + wifi_pass + '"' )
                                            if tasmota.wifi()['up'] == true 
                                                wifi_ssid_active = tasmota.cmd('state')['Wifi']['SSId']
                                                self.sendnx('wfconf.t5wfn.txt="'+wifi_ssid_active+'"')
                                            else
                                                self.sendnx('wfconf.t5wfn.txt=""')
                                            end

                                            if ip != nil && wifi_ssid_active != nil
                                                # Свързана                                                
                                                self.write_to_nextion(bt_svarzana)
                                                #self.sendnx('t0.txt="Свързана"')
                                                self.sendnx('wfconf.tip.txt="'+ip+'"')
                                            else
                                                self.write_to_nextion(bt_nocon)
                                                self.sendnx('wfconf.tip.txt="----"')
                                            end
                                            
                                        end

                                    end

                                    if jl_parsed.contains('button_test')
                                        log('NXP: jl1')                                                                        
                                        if jl_parsed['button_test'] == false
                                             
                                            #tasmota.set_power(0,true)
                                            if self.pump_current_state==true
                                                tasmota.set_power(0,false) 
                                                tasmota.set_power(1,false) 
                                                tasmota.remove_timer(self.pump_active_off_timer)
                                                self.pump_current_state=false                                                
                                            else
                                                self.zadanie_ms_to_pump_total = int(self.zadanie_ml * self.koef_ml_to_ms)
                                                self.manual_pump_request=true
                                                self.pump_actuate_callback()
                                            end 
                                        end
                                        if jl_parsed['button_test'] == true
                                            
                                            #tasmota.set_power(0,false)                                    
                                        end
                                    end

                                    if jl_parsed.contains('button_calstart')
                                        if jl_parsed['button_calstart'] == true
                                            log('DOZATOR: calibration start request btn push')
                                            #-
                                            self.calibration_raw_ml = real(jl_parsed['x0']) /100
                                            self.calibration_raw_koef = real(jl_parsed['x1']) /100
                                            self.calibration_raw_ms = self.calibration_raw_ml*self.calibration_raw_koef
                                            self.zadanie_ms_to_pump_total = int(self.calibration_raw_ms)
                                            self.manual_pump_request=true

                                            
                                            -#
                                            def click_on_xcalinput() self.sendnx('click xcalreal0,0') end
                                            tasmota.set_timer(5005,click_on_xcalinput)
                                            
                                            tasmota.set_power(self.relay_used,true)
                                            tasmota.delay(ms_to_use_for_calibration)
                                            tasmota.set_power(self.relay_used,false)

                                            
                                            
                                            
                                        elif jl_parsed['button_calstart'] == false
                                            log('DOZATOR: calibration start request btn release')
                                        end

                                    end

                                    if jl_parsed.contains('wifi_state_check')
                                        var tasmota_resp = tasmota.cmd('state')
                                        if tasmota_resp.has('Wifi')                                                                                     
                                            var wifi_state = tasmota.cmd('state')['Wifi']
                                            if tasmota.wifi()['up'] && wifi_state['SSId'] != nil
                                                if self.page == 'main'
                                                    log('DOZATOR: wifi is up')
                                                    self.sendnx('main.p0wif.pic=7')
                                                end
                                            else
                                                if self.page == 'main'
                                                    log('DOZATOR: wifi is down')
                                                    self.sendnx('main.p0wif.pic=6')
                                                end
                                            end
                                        else
                                            log('DOZATOR: WIFI IS NOT UP')
                                        end
                                    end
                                    
                                    if jl_parsed.contains('TRST0')
                                        self.totaliser_kgs_pumped=0
                                        import persist                                        
                                        persist.setmember('totaliser_kgs_pumped',0)
                                        persist.save()
                                        self.sendnx('tot.n0.val=0')
                                        self.sendnx('main.n1.val=0')
                                        #self.sendnx('page main')
                                        self.sendnx('page inter_saved')
                                        
                                    end

                                    if jl_parsed.contains('TRST2')
                                        self.totaliser_kgs_pumped_all_time = 0
                                        import persist                                        
                                        persist.setmember('totaliser_kgs_pumped_all_time',0)
                                        persist.save()
                                        self.sendnx('tot.n2.val=0')      
                                        self.sendnx('page main')
                                    end
                                    
                                    if jl_parsed.contains('setrelay')
                                        if int(jl_parsed['setrelay']) != self.relay_used
                                            self.set_relay_to_use( jl_parsed['setrelay'] )
                                        end
                                    end

                                    if jl_parsed.contains('wifitoggle')
                                        if int(jl_parsed['wifitoggle']) == 1
                                            import persist                                        
                                            persist.setmember('wifitoggle','true')
                                            persist.save()
                                            tasmota.cmd('Wifi 1')
                                        end
                                        if int(jl_parsed['wifitoggle']) == 0
                                            import persist                                        
                                            persist.setmember('wifitoggle','false')                                            
                                            persist.save()
                                            tasmota.cmd('Wifi 0')
                                        end
                                    end 
    
                                    tasmota.publish_result(jm, "RESULT")
                                end
                                        
                            elif msg[0]==0x07 && size(msg)==1 # BELL/Buzzer
                                tasmota.cmd("buzzer 1,1")
                            else
                                var jm = string.format("{\"nextion\":\"%s\"}",str(msg[0..-4]))
                                tasmota.publish_result(jm, "RESULT")        
                            end
                        end       
                    end
                end
            end
        end
    end      

    def begin_nextion_flash()
        self.flash_written = 0
        self.awaiting_offset = 0
        self.flash_offset = 0
        self.sendnx('DRAKJHSUYDGBNCJHGJKSHBDN')
        self.sendnx('recmod=0')
        self.sendnx('recmod=0')
        self.flash_mode = 1
        self.sendnx("connect")        
    end
    
    def set_power()
      var ps = tasmota.get_power()
      for i:0..1
        if ps[i] == true
          ps[i] = "1"
        else 
          ps[i] = "0"
        end
      end
      var json_payload = '{ "switches": { "switch1": ' + ps[0] + ' , "switch2": ' + ps[1] +  ' } }'
      log('NXP: Switch state updated with ' + json_payload)
    #   self.send(json_payload)
    end

    def set_clock()
      var now = tasmota.rtc()
      var time_raw = now['local']
      var nsp_time = tasmota.time_dump(time_raw)
      var time_payload = '{ "clock": { "date":' + str(nsp_time['day']) + ',"month":' + str(nsp_time['month']) + ',"year":' + str(nsp_time['year']) + ',"weekday":' + str(nsp_time['weekday']) + ',"hour":' + str(nsp_time['hour']) + ',"min":' + str(nsp_time['min']) + ' } }'
      log('NXP: Time and date synced with ' + time_payload, 3)
      self.send(time_payload)
    end

    def open_url(url)
        import string
        var host
        var port
        var s1 = string.split(url,7)[1]
        var i = string.find(s1,":")
        var sa
        if i<0
            port = 80
            i = string.find(s1,"/")
            sa = string.split(s1,i)
            host = sa[0]
        else
            sa = string.split(s1,i)
            host = sa[0]
            s1 = string.split(sa[1],1)[1]
            i = string.find(s1,"/")
            sa = string.split(s1,i)
            port = int(sa[0])
        end
        var get = sa[1]
        log(string.format("FLH: host: %s, port: %s, get: %s",host,port,get))
        self.tcp = tcpclient()
        self.tcp.connect(host,port)
        log("FLH: Connected:"+str(self.tcp.connected()),3)
        var get_req = "GET "+get+" HTTP/1.0\r\n"
	get_req += string.format("HOST: %s:%s\r\n\r\n",host,port)
        self.tcp.write(get_req)
        var a = self.tcp.available()
        i = 1
        while a==0 && i<5
          tasmota.delay(100*i)
          tasmota.yield() 
          i += 1
          log("FLH: Retry "+str(i),3)
          a = self.tcp.available()
        end
        if a==0
            log("FLH: Nothing available to read!",3)
            return
        end
        var b = self.tcp.readbytes()
        i = 0
        var end_headers = false;
        var headers
        while i<size(b) && headers==nil
            if b[i..(i+3)]==bytes().fromstring("\r\n\r\n") 
                headers = b[0..(i+3)].asstring()
                self.flash_buff = b[(i+4)..]
            else
                i += 1
            end
        end
        #print(headers)
		# check http respose for code 200
		var tag = "200 OK"
        i = string.find(headers,tag)
        if (i>0) 
            log("FLH: HTTP Respose is 200 OK",3)
		else
            log("FLH: HTTP Respose is not 200 OK",3)
			print(headers)
			return
        end
		# check http respose for content-length
        tag = "Content-Length: "
        i = string.find(headers,tag)
        if (i>0) 
            var i2 = string.find(headers,"\r\n",i)
            var s = headers[i+size(tag)..i2-1]
            self.flash_size=int(s)
        end
        if self.flash_size==0
            log("FLH: No size header, counting ...",3)
            self.flash_size = size(self.flash_buff)
            #print("counting start ...")
            while self.tcp.connected()
                while self.tcp.available()>0
                    self.flash_size += size(self.tcp.readbytes())
                end
                tasmota.delay(50)
            end
            #print("counting end ...",self.flash_size)
            self.tcp.close()
            self.open_url(url)
        else
            log("FLH: Size found in header, skip count",3)
        end
        log("FLH: Flash file size: "+str(self.flash_size),3)

    end

    def flash_nextion(url)

        self.flash_size = 0
        self.open_url(url)
        self.begin_nextion_flash()

    end

    def version_number(sval)
        import string
        var i1 = string.find(sval,".",0)
        var i2 = string.find(sval,".",i1+1)
        var num = int(sval[0..i1-1])*10000+int(sval[i1+1..i2-1])*100+int(sval[i2+1..])
        return num
    end

    def auto_update()
        log("NXP: Triggering update check")
        self.auto_update_flag = 1
        var json = '{"config": ""}'
        self.send(json)

    end

    def init()
        log("NXP: Initializing Driver")
        self.ser = serial(17, 16, 115200, serial.SERIAL_8N1)
        self.sendnx('DRAKJHSUYDGBNCJHGJKSHBDN')
        self.sendnx('rest')
        self.flash_mode = 0
        #self.fast_loop_turn_off = false
    end

    def install()
        self.flash_nextion("http://proto.systems/nxpanel/nxpanel-latest.tft")
    end
end

var nextion = Nextion()

tasmota.add_driver(nextion)

def install_dozator_hmi()    
    tasmota.cmd("Backlog flashnextion "+ tft_flash_url)
end

def install_dozator()
    tasmota.set_timer(50,/->install_dozator_hmi())
    tasmota.resp_cmnd_done() 
end

def flash_nextion(cmd, idx, payload, payload_json)
    def task()
        nextion.flash_nextion(payload)
    end
    tasmota.set_timer(0,task)
    tasmota.resp_cmnd_done()
end

def send_cmd(cmd, idx, payload, payload_json)
    nextion.sendnx(payload)
    tasmota.resp_cmnd_done()
end

def send_cmd2(cmd, idx, payload, payload_json)
    nextion.send(payload)
    tasmota.resp_cmnd_done()
end

def install_nxpanel()
    tasmota.set_timer(50,/->nextion.install())
    tasmota.resp_cmnd_done()
end

def setdim(cmd, idx, payload, payload_json)
    nextion.sendnx('dim='+ str(payload))
    tasmota.resp_cmnd_done()
end

tasmota.add_cmd('setdim', setdim)

tasmota.add_cmd('Nextion', send_cmd)
tasmota.add_cmd('Screen', send_cmd2)
tasmota.add_cmd('NxPanel', send_cmd2)
tasmota.add_cmd('FlashNextion', flash_nextion)
tasmota.add_cmd('InstallNxPanel', install_nxpanel)
tasmota.add_cmd('reflash', install_dozator)
tasmota.add_cmd('d_reflash_display',install_dozator)

tasmota.cmd("Rule3 1") # needed until Berry bug fixed
tasmota.cmd("State")
 