This script collects data from SmartEVSE charging level and charging time and displays time and relative percentage for each charging level in a Text Sensor (Charging Levels)
Code: Select all
-- this script is for rendering insight in how much time PV powered charging is active
-- another script gathers these data at the end of each month to give a monthly oversight
-- time is calculated
-- relative percentages are calculated
-- at the end of the month data are cleared
-- at 23:56 daily data are collected and domoticz.data.solarkWhThisMonth updated
-- at 23:57 on the last day of each month the script Charging Time Monthly Summary Display
-- collects the data from the current month
-- at 23:58 on the last day of each month the data in this script are reset to zero to be able
-- to collect data for the next month in an empty table
return {
active = true,
on = {
devices = { 'Charging Level' }, -- Charging Level is the name of the selector switch
timer = {'at 23:56', 'at 23:58'}, -- { 'every minute' },
},
data = {
resetTrigger = { initial = 0 },
startTime = { initial = 0 },
totalTime = { initial = {0, 0, 0, 0, 0} },
lastState = { initial = 0 },
laadpaalkWhBeginThisMonth = { initial = 0 },
laadpaalkWhThisMonth = { initial = 0 },
solarkWhToday = { initial = 0 },
solarkWhThisMonth = { initial = 0 },
},
logging = {
-- Level can be domoticz.LOG_INFO, domoticz.LOG_MODULE_EXEC_INFO, domoticz.LOG_DEBUG, domoticz.LOG_ERROR or domoticz.LOG_FORCE
level = domoticz.LOG_DEBUG,
marker = '---- Charging Time Split Display -----',
},
execute = function(domoticz, item)
local function padRight(label, value, length)
local labelLength = #label
local valueStr = tostring(value)
local valueLength = #valueStr
local padding = length - (labelLength + valueLength)
return label .. string.rep('', padding) .. valueStr
end
local selectorSwitch = domoticz.devices('Charging Level')
local itemLevel = item and item.level or selectorSwitch.level
if not itemLevel then
domoticz.log('Error: item.level is nil', domoticz.LOG_ERROR)
return
end
-- Check if the reset trigger is activated
-- domoticz.data.resetTrigger = 1
-- if set to 1 all data will be reset to 0
-- uncomment to reset
-- the script ends after the reset.
-- comment domoticz.data.resetTrigger = 1 again to let the script execute all the way
domoticz.log('Time calculated with os.date({%H:%M})' .. os.date('%H:%M'), domoticz.LOG_DEBUG)
domoticz.log('Calculated last day of '..os.date('%B')..' : ' .. (tostring(os.date('*t', os.time{year=os.date('%Y'), month=os.date('%m')+1, day=0}).day)), domoticz.LOG_DEBUG)
-- and (os.date('%d') == tostring(os.date('*t', os.time{year=os.date('%Y'), month=os.date('%m')+1, day=0}).day))) then
if domoticz.data.resetTrigger == 1 or (os.date('%H:%M') == '23:58' and (os.date('%d') == tostring(os.date('*t', os.time{year=os.date('%Y'), month=os.date('%m')+1, day=0}).day))) then
domoticz.data.startTime = 0
domoticz.data.totalTime = {0, 0, 0, 0, 0, 0}
domoticz.data.lastState = 0
domoticz.data.laadpaalkWhBeginThisMonth = 0
domoticz.data.laadpaalkWhThisMonth = 0
domoticz.data.times = {}
domoticz.data.totalActiveTime = 0
domoticz.data.percentages = {}
domoticz.data.solarkWh = 0
domoticz.data.solarkWhToday = 0
domoticz.data.solarkWhThisMonth = 0
domoticz.data.laadpaalkWhBeginThisMonth = domoticz.devices('Laadpaal').WhTotal/1000 or 0
domoticz.log('Values reset to zero. Laadpaal start value set to: ' .. domoticz.data.laadpaalkWhBeginThisMonth, domoticz.LOG_INFO)
-- Reset the trigger to 0
domoticz.data.resetTrigger = 0
return
end
-- domoticz.data.laadpaalkWhThisMonth = 69.43 --domoticz.devices('Laadpaal').WhTotal/1000 -
-- domoticz.data.solarkWhThisMonth = 6.83
if domoticz.devices('EVSE Switch 16A').state == 'Off' then
domoticz.log('#1 hier ' .. os.date('%H:%M'), domoticz.LOG_DEBUG)
local now = os.time()
local stateMap = {
[0] = 1, -- Off
[10] = 2, -- 6A
[20] = 3, -- 8A
[30] = 4, -- 10A
[40] = 5, -- 13A
[50] = 6 -- 16A
}
-- Add logging to check item.level
domoticz.log('item.level: ' .. tostring(itemLevel), domoticz.LOG_DEBUG)
local state = stateMap[itemLevel]
domoticz.log('Current state: ' .. (state or 'unknown'), domoticz.LOG_INFO)
if state == nil then
domoticz.log('Error: state is nil for item.level: ' .. tostring(itemLevel), domoticz.LOG_ERROR)
return
end
if domoticz.data.lastState ~= 0 and domoticz.data.totalTime[domoticz.data.lastState] then
local duration = now - domoticz.data.startTime
domoticz.log('Duration for state ' .. domoticz.data.lastState .. ': ' .. duration .. ' seconds', domoticz.LOG_INFO)
domoticz.data.totalTime[domoticz.data.lastState] = domoticz.data.totalTime[domoticz.data.lastState] + duration
end
domoticz.data.startTime = now
domoticz.data.lastState = state or 0
-- Ensure totalTime values are not nil
for i = 2, 6 do
if domoticz.data.totalTime[i] == nil then
domoticz.data.totalTime[i] = 0
end
end
-- Convert total time from seconds to truncated minutes
local times = {
padRight('6A: ', math.floor(domoticz.data.totalTime[2] / 60) .. 'm', 8),
padRight('8A: ', math.floor(domoticz.data.totalTime[3] / 60) .. 'm', 8),
padRight('10A: ', math.floor(domoticz.data.totalTime[4] / 60) .. 'm', 8),
padRight('13A: ', math.floor(domoticz.data.totalTime[5] / 60) .. 'm', 8),
padRight('16A: ', math.floor(domoticz.data.totalTime[6] / 60) .. 'm', 8)
}
-- Calculate total active time excluding 'Off' state
local totalActiveTime = 0
for i = 2, 6 do
totalActiveTime = totalActiveTime + domoticz.data.totalTime[i]
end
-- Calculate percentage of time each state was active
local percentages = {
padRight('6A: ', string.format('%.0f', (domoticz.data.totalTime[2] / totalActiveTime) * 100) .. '%', 9),
padRight('8A: ', string.format('%.0f', (domoticz.data.totalTime[3] / totalActiveTime) * 100) .. '%', 9),
padRight('10A: ', string.format('%.0f', (domoticz.data.totalTime[4] / totalActiveTime) * 100) .. '%', 9),
padRight('13A: ', string.format('%.0f', (domoticz.data.totalTime[5] / totalActiveTime) * 100) .. '%', 9),
padRight('16A: ', string.format('%.0f', (domoticz.data.totalTime[6] / totalActiveTime) * 100) .. '%', 9)
}
domoticz.log('#3 hier ' .. os.date('%H:%M'), domoticz.LOG_DEBUG)
-- Get kWh from 'Laadpaal' meter
domoticz.data.solarkWhToday = domoticz.devices('Laadpaal').WhToday / 1000
domoticz.log('Collected solarkWhToday: ' .. domoticz.data.solarkWhToday .. ' kWh', domoticz.LOG_DEBUG)
-- daily update of solarkWhThisMonth
if os.date('%H:%M') == '23:56' then
domoticz.data.solarkWhThisMonth = domoticz.data.solarkWhThisMonth + domoticz.data.solarkWhToday
domoticz.log('Collected solarkWhToday: ' .. string.format('%.2f',domoticz.data.solarkWhToday) .. ' kWh', domoticz.LOG_DEBUG)
domoticz.data.solarkWhToday = 0
domoticz.log('Updated solarkWhThisMonth: ' .. string.format('%.2f',domoticz.data.solarkWhThisMonth) .. ' kWh', domoticz.LOG_DEBUG)
end
-- Update the text sensor with the total active time for each state and kWh
local textSensorDay = domoticz.devices('Charging Levels Day')
if textSensorDay then
local text = os.date('%B')..' : '..string.format('%.2f', domoticz.data.laadpaalkWhThisMonth)..' kWh, Solar : '.. string.format('%.2f',(domoticz.data.solarkWhThisMonth + domoticz.data.solarkWhToday)).. ' kWh\n '..
'\nSolar Today : '.. string.format('%.2f',domoticz.data.solarkWhToday).. ' kWh\n\n '
textSensorDay.updateText(text)
domoticz.log('Updated text sensor with: ' .. text, domoticz.LOG_INFO)
else
domoticz.log('textSensorDay not found', domoticz.LOG_ERROR)
end
local textSensor = domoticz.devices('Charging Levels')
if textSensor then -- '<pre style="font-family: monospace;font-size: 10px;">'..
local text = os.date('%B %Y') .. '\n' ..
table.concat(times, '|') .. '\n' ..
table.concat(percentages, '|') -- .. '</pre>'
textSensor.updateText(text)
domoticz.log('Updated text sensor with: ' .. text, domoticz.LOG_INFO)
else
domoticz.log('Text sensor not found', domoticz.LOG_ERROR)
end
end -- if EVSE switch is off
end
}
The next script collects the data from the first script and presents a table in another Text Sensor (Charging Summary) where PV charging time and PV charging percentages are displayed for each month.
Code: Select all
-- this script runs every day at 23:57
-- it only executes on the last day of the month
-- it collects the data
-- to reset all data change line 36
return {
active = true,
on = {
timer = { 'at 23:57' } -- {'every 1 minutes'} for testing
},
data = {
monthlyData = { initial = {} },
},
logging = {
-- Level can be domoticz.LOG_INFO, domoticz.LOG_MODULE_EXEC_INFO, domoticz.LOG_DEBUG, domoticz.LOG_ERROR or domoticz.LOG_FORCE
level = domoticz.LOG_DEBUG,
marker = '---- Charging Time Monthly Summary Display -----',
},
execute = function(domoticz)
domoticz.log('os.date({%H:%M})' .. os.date('%H:%M'), domoticz.LOG_DEBUG)
if (os.date('%d') == tostring(os.date('*t', os.time{year=os.date('%Y'), month=os.date('%m')+1, day=0}).day)) then
local textSensor = domoticz.devices('Charging Levels')
local monthTextSensor = domoticz.devices('Charging Summary')
local currentMonth = os.date('%m-%y')
local text = textSensor.text
-- Store the data for the current month
domoticz.data.monthlyData[currentMonth] = text
-- Keep only the last 12 months of data
local keys = {}
for k in pairs(domoticz.data.monthlyData) do
table.insert(keys, k)
end
table.sort(keys)
while #keys > 12 do -- > replacing 12 with 0 removes all data
domoticz.data.monthlyData[keys[1]] = nil
table.remove(keys, 1)
end
-- Update the month text sensor with the collected data
local monthText = ''
for _, k in ipairs(keys) do
monthText = monthText .. k .. ': ' .. domoticz.data.monthlyData[k] .. '\n\n'
end
monthTextSensor.updateText(monthText)
domoticz.log('Updated month text sensor with: ' .. monthText, domoticz.LOG_DEBUG)
end
end
}