Page 2 of 2

Re: Calculate daily energy cost

Posted: Thursday 24 October 2019 22:13
by Piacco
Hello,

I'am want to calculate the total daily energycost.

But i'am struggling with the P1 data, i can read the total daily used energy with the following code

Code: Select all

local todayKwh= dz.devices('Power').counterToday  -- Used energy today 
But how can i read the counter Return today the code below is not working

Code: Select all

local ReturntodayKwh= dz.devices('Power').counterTodayReturn  -- Delivered energy today

Re: Calculate daily energy cost

Posted: Thursday 24 October 2019 22:45
by waaren
Piacco wrote: Thursday 24 October 2019 22:13 How can i read the counter Return today ?
If you look at the wiki P1 subject you will find the correct attribute.
Try

Code: Select all

local ReturntodayKwh= dz.devices('Power').counterDeliveredToday

Re: Calculate daily energy cost

Posted: Sunday 27 October 2019 17:25
by Piacco
Thanks @waaren, next time i will have a better look in the wiki :roll:

Re: Calculate daily energy cost

Posted: Friday 05 June 2020 13:27
by plugge
waaren wrote: Saturday 20 April 2019 0:37
EdwinK wrote: Wednesday 17 April 2019 10:37 Working for me too ;)

Just something else.. I have two tariffs for electricity, (High and low (11pm-07am). How can I add those, and I guess that the other settings are for transportation costs.
Working with tariffs and returning energy to the grid (solar panels) need a different approach.
Can you try below script ?

Code: Select all

--[[
            put today's electricity and / or today's Gas usage costs in custom virtual sensor
            Collect information from a P1 device and /or a Gas device
            electricity takes different tariffs and returns (from solarpanels or the likes) into account.
            
]]--

local scriptVar = "dailyEnergyCost"

return  {
            on = { timer = { "every 6 minutes" },           -- using 6 minutes because Stroom and Gas are updated very frequently
                   httpResponses = { scriptVar .. "*" },
        },

        logging = { level   =   domoticz.LOG_DEBUG,    -- change to LOG_ERROR when script executes OK
                    marker  =   sciptVar},

        data =  { energyCosts = { initial = {} }
                },

    execute = function(dz, item)
        --  ********************************************************************* Your changes below this line *************
        -- input Devices
        local electricity = dz.devices("Power") -- P1 device or comment this line
        local gas = dz.devices("Gas") -- Gas meter or comment this line
        
        -- outPut Devices
        local electricityCost = dz.devices("electricityCost today") -- define this virtual sensor as Custom sensor or comment this line when not used
        local gasCost = dz.devices("gasCost today")   -- define this virtual sensor as Custom sensor or comment this line when not used
        
        -- fixed Transport + contract costs per month in Euro's
        local electricityFixedMonth = 6.31 
        local gasFixedMonth = 6.31 
        --      ********************************************************************** No changes below this line **************

        local function getDaysForMonth(month, year)       -- Returns number of days in given or current month
            if month == nil then month = dz.time.month end
            if year == nil then year = dz.time.year end
            return os.date('*t',os.time{year=year,month=month+1,day=0}).day
        end

        local function triggerJSON(url, response, delay)
            local delay = delay or 0
            dz.openURL({    url = url,
                            method = "GET",
                            callback = response}).afterSec(delay)
        end
        
        local function getCosts(id) -- these costs should be set in domoticz settings
            if next(dz.data.energyCosts) == nil  or dz.data.energyCosts.creationTime < ( dz.time.dDate - dz.time.secondsSinceMidnight ) then
                local costURL = dz.settings['Domoticz url'] .. "/json.htm?param=getcosts&type=command&idx=" .. id
                triggerJSON(costURL, scriptVar .. "_cost")
            end
            return ( next(dz.data.energyCosts) ~= nil )
        end
        
        local function makeCostTable(t) -- (re)Create costTable if not existing yet; Refreshed at least once a day
            if next( dz.data.energyCosts ) == nil or dz.data.energyCosts.creationTime < ( dz.time.dDate - dz.time.secondsSinceMidnight ) then
                dz.data.energyCosts = t
                dz.data.energyCosts.electricityFixedDay  = (electricityFixedMonth or 0 ) / getDaysForMonth()
                dz.data.energyCosts.gasFixedDay  = (gasFixedMonth or 0 ) / getDaysForMonth()
                dz.data.energyCosts.creationTime = dz.time.dDate
                dz.data.energyCosts.humanReadableCreationTime = dz.time.raw
            end
        end
        
        local function updateCustomSensor(device, value) 
            local currentValue = device.rawData[1]
            if value ~= tonumber(currentValue) then              -- Update only needed when new value is different fom previous one 
                dz.log(device.name .. " ==>> previous value: " .. currentValue .. " ; new value " .. value,dz.LOG_DEBUG)
                device.updateCustomSensor(value) 
            end
        end
        
        local function getEnergy(id)
            local energyURL = dz.settings['Domoticz url'] .. "/json.htm?range=month&sensor=counter&type=graph" ..
                                "&actmonth=" .. dz.time.month ..
                                "&idx=" .. id
                triggerJSON(energyURL, scriptVar .. "_energy")
        end
        
        local function makeTodaysGasCosts()
            local gasTodayCost
            if gasCost then
                gasTodaysCost = dz.data.energyCosts.gasFixedDay * 10000
                gasTodaysCost = gasTodaysCost + gas.counterToday * dz.data.energyCosts.CostGas
            end
           return dz.utils.round(gasTodaysCost / 10000, 2)
        end
        
        local function makeTodaysElectricityCosts(t)
            local today
            for i, record in ipairs(t) do
                if record.d == dz.time.rawDate then
                    today = i
                end
            end
            
            local electricityTodaysCost
            if electricityCost then 
                electricityTodaysCost = dz.data.energyCosts.electricityFixedDay * 10000
                if today then 
                    electricityTodaysCost = electricityTodaysCost + t[today].v * dz.data.energyCosts.CostEnergy
                    electricityTodaysCost = electricityTodaysCost + t[today].v2 * dz.data.energyCosts.CostEnergyT2
                    electricityTodaysCost = electricityTodaysCost - t[today].r1 * dz.data.energyCosts.CostEnergyR1
                    electricityTodaysCost = electricityTodaysCost - t[today].r2 * dz.data.energyCosts.CostEnergyR2
                end
            end
           
           return dz.utils.round(electricityTodaysCost / 10000,2) 
        end
        
        local function updateCustomSensor(device, value)
            if device == nil or value == nil then
                return 
            end
            local currentValue = device.rawData[1]
            if value ~= tonumber(currentValue) then              -- Update only needed when new value is different fom previous one 
                dz.log(device.name .. " ==>> previous value: " .. currentValue .. " ; new value " .. value,dz.LOG_DEBUG)
                device.updateCustomSensor(value) 
            end
        end
        
        if not ( item.isHTTPResponse ) then
            if not ( getCosts(electricity.id) ) then 
                -- logWrite("No or outdated costs. Next time the costs will be there")
                return 
            end 
            -- logWrite("-- costs are there; get energy data")
            getEnergy(electricity.id)
        elseif item.trigger == ( scriptVar .. "_energy" ) then
            updateCustomSensor( electricityCost, makeTodaysElectricityCosts(item.json.result))
            updateCustomSensor( gasCost, makeTodaysGasCosts())
        else
            makeCostTable(item.json)
        end
    end
}
@waaren. Thank you for this script, it's works fine for me. I am new to Domoticz, still learning.
I have added a user variable to gather the accumulated costs for gas and electricity for each day during a contract period (i.e., a year).
However, I could not find the moment in the script where the final daily costs are ready (to be intercepted by me, to be added to my user variable) and a new counter for the day starts. I know that it is around midnight and involves the fixed costs for the day. But where in your script could I intercept the final daily costs? (If I setup a test based on a guess, I'll have to wait a day to see if it works. :-/ )
Thank you for any help!

Re: Calculate daily energy cost

Posted: Friday 05 June 2020 17:19
by waaren
plugge wrote: Friday 05 June 2020 13:27Where in your script could I intercept the final daily costs?
If you retrieve the values from the energyCost and gasCost devices just below line 135

Code: Select all

makeCostTable(item.json)
You should find the final daily costs. This part of the script will execute shortly after midnight so the costs are from yesterday

Re: Calculate daily energy cost

Posted: Saturday 06 June 2020 2:12
by plugge
Thank you waaren!

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 15:13
by plugge
waaren wrote: Tuesday 16 April 2019 21:04 the method text is not available for device type custom sensor. You need another function to do that. Try this:

Code: Select all

return {
    on = { timer   = { "every 6 minutes" }},                 -- using 6 minutes because Stroom and Gas are updated very frequently

      logging =   {   level   =   domoticz.LOG_DEBUG,    -- Remove -- on these two lines if you want debug logging
                      marker  =   "EnergyCost" },    

    execute = function(dz)
        -- Devices
        local todayKwh      = dz.devices("Power").counterToday
        local todayM3       = dz.devices("Gas").counterToday
        local energyCost    = dz.devices("energyCost today") -- define this virtual sensor as Custom sensor
        local gasCost       = dz.devices("gasCost today")   -- define this virtual sensor as Custom sensor
        
        -- Price in Euro's / Kwh - M3
        local kwhPrice      = 0.20
        local gasM3Price    = 0.62
        local kwHFixedDay   = 6.99 / 31
        local m3FixedDay    = 6.99 / 31

        -- calculations
        local kwhValue  = dz.utils.round( (kwhPrice * todayKwh + kwHFixedDay),2)   -- rounded to two decimals
        local m3Value   = dz.utils.round( (gasM3Price * todayM3 + m3FixedDay),2)
        
        local function updateCustomSensor(device, value)
            local currentValue = device.rawData[1]
            if value ~= tonumber(currentValue) then              -- Update only needed when new value is different fom previous one 
                dz.log(device.name .. " ==>> previous value: " .. currentValue .. " ; new value " .. value,dz.LOG_DEBUG)
                device.updateCustomSensor(value) 
            end
        end
        
        -- call function
        updateCustomSensor(energyCost, kwhValue) 
        updateCustomSensor(gasCost, m3Value) 
        
    end
}
Hi,
In the above code, the following function - updateCustomSensor(device, value) - turns out to be always true, even when currentValue and value are equal:

Code: Select all

        local function updateCustomSensor(device, value)
            if device == nil or value == nil then
                return 
            end
            local currentValue = device.rawData[1]
            if value ~= tonumber(currentValue) then              -- Update only needed when new value is different fom previous one 
                dz.log(device.name .. " ==>> previous value: " .. currentValue .. " ; new value " .. value,dz.LOG_DEBUG)
                device.updateCustomSensor(value) 
            end
        end
See my log snippet:

Code: Select all

2020-06-26 15:00:00.462 Status: dzVents: Info: ------ Start internal script: DailyEnergyCosts: HTTPResponse: "dailyEnergyCosts_energy"
2020-06-26 15:00:00.488 Status: dzVents: Info: Power costs today ==>> previous value: -1.01 ; new value -1.01
2020-06-26 15:00:00.488 Status: dzVents: Info: Gas costs today ==>> previous value: 1.04 ; new value 1.04
2020-06-26 15:00:00.488 Status: dzVents: Info: Energy costs today ==>> previous value: 0.03 ; new value 0.03
2020-06-26 15:00:00.490 Status: dzVents: Info: ------ Finished DailyEnergyCosts
This should never enter the log since both values are the same.
Any idea what could be wrong?
TIA

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 16:20
by waaren
plugge wrote: Friday 26 June 2020 15:13 ..... This should never enter the log since both values are the same.
Any idea what could be wrong?
TIA
Maybe value is not a numbertype in

Code: Select all

if value ~= tonumber(currentValue) then 
Try changing this in

Code: Select all

if tonumber(value) ~= tonumber(currentValue) then 

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 16:49
by plugge
waaren wrote: Friday 26 June 2020 16:20
plugge wrote: Friday 26 June 2020 15:13 ..... This should never enter the log since both values are the same.
Any idea what could be wrong?
TIA
Maybe value is not a numbertype in

Code: Select all

if value ~= tonumber(currentValue) then 
Try changing this in

Code: Select all

if tonumber(value) ~= tonumber(currentValue) then 
Hi waaren, that was my first thought, but that didn't make any difference. The rule keeps firing with equal tonumber() values, but shouldn't:

Code: Select all

2020-06-26 16:20:00.086 Status: EventSystem: Script event triggered: D:\Program Files (x86)\Domoticz\dzVents\runtime\dzVents.lua
2020-06-26 16:20:00.200 Status: dzVents: Info: ------ Start internal script: DailyEnergyCosts: HTTPResponse: "dailyEnergyCosts_energy"
2020-06-26 16:20:00.226 Status: dzVents: Info: Energy costs today ==>> previous value: -0.18 ; new value -0.18
2020-06-26 16:20:00.227 Status: dzVents: Info: ------ Finished DailyEnergyCosts

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 17:18
by plugge
Maybe lua doesn't like the following function call?

Code: Select all

updateCustomSensor( energyCost, gascosts + powercosts)
Changed it by moving the addition to a local

Code: Select all

local sum = tonumber(gascosts) + tonumber(powercosts)
updateCustomSensor( energyCost, sum)
 
That seems to do the trick.
Still puzzled about the behavior though.

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 17:43
by plugge
plugge wrote: Friday 26 June 2020 17:18 That seems to do the trick.
Still puzzled about the behavior though.
No, it doesn't. The rule still fires with (seemingly) equal values:

Code: Select all

2020-06-26 17:30:00.673 Status: dzVents: Info: Energy costs today ==>> previous value: -0.38 ; new value -0.38

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 18:34
by waaren
plugge wrote: Friday 26 June 2020 17:43 No, it doesn't. The rule still fires with (seemingly) equal values:
what about ?

Code: Select all

if dz.utils.round(tonumber(value),2) ~= dz.utils.round(tonumber(currentValue),2) then 

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 19:15
by plugge
Nope.

Code: Select all

2020-06-26 18:45:00.230 Status: dzVents: Info: Energy costs today ==>> previous value: -0.42 ; new value -0.43
2020-06-26 18:50:00.541 Status: dzVents: Info: Energy costs today ==>> previous value: -0.43 ; new value -0.43
2020-06-26 18:55:00.477 Status: dzVents: Info: Energy costs today ==>> previous value: -0.43 ; new value -0.43
2020-06-26 19:00:01.204 Status: dzVents: Info: Energy costs today ==>> previous value: -0.43 ; new value -0.41
Interestingly it only occurs with the custom sensor that I added. Same type as the custom gas and power sensors. But apparently something is different with the value stored.
Now trying to see what happens when:

Code: Select all

local currentValue = dz.utils.round(tonumber(device.rawData[1],2)

Re: Calculate daily energy cost

Posted: Friday 26 June 2020 19:25
by plugge
plugge wrote: Friday 26 June 2020 19:15 Now trying to see what happens when:

Code: Select all

local currentValue = dz.utils.round(tonumber(device.rawData[1],2)
That did the trick!
No more equal value entries in the log, so the rule works.
Apparently the print() in the log truncates values?
Thanx