Delay-wait in script

Easy to use, 100% Lua-based event scripting framework.

Moderator: leecollings

Post Reply
Piacco
Posts: 81
Joined: Friday 14 November 2014 9:33
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Delay-wait in script

Post by Piacco »

Hello,

A question about delay/wait in a dzvents script is this possible?

The reasons wy I need is that when domoticz sends a notification to my phone they are not always received in the right order.
So I was thinking to build an delay in the script to solve this.

Code: Select all

   dz.notify("Domoticz", "De zonnepanelen opbrengst voor vandaag = " .. TotaalDezeDagOpgewekt .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , "telegram")
        wait for 2 seconds
        dz.notify("Domoticz", "Waarvan teruggeleverd = " .. TotaalDezeDagTeruggeleverd .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , "telegram")
        wait for 2 seconds
        dz.notify("Domoticz", "Waarvan verbruikt = " .. Zonnestroomverbruikt .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , "telegram")

Code: Select all

return 
{
    on      =   
	{  

                timer = { 'at 21:15' },
			--	timer = { 'every minute' },
	},
     
    
   logging = 
    {
        level = domoticz.LOG_DEBUG,
        marker = "Dagopbrengst zonnepanelen"
    },
                
    execute = function(dz)
        -- ****************************** Your settings below this line ***************************************************
        usageDevice = dz.devices(1024).WhToday                    -- Replace xxxx with ID of energyDevice you want to track
        deliveryDevice = dz.devices(201).counterDeliveredToday    -- Replace xxxx with ID of energyDevice you want to track
        
        local Zonnestroomverbruikt = dz.utils.round(((usageDevice/1000)- deliveryDevice),2)
        local TotaalDezeDagOpgewekt = dz.utils.round((usageDevice/1000),2)
        local TotaalDezeDagTeruggeleverd = dz.utils.round((deliveryDevice),2)
    
        dz.notify("Domoticz", "De zonnepanelen opbrengst voor vandaag = " .. TotaalDezeDagOpgewekt .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , "telegram")
        dz.notify("Domoticz", "Waarvan teruggeleverd = " .. TotaalDezeDagTeruggeleverd .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , "telegram")
        dz.notify("Domoticz", "Waarvan verbruikt = " .. Zonnestroomverbruikt .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , "telegram")
        --***********************************************************************************************************
 
    end
}
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Delay-wait in script

Post by waaren »

Piacco wrote: Wednesday 22 April 2020 13:27 A question about delay/wait in a dzvents script is this possible?
to build in a delay in a domoticz Event script (dzVents, Lua or any other) is not the way to handle this.
The domoticz event system is single threaded meaning that during the execution of a script no other eventScripts can be processed. Effectively you will block the entire event system.

One way of solving your issue is by using the customEvents script trigger like it is implemented below.

Code: Select all

local myCustomEvent = 'delayedNotification'

return 
{
    on =
    {
        timer = 
        { 
            'at 21:15',
            -- 'every minute',
        },
        customEvents =
        {
            myCustomEvent,
        },
    },
    
   logging = 
    {
        level = domoticz.LOG_DEBUG,
        marker = "Dagopbrengst zonnepanelen"
    },

    execute = function(dz, item)
        -- ****************************** Your settings below this line ***************************************************
        usageDevice = dz.devices(1024).WhToday                    -- Replace xxxx with ID of energyDevice you want to track
        deliveryDevice = dz.devices(201).counterDeliveredToday    -- Replace xxxx with ID of energyDevice you want to track
        --***********************************************************************************************************
        
        if item.isTimer then
            local Zonnestroomverbruikt = dz.utils.round(((usageDevice/1000)- deliveryDevice),2)
            local TotaalDezeDagOpgewekt = dz.utils.round((usageDevice/1000),2)
            local TotaalDezeDagTeruggeleverd = dz.utils.round((deliveryDevice),2)
            dz.notify("Domoticz", "De zonnepanelen opbrengst voor vandaag = " .. TotaalDezeDagOpgewekt .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , dz.NSS_TELEGRAM)
            dz.emitEvent(myCustomEvent,'notification2').afterSec(2)
        elseif item.data == 'notification2' then
            dz.notify("Domoticz", "Waarvan teruggeleverd = " .. TotaalDezeDagTeruggeleverd .. , dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , dz.NSS_TELEGRAM)
            dz.emitEvent(myCustomEvent,'notification3').afterSec(2)
        elseif item.data == 'notification3' then
            dz.notify("Domoticz", "Waarvan verbruikt = " .. Zonnestroomverbruikt .. "kWh", dz.PRIORITY_NORMAL,dz.SOUND_DEFAULT, "" , dz.NSS_TELEGRAM)
        end
    end
}
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
HvdW
Posts: 664
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Delay-wait in script

Post by HvdW »

This is an old post. I couldn't find the answer elsewhere.

I want 2 dzvents scripts to execute every 12 minutes.
Plus I want the second script to be able to overrule the decision made by the first script. (make another decision based on other input)
I was thinking about a 2 second delay in the execution of the second script.
So the second script executes at 12 min + 2 sec, 24 min + 2 sec, 36 min + 2 sec and so on.
How can that be done?
Bugs bug me.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Delay-wait in script

Post by rrozema »

You would need some 'storage' 'shared' by the both scripts for them to communicate their results. For example either a dummy switch/selector or a user variable or a dzvents global data can all do fine for the purpose, which one fits best depends mostly on other criteria you may have.

The timing of the scripts should be less important if they both run regularly and each sets that shared 'variable', then takes into account the value of that shared variable in deciding what to do. Maybe you should make your example a bit more specific so we can give more a specific answer. I personally don't very much like processes where they have to be run in some particular order: most of the times there are more elegant and more reliable solutions.
HvdW
Posts: 664
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Delay-wait in script

Post by HvdW »

The high gas prices made us decide to retire in the kitchen during the day and leave the living room at a low temperature.
During the day I want the living room to be at a low temperature.
I wrote a script to read room temperature and switch the heater for normal operation. (the old state)

Now we are in a situation that the kitchen needs to be heated.
This can be accomplished simply by firing up the heating for a short time.
If the heating fires during the day every 12 minutes during 3 minutes the kitchen will be warm.
During the day the script for the living room switches the heater off every 12 minutes.
I want to overrule this with another script every 12 minutes that switches the heating on during 3 minutes as long as the desired kitchen temperature is not reached.
So the second script has to start 2 seconds or so later.
Bugs bug me.
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Delay-wait in script

Post by EddyG »

I think this could also be done without a variable.
In @waaren script the is a emitEvent

Code: Select all

dz.emitEvent(myCustomEvent,'notification3').afterSec(2)
In the second parameter of emitEvent you could put almost everything you want, like an array, so multiple parameters.

B.t.w. There is a plugin https://www.domoticz.com/wiki/Plugins/S ... ostat.html
Perhaps you can do the same with 2 smart virtual thermostats?
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Delay-wait in script

Post by rrozema »

As I said, having 2 scripts that need to be run in order is not a very reliable way of automating. Apart from that are the timers in Domoticz only per minute; there are ways around that, but it gets complicated quickly if you want to go this way, plus it will prove often not to work correctly.

As an alternative I suggest you create a dummy selector switch called 'heating' and has 4 values: 0 - Off, 10 - Living, 20 - Kitchen and 30 - All. Then adapt your scripts to check the value of that selector to see what needs to be done by each script: Off = neither does anything, Kitchen = only the kitchen needs to do it's job, Living = only the living needs to do it's job and finally All = both do their job.

If i guess correctly, it's the same heater/boiler for both rooms, but you're firing it up for 3 minutes every 12 minutes when you only need the kitchen heated, correct? And if the living needs to be heated, you let it run on the thermostat that is in the living room? How are you switching the heater on and off? I mean, you're not switching the entire heating on and off with a 220V switch are you? If you do that, you could break your heater, plus it may not be very cost effective at all (modern heaters do their own calculations internally to burn as efficient as possible, if you switch it off completely it's reset every time and the water cools down more than it would with the heater left on). Assuming my guesses are correct, a better way would be to replace the thermostat by a simple relays so you can have the heater switch on and off by Domoticz. A good way to do this is using the svt plug in (= Smart Virtual Thermostat): you wouldn't even need to use a script anymore.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Delay-wait in script

Post by rrozema »

EddyG wrote: Thursday 06 January 2022 14:47 I think this could also be done without a variable.
In @waaren script the is a emitEvent

Code: Select all

dz.emitEvent(myCustomEvent,'notification3').afterSec(2)
In the second parameter of emitEvent you could put almost everything you want, like an array, so multiple parameters.

B.t.w. There is a plugin https://www.domoticz.com/wiki/Plugins/S ... ostat.html
Perhaps you can do the same with 2 smart virtual thermostats?
Correct, the emitEvent can be used to trigger a next event after 2 seconds, that's what I mean when I said it is possible. But for example, there's no guarantee that the custom event will fire exactly 2 seconds later, it could be later because other software has a higher priority and needs to be run first. Plus also, it's not very easy to properly use the custom events correctly, especially when multiple scripts are involved. That's why I think OP needs to rethink what exactly he needs, instead of working from what he's got.
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Delay-wait in script

Post by EddyG »

I don't think @HvdW means exactly 2 seconds (on the micro second)
I have a script using every 3 seconds an emitEvent to turn bedlights on every morning starting at 0% up to 80% with stepsize of 3
I must say it works pretty good, it has a steady and good pace to turn on the lights.
HvdW
Posts: 664
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Delay-wait in script

Post by HvdW »

I think I made a fail safe construct here.

Code: Select all

-- CV - gewone versie

-- assumptions:
-- the setpoint is set by a dummy device (not a selector type) 

local CV_SWITCH = 'CV auto' -- switch device
local TEMP_SETPOINT = 'Thermostaat' -- selector dummy device
local TEMP_SENSOR_1 = 'TFA1'
local TEMP_SENSOR_2 = 'TFA2'
local BAT_THRESHOLD = 30
local LOGGING = true
local keuken_SWITCH = 'Keuken warm houden'
local setpoint_keuken = 22

return
{
    on = 
    {
        timer = 
        {
            'every 12 minutes'
        },
    },

    --LOG levell: This is the log level you want for this script. Can be domoticz.LOG_INFO, domoticz.LOG_MODULE_EXEC_INFO, domoticz.LOG_DEBUG or domoticz.LOG_ERROR
    --marker: A string that is prefixed before each log message. That way you can easily create a filter in the Domoticz log to see just these messages.

    logging =   
    {
        level = LOGGING and domoticz.LOG_DEBUG or domoticz.LOG_ERROR,
        --level = LOGGING and domoticz.LOG_ERROR,
        marker = 'dzVents heating',
    },

    execute = function(dz)

        -- collect all input data
        local boiler_switch_state = dz.devices(CV_SWITCH).state           
        local keuken_switch_state = dz.devices(keuken_SWITCH).state
        local temp_1 = dz.devices(TEMP_SENSOR_1).temperature  
        local temp_2 = dz.devices(TEMP_SENSOR_2).temperature
        local bat_1 = dz.devices(TEMP_SENSOR_1).batteryLevel
        local bat_2 = dz.devices(TEMP_SENSOR_2).batteryLevel
        local setpointValue = dz.devices(TEMP_SETPOINT).setPoint

        local average_temp = 50  -- very high so the heating will not fire
        local temp_diff = temp_1 - temp_2

        -- info only on log level = LOG_DEBUG
        dz.log('CV_SWITCH      : ' .. boiler_switch_state, dz.LOG_DEBUG)
        dz.log('keuken_SWITCH  : ' .. keuken_switch_state, dz.LOG_DEBUG)
        dz.log('Temp_1         : ' .. temp_1, dz.LOG_DEBUG)
        dz.log('Temp_2         : ' .. temp_2, dz.LOG_DEBUG)
        dz.log('Bat_1          : ' .. bat_1, dz.LOG_DEBUG)
        dz.log('Bat_2          : ' .. bat_2, dz.LOG_DEBUG)
        dz.log('Setpoint_value : ' .. setpointValue, dz.LOG_DEBUG)
        
        -- test if conditions are met and set acting values
        if ((temp_1 - temp_2 > 3.1) or (temp_2 - temp_1 > 3.1)) then
            dz.log('Temperature difference between sensors is more than 1.1 degrees : ' .. temp_diff, dz.LOG_DEBUG)        
            -- dz.notify('Notifyer', 'Temperature difference between sensors is more than 1.1 degrees' .. temp_diff , dz.PRIORITY_HIGH)
        elseif (bat_1 < BAT_THRESHOLD) then
            dz.log('Battery level of temperature sensor 1 = ' .. bat_1, dz.LOG_DEBUG)        
            dz.notify('Notifyer', 'Temperature sensor 1 ' .. bat_1 .. 'battery level low.',dz.PRIORITY_HIGH)
        elseif (bat_2 < BAT_THRESHOLD) then
            dz.log('Battery level of temperature sensor ' .. bat_2, dz.LOG_DEBUG)        
            dz.notify('Notifyer', 'Temperature sensor 2 ' .. bat_2 .. 'battery level low.',dz.PRIORITY_HIGH)
        else 
            average_temp = (temp_1 + temp_1)/2
            dz.log('Average temperature sensors value is : ' .. average_temp, dz.LOG_DEBUG)
        end

        -- now determine what to do
        if (average_temp >= setpointValue) then
            dz.devices(CV_SWITCH).switchOff()
            dz.log('Target temperature reached, boiler off')
        elseif (average_temp <= (setpointValue -1))  then
            dz.log('Average temperature more than 1 degree below setpointValue, switchOn during 9 minutes')
            dz.devices(CV_SWITCH).switchOn().forMin(9)
        elseif ((average_temp > (setpointValue - 1)) and (average_temp < (setpointValue - 0.5))) then
            dz.log('Average temperature less than 1 degree below setpointValue, switchOn during 6 minutes')
            dz.devices(CV_SWITCH).switchOn().forMin(6)
        elseif ((average_temp > (setpointValue -0.5)) and (average_temp < setpointValue)) then
            dz.log('Average temperature less than 0.5 degree below setpointValue, switchOn during 4 minutes')
            dz.devices(CV_SWITCH).switchOn().forMin(4)
        else  -- not predicted situation
            dz.log('Unpredicted situation, boiler off')
            dz.devices(CV_SWITCH).switchOff()            
        end
        -- if ((temp_2 <= setpoint_keuken) and (setpointValue <= 19) and (setpointValue > 18))  then
        --    dz.log('Keukentemp hoog houden, switchOn during 3 minutes')
        --    dz.devices(CV_SWITCH).switchOn().forMin(4)
        -- end
        if ((temp_2 <= setpoint_keuken) and (dz.devices(keuken_SWITCH).state == 'On'))  then
            dz.log('Temperatuur keuken = ' .. temp_2, dz.LOG_DEBUG)
            dz.log('Keuken setpoint    = ' .. setpoint_keuken, dz.LOG_DEBUG)
            dz.log('Keuken_SWITCH      = ' .. keuken_switch_state, dz.LOG_DEBUG)
            dz.log('SwitchOn during 4 minutes')
            dz.devices(CV_SWITCH).switchOn().forMin(4)
        end
    end
}

-- einde CV - gewone versie
Bugs bug me.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Delay-wait in script

Post by rrozema »

I think mostly that should work. I do have 2 suggestions though to make it more robust. The first is on sensors timing out: if a sensor doesn't send any updates for a while (for example because the network is down, or the battery is depleted), Domoticz will still report the last value that was received from this sensor. The device in Domoticz is shown with a red title bar though in this situation. Since your script can't see that red title bar, it will still use that timed out sensor's value when calculating the average temperature. You can check the sensor devices' .timedOut property to see if you should include it. If .timedOut is true, you better ignore that sensor's value and rely on the working sensor only. But if both sensors .timedOut property is true, the average is not going to change at all anymore, no matter how long you switch the heater on or off. If no sensor is 'alive' you should better stop the heater at all.

The 2nd suggestion is that I see you've done some nice work on trying not to overshoot the setpoint for the living room by reducing the heating period when the temperature is closer to the setpoint. Why do you only do this for the living room? You could easily do the same for the kitchen too by moving the check "if dz.devices(keuken_SWITCH).state == 'On'" up to before the line "-- now determine what to do". Based on this switch you can set the setpointValue and the average_temp value either for the living room + kitchen or the kitchen alone, then let the code below there do the same job for both the kitchen and the living room or just the kitchen. I don't think the behaviour is going to be exactly linear as you've coded it here, so you will most likely still have some overshoot, but it's a nice approximation and it will at least help reduce overshoot better than simply on-off all the period.
HvdW
Posts: 664
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Delay-wait in script

Post by HvdW »

Hi @rrozema,
Thank you for your comment.
I'll dive into this .timedOut property , a very usefull tip.

Your proposal makes me ask the next question.
The actor I'm using on the boiler/heater is an ESP8266 with the ESPEasy Mega software.
The ESP8266 is situated behind the former outside wall of the house receiving a WiFi signal of -67 dB.
I installed a script I stumbled upon on the ESP8266 to make it restart whenever the WiFi connection is lost.

Code: Select all

on System#Boot do
  Let,1,0                                // set user variable to initial state of 0         
endon

On Wifi#Disconnected do 
  if [VAR#1]=0
  timerSet,1,180
  Let,1,1                                // set user variable 1 to 1 to prevent re-set of var1 on each event.
  endif
endon

on Wifi#Connected do     	 	 // if wifi returns, cancel reboot
  timerSet,1,0                    	 // cancel timer
  Let,1,0                                // set user variable 1 to 0
endon

On Rules#Timer=1 do
  reboot
endon 
This isn't good enough because the Domoticz log tells me every now-and-then the connection is lost.

Code: Select all

 2022-01-08 13:36:00.694 Error: Error opening url: http://192.168.2.16/control?cmd=GPIO,4,0
2022-01-08 13:39:00.749 Error: Error opening url: http://192.168.2.16/control?cmd=GPIO,4,1 
So I installed System Alive Checker to check the ESP8266's WiFi presence.
This wasn't enough for me so I added a DHT11 temp sensor I had laying around to the ESP8266.
As long as I have temp I have connection.
Will the .timedOut check be sufficient to check?
Any suggestions?


Next, why I wrote this thermostat script myself.
I have been using the well known Smart Virtual Thermostat and noticed that it keeps the boiler burning for a long time and/or in short sequences.
It doesn't give the room the time to reflect the effect of the radiators heating the room.
So I was thinking the Smart Virtual Thermostat results in unnecessary firing the heater and thus spilling gas.
(on one of the last posts a saw a heating time of 20 minutes!)
My 1999 Nefit Ecomline HR doesn't have any kind of modulation to make it stop burning when the returning water is > 59 degrees Celcius, so it would keep on burning much too long.
That's why.
Bugs bug me.
HvdW
Posts: 664
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Delay-wait in script

Post by HvdW »

rrozema wrote: Thursday 06 January 2022 17:44 The 2nd suggestion is that I see you've done some nice work on trying not to overshoot the setpoint for the living room by reducing the heating period when the temperature is closer to the setpoint. Why do you only do this for the living room? You could easily do the same for the kitchen too by moving the check "if dz.devices(keuken_SWITCH).state == 'On'" up to before the line "-- now determine what to do". Based on this switch you can set the setpointValue and the average_temp value either for the living room + kitchen or the kitchen alone, then let the code below there do the same job for both the kitchen and the living room or just the kitchen. I don't think the behaviour is going to be exactly linear as you've coded it here, so you will most likely still have some overshoot, but it's a nice approximation and it will at least help reduce overshoot better than simply on-off all the period.
Haha, indeed.
You know, this is a new situation created by the high gas prices. I had thes TFA1 and TFA2 both in the living room and put TFA2 on a kitchen cupboard at 225 cm from the ground. That's why 22 degrees is set as setpoint_keuken and not 20.
It creates a nice temperature of around 20 degrees at the kitchen table.
I close 2 of the 4 radiators in the living room manually at 8 in the morning and open them at 7 PM.
So we have a reduced circulation in the house.
The switch keuken-SWITCH switches on at 8 AM and switches off at 7 PM. After that the normal temperature regulation for the living room takes over.

The radiator in the kitchen is nearest of all to the boiler. 4 minutes burning is more than enough to get that radiator warm. After some testing I set the burning time now to 3 minutes.
3 minutes heats up the kitchen radiator and 4 , 5 or 6 minutes will heat the 2 remaining room radiators as well.
These 2 don't need to be warm during the day.
So that's why there is a fixed heating time for operation during the day.

Last remark.
Overshoot is relative in two ways.
First.
The kitchen is well isolated, the living room isn't. We don't dare to isolate the walls because of humidity problems. The floor and ceiling are very well isolated. Overshoot in such a room is more or less impossible.
Second.
What is 0.3 degrees overshoot worth on 20 degrees. How precise are my sensors.
It is the comfortable climate in the room that counts.
In case of constant overshoot it's a question of setting the setpoint a bit lower.
Bugs bug me.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Delay-wait in script

Post by rrozema »

I'm guessing you're dutch; did you ever try adjusting the water flow in your radiators so that they all heat up at the same time and sufficient for the room they are in? Here's a page that describes this process and other measures you can take to reduce the amount of energy used for heating, while still maintaining comfort and maybe even improving it: https://www.zelfenergiebesparen.nl/gas- ... afstellen/

Isolating the walls could be a good way to avoid humidity problems. I suggest you have an expert come by to give you advice for your particular situation. I think most of the times this is free and the costs of isolating the walls doesn't have to be very expensive. Please note that the "kit" they mention in the article is mostly that ir-thermometer, which you can also get at aliexpress for less than 10 euros. Any thermometer will do because it doesn't need to be perfectly calibrated for this task: you're comparing the in with the out temperatures, so no need to know the exact temperatures.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest