SmartEVSE script for car charging

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

Moderator: leecollings

Post Reply
HvdW
Posts: 535
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

SmartEVSE script for car charging

Post by HvdW »

Hi,
I wrote a script for car charging using the SmartEVSE device.
Our EVBox is > 10 years old and I decided to make it 'smart'.
Michael Stegen gave me some clues how to open and change the EVBox and put the SmartEVSE device in place and it worked right from the start.
The script I wrote functioned like it shoud but I wanted to have it rewritten.
So I asked Microsoft CoPilot to do so.
The result is a script that goes beyond my programming skills, like the use of powerLevels in a table and the in pairs construct.

Code: Select all

for level, power in pairs(powerLevels) do
            if availableEnergy >= power then
                newLevel = level
            end
CoPilot added a time 'buffer' as well to hev the script not change the carging level for every single cloud passing by.
Here's the code:

Code: Select all

return {
    on = {
        timer = {'every 1 minutes at daytime'}, -- at daytime
    },

    logging = {
        level = domoticz.LOG_ERROR,
        marker = 'Car charging on PV Power',
    },

    data = {
        desiredLevel = { initial = 'Off' },
        lastChangeTime = { initial = 0 }
    },

    execute = function(domoticz, triggeredItem)
        local ChargingSwitchState = domoticz.devices('Charging Level').levelName
        local EVSwitch_16A = domoticz.devices('EVSE Switch 16A')

        -- Define power levels
        local powerLevels = {
            Off = 0,
            ['6A'] = 1400,
            ['8A'] = 1850,
            ['10A'] = 2300,
            ['13A'] = 3000,
            ['16A'] = 3700
        }

        -- Get power level based on charging switch state
        local PowerLaadpaal = powerLevels[ChargingSwitchState] or 0

        -- Get power data
        local PowerReturn = tonumber(domoticz.devices('Power').rawData[6]) -- Solar power generation
        local PowerConsumption = tonumber(domoticz.devices('Power').rawData[5]) -- Home power consumption
        local availableEnergy = PowerLaadpaal + PowerReturn - PowerConsumption

        -- Logging
        domoticz.log('#1 Energy usage from the network: ' .. PowerConsumption, domoticz.LOG_DEBUG)
        domoticz.log('#2 Energy return to the network: ' .. PowerReturn, domoticz.LOG_DEBUG)
        domoticz.log('#3 Actual power on laadpaal: ' .. PowerLaadpaal, domoticz.LOG_DEBUG)
        domoticz.log('#4 Available energy: ' .. availableEnergy, domoticz.LOG_DEBUG)
        
        if domoticz.globalData.EVSE_CommunicationError ~= 'None' then
           domoticz.notify('There is an EVSE Communication Error', domoticz.PRIORITY_NORMAL)
        end
        
        -- Determine the desired charging level
        local newLevel = 'Off'
        for level, power in pairs(powerLevels) do
            if availableEnergy >= power then
                newLevel = level
            end
        end
        
        -- Check if EV is connected and switch is off
        if domoticz.globalData.EVSE_Connected == 'Connected' and EVSwitch_16A.state == 'Off' then
            local currentTime = os.time()
            if newLevel ~= domoticz.data.desiredLevel then
                domoticz.data.desiredLevel = newLevel
                domoticz.data.lastChangeTime = currentTime
            elseif currentTime - domoticz.data.lastChangeTime >= 120 then -- 2 minutes buffer
                if availableEnergy < 900 then
                    newLevel = 'Off'
                end
                domoticz.devices('Charging Level').switchSelector(newLevel)
                domoticz.log('## Set to: ' .. newLevel, domoticz.LOG_DEBUG)

                -- Notification for starting or stopping charging
                local current_time = os.date('%Y-%m-%d %H:%M:%S')
                local subject, message
                if newLevel == 'Off' then
                    subject = 'We stopped charging on PV power!'
                    message = current_time .. '\nCharging level is set to: Off'
                else
                    subject = 'We are charging on PV power!'
                    message = current_time .. '\nCharging level is set to: ' .. newLevel
                end
                domoticz.notify(subject, message, domoticz.PRIORITY_NORMAL)
                domoticz.log('#8 Notification sent: ' .. subject, domoticz.LOG_DEBUG)
            end
            domoticz.log('#10 Car charging on PV Power END of test loop', domoticz.LOG_DEBUG)
        end
    end
}

Switching is done using a selector switch and the 'EVSE Switch 16A' is used to be able to override PV power charging.

There is more to come.
I'll show you how to switch the SmartEVSE using mosquitto, how to set the charging level for the car using the Python Plugin: Volvo EV and a script that shows how much charging time and percentage is used for each charhing level.

Before publishing the scripts I'll consult CoPilot to enhance the scripts I wrote.
Last edited by HvdW on Sunday 27 October 2024 0:20, edited 1 time in total.
Bugs bug me.
HvdW
Posts: 535
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: SmartEVSE script for car charging

Post by HvdW »

Here's the code for the Charging Level for the selector switch.

Code: Select all

return {
    on = {
        devices = {'Charging Level'},
    },
    
    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 Level -----',
    },
    
    execute = function(domoticz, triggerObject)
        local SunChargingLevel = domoticz.devices('Charging Level')
        local stateActions = {
            Off = {mode = 'Off', current = 0},
            ['6A'] = {mode = 'Normal', current = 60},
            ['8A'] = {mode = 'Normal', current = 80},
            ['10A'] = {mode = 'Normal', current = 100},
            ['13A'] = {mode = 'Normal', current = 130},
            ['16A'] = {mode = 'Normal', current = 160}
        }

        local action = stateActions[SunChargingLevel.state]
        if action then
            domoticz.log(SunChargingLevel.state .. ' SunChargingLevel set to ' .. SunChargingLevel.state, domoticz.LOG_DEBUG)
            domoticz.executeShellCommand('mosquitto_pub -h 192.168.2.104 -t "SmartEVSE/Set/Mode" -m ' .. action.mode)
            domoticz.executeShellCommand('mosquitto_pub -h 192.168.2.104 -t "SmartEVSE/Set/CurrentOverride" -m ' .. action.current)
        else
            domoticz.log('Unknown state: ' .. SunChargingLevel.state, domoticz.LOG_ERROR)
        end

        domoticz.log('End Sun Charging Level set to: ' .. SunChargingLevel.state, domoticz.LOG_DEBUG)
    end
}
As you can see this script uses Normal Mode, not Solar Mode or Smart Mode because Domoticz scripts govern the SmartEVSE.

The SmartEVSE as it is delivered has a Min of 6A and a max of 13A.
To change the max to 16A just set it in Solar mode, change the max and go back to Normal mode.
You can do it with the buttons on the device but you can do it just as easy by using curl or mosquitto commands from the RPI SSH command line.
The benefit of using mosquitto is that it is independent of the IP address of the SmartEVSE device.
The documentation on GitHub isn't perfect.
To set mode using mosquitto you need to use 0, 1, 2, 3 instead of the Curl Off, Normal, Solar, Smart
Bugs bug me.
HvdW
Posts: 535
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

SmartEVSE with Volvo API feedback

Post by HvdW »

Here is the extended version which uses the information from the car and a new dimmer switch is added which sets the desired charging level.
The dimmer is 'XC40 battery charge set'

Code: Select all

return {
    on = {   --  at daytime','at 20 minutes after sunset 
        timer = {'every 1 minutes', }, -- at daytime
    },

    logging = {
        level = domoticz.LOG_DEBUG,
        marker = 'Car charging Simple'
    },

    data = {
        desiredLevel = { initial = 'Off' },
        lastChangeTime = { initial = 0 },
    },

    execute = function(domoticz, triggeredItem)
        -- domoticz.devices('Charging Level').dump()
        local ChargingSwitchState = domoticz.devices('Charging Level').levelName
        local EVSwitch_16A = domoticz.devices('EVSE Switch 16A')
        local chargeLevelSet = domoticz.devices('XC40 battery charge set').level
	    local chargeLevelActual = domoticz.devices('XC40-ChargeLevel').nValue -- is een int en niet iets met een komma XC40-ChargeLevel
        local EVSE_Connected = domoticz.globalData.EVSE_Connected
        local newLevel = 'Off'
        
        -- Define power levels
        local powerLevels = {
            Off = 0,
            ['6A'] = 1400,
            ['8A'] = 1850,
            ['10A'] = 2300,
            ['13A'] = 3000,
            ['16A'] = 3700
        }
        
        -- Get power level based on charging switch state
        local PowerLaadpaal = powerLevels[ChargingSwitchState] or 0
        
        -- Get power data
        local PowerReturn = tonumber(domoticz.devices('Power').rawData[6]) -- Solar power generation
        local PowerConsumption = tonumber(domoticz.devices('Power').rawData[5]) -- Home power consumption
        local availableEnergy = PowerLaadpaal + PowerReturn - PowerConsumption

        -- Logging
        domoticz.log('#1 Energy usage from the network: ' .. PowerConsumption, domoticz.LOG_DEBUG)
        domoticz.log('#2 Energy return to the network: ' .. PowerReturn, domoticz.LOG_DEBUG)
        domoticz.log('#3 Actual power on laadpaal: ' .. PowerLaadpaal, domoticz.LOG_DEBUG)
        domoticz.log('#4 Available energy: ' .. availableEnergy, domoticz.LOG_DEBUG)
        
        -- Check if SmartEVSE is OK
        if domoticz.globalData.EVSE_CommunicationError ~= 'None' then
           domoticz.notify('@1 There is an EVSE Communication Error', domoticz.PRIORITY_NORMAL)
           domoticz.devices('Auto laden').switchOff().forSec(30)
        end
        
        -- Determine the desired charging level
        local newLevel = 'Off'
        local currentLevel = domoticz.devices('Charging Level').state
        domoticz.log('###### currentLevel : ' .. currentLevel, domoticz.LOG_DEBUG)
        for level, power in pairs(powerLevels) do
            if availableEnergy >= power then
                newLevel = level
            end
        end
        domoticz.log('###### newLevel : ' .. newLevel, domoticz.LOG_DEBUG)
        
        -- Check if chargeLevelActual >= chargeLevelSet
        if chargeLevelActual >= chargeLevelSet and EVSE_Connected == 'Connected' then
            domoticz.devices('Charging Level').switchSelector('Off')
            domoticz.log('#5 Set to: ' .. newLevel, domoticz.LOG_DEBUG)
        -- Check if chargeLevelActual < chargeLevelSet
        elseif chargeLevelActual < chargeLevelSet and EVSE_Connected == 'Connected' and EVSwitch_16A.state == 'Off' then
            local currentTime = os.time()
            if availableEnergy < 900 then  -- 1400 - 500 = 900
                newLevel = 'Off'
            end
            domoticz.devices('Charging Level').switchSelector(newLevel)
            domoticz.log('#5 Set to: ' .. newLevel, domoticz.LOG_DEBUG)
            
            -- Notify charging state
            -- It sends the notification one time when the level has changed
            local current_time = os.date('%d-%m-%Y %H:%M')
            local subject, message
            if newLevel == 'Off' and (currentLevel ~= newLevel) then 
                subject = '@2 Charging on PV power has ended.'
                message = 'Charging Off, Energy : ' .. availableEnergy .. '\nTime : ' .. current_time
                if domoticz.LOG_DEBUG then
                    domoticz.notify(subject, message, domoticz.PRIORITY_NORMAL)
                    domoticz.log('#6 Notification sent: ' .. subject, domoticz.LOG_DEBUG)
                end
            elseif newLevel ~= 'Off'and (currentLevel ~= newLevel) then
                subject = '@3 We are charging on PV power.'
                message = 'Charging ' .. newLevel .. ', Energy : ' .. availableEnergy .. '\nTime : ' .. current_time
                if domoticz.LOG_DEBUG then
                    domoticz.notify(subject, message, domoticz.PRIORITY_NORMAL)
                    domoticz.log('#7 Notification sent: ' .. subject, domoticz.LOG_DEBUG)
                end
            end
            domoticz.log('#7 Car charging on PV Power END of test loop', domoticz.LOG_DEBUG)
        end
    end
}
The code has many logging points.
Logging can be switched off by changing LOG_DEBUG to LOG_ERROR
To prevent being spammed by notifications a new notification is only being sent when a change in Charging Level occurs.
The code checks if the SmartEVSE has a Communication Error or None and switches off power to the SmartEVSE unit for 30 seconds to reset it.
Last edited by HvdW on Tuesday 29 October 2024 15:10, edited 1 time in total.
Bugs bug me.
HvdW
Posts: 535
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Car charging Solar Simple for SmartEVSE

Post by HvdW »

Here's another to use the SmartEVSE device in Solar mode

Code: Select all

-- Car charging Solar Simple for SmartEVSE

return {
    on = { devices = {'Power'} },

    logging = {
        level = domoticz.LOG_DEBUG,
        marker = 'Car Charging Solar',
    },

    execute = function(domoticz, triggeredItem)
        local EVSwitch_16A = domoticz.devices('EVSE Switch 16A')
        local PowerReturn = tonumber(domoticz.devices('Power').rawData[6]) -- Solar power generation
        
        local function wattsToAmps(watts, volts)
            return watts / volts
        end
        local amps = wattsToAmps(PowerReturn, 230)
        local current = amps * 10
        
        -- Check if SmartEVSE is OK
        if domoticz.globalData.EVSE_CommunicationError ~= 'None' then
           domoticz.notify('@1 There is an EVSE Communication Error, the device will restart in 30 seconds', domoticz.PRIORITY_NORMAL)
           domoticz.devices('Auto laden').switchOff().forSec(30)
        end
        
        if domoticz.devices('EVSE Switch 16A') == 'Off' and PowerReturn > 1400 then
            domoticz.executeShellCommand('mosquitto_pub  -h 192.168.2.104 -t "SmartEVSE/Set/Mode" -m Solar')
            domoticz.executeShellCommand('mosquitto_pub -h 192.168.2.104 -t "SmartEVSE/Set/MainsMeter" -m ' .. current .. ':0:0')
        end

        domoticz.log('#1 Energy return to the network: ' .. PowerReturn, domoticz.LOG_DEBUG)
        domoticz.log('#2 Power return in Ampère : ' .. amps, domoticz.LOG_DEBUG)

    end
}
You may wonder why the earlier published codes are so complicated.
That's because I want to be able to log the time and % of charging for each state. (6A, 8A,10A,13A,16A)
Bugs bug me.
HvdW
Posts: 535
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Insight in charging levels and charging time

Post by HvdW »

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)
Charging levels.jpg
Charging levels.jpg (13.71 KiB) Viewed 931 times

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
}
Bugs bug me.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 0 guests