Sun at South

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

Moderator: leecollings

Post Reply
bagheerahans
Posts: 7
Joined: Tuesday 01 May 2018 12:23
Target OS: Raspberry Pi / ODroid
Domoticz version: V4.9700
Contact:

Sun at South

Post by bagheerahans »

I am looking for the way to script 'Sun at South' in dzVents.
In the switches it is available, but in Lua I can't find the way to code.
The 'at daytime' is already a between and not a time value.
Do a overlook something, or is it possible to calculate with times like :
(between (sunset + sunrise) / 2 and sunset)
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Sun at South

Post by waaren »

bagheerahans wrote: Tuesday 07 July 2020 18:57 I am looking for the way to script 'Sun at South' in dzVents.
You could use

Code: Select all

return
{
    on =
    {
        timer =
        {
                'every minute'
        },
    },

    execute = function(dz)

        local function minutesToTime(minutes)
            local h =  math.floor(minutes / 60)
            local hh = ('0' .. h):sub(-2)
            local mm = ('0' .. minutes - (h * 60)):sub(-2)
            return hh .. ':' .. mm
        end

        dz.log("Sun at South is " .. minutesToTime(_G.timeofday.SunAtSouthInMinutes) )
    end
}
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
hestia
Posts: 357
Joined: Monday 25 December 2017 23:06
Target OS: Raspberry Pi / ODroid
Domoticz version: 2022.1
Location: Paris
Contact:

Re: Sun at South

Post by hestia »

You could use a script that give solar data like
https://www.domoticz.com/forum/viewtopi ... 72&t=19220
the south is Azimuth = 180°. You could trigger the device
I did a simplified version without link to a weather site

Code: Select all

--[[
	Prerequisites
	==================================
	Requires dz v3.8551 or later
	2 sensors with Cloud Cover and Pressure updated
	
	Virtual Lux sensor and other real-time solar data

-- Authors  ----------------------------------------------------------------
	V1.0 - Sébastien Joly - Great original work
	V1.1 - Neutrino - Adaptation to dz
	V1.2 - Jmleglise - An acceptable approximation of the lux below 1° altitude for Dawn and dusk + translation + several changes to be more userfriendly.
	V1.3 - Jmleglise - No update of the Lux data when <=0 to get the sunset and sunrise with lastUpdate
	V1.4 - use the API instead of updateDevice to update the data of the virtual sensor to be able of using devicechanged['Lux'] in our scripts. (Due to a bug in dz that doesn't catch the devicechanged event of the virtual sensor)
	V1.5 - xces - UTC time calculation.
	V2.0 - BakSeeDaa - Converted to dzVents and changed quite many things.
	V2.4.1-DarkSky - oredin - Use Dark Sky API instead of WU API
	V x.x - SolarData_V0 by hestia - sources independent: use sensor with input values for CloudCover and Pressure
	06/11/2019 - Function math.pow(x, y) has been deprecated in Lua 5.3. Please consider changing code to x^y
]]--

-- Variables to customize ------------------------------------------------
-- Dummy devices to create
local idxSolarAzimuth = 337						-- (Integer) Virtual Azimuth Device ID
local idxSolarAltitude = 338					-- (Integer) Your virtual Solar Altitude Device ID

-- dz devices to get data
-- local idxCloudCover = 309 -- (Integer) dz Cloud Cover (custom sensor device ID)
-- local idxCloudCoverPerCent = 339 -- (Integer) dz Cloud Cover (percentage device ID) => to choose the one you want
local idxOkta = 442 -- (Integer) (Custom) sensor device ID
local idxPressure = 303 -- (Integer) Your Pressure Device ID

-- dz devices to save result
local idxLux = 305 -- (Integer) dz virtual Lux device ID
local idxSolarRadiation = 304 -- (Integer) dz virtual Solar Radiation device ID


local fetchIntervalDayMins = 5 -- Day time scraping interval
local fetchIntervalNightMins = 15 -- Night time scraping interval


local latitude = 51.748485	-- Latitude. (Decimal number) Decimal Degrees. E.g. something like 51.748485
local longitude = 5.629728	-- Longitude. (Decimal number) Decimal Degrees. E.g.something like 5.629728.
local altitude = 100	-- Altitude. (Integer) Meters above sea level.

-- There is no simple conversion...it depends on the wavelength or color of the light
-- However, for the SUN, there is an approximate conversion of 0.0079
-- Lux.http://bccp.berkeley.edu/o/Academy/workshop_09/pdfs/InverseSquareLawPresentation.p
local LuxToWm2 = 0.0079

local scriptVersion = '2.4.1'

return {
	active = true,
	logging = {
		level = domoticz.LOG_ERROR, -- Select one of LOG_DEBUG, LOG_INFO, LOG_ERROR, LOG_FORCE to override system log level -- LOG_ERROR for normal execution
	},
	on = {
		devices = {idxOkta, idxPressure},
		-- timer = {
		--	'every '..tostring(fetchIntervalDayMins)..' minutes at civildaytime',
		--	'every '..tostring(fetchIntervalNightMins)..' minutes at civilnighttime',
		-- },
	},
	
	execute = function(dz, item, triggerInfo)
    _G.logMarker =  dz.moduleLabel -- set logmarker to scriptname   
	    
	    local LOG_LEVEL = dz.LOG_INFO -- LOG_INFO, LOG_DEBUG, LOG_ERROR, LOG_FORCE -- LOG_INFO for normal execution

    	local function leapYear(year)   
			return year%4==0 and (year%100~=0 or year%400==0)
		end

		local arbitraryTwilightLux = 6.32 -- W/m² egal 800 Lux (the theoritical value is 4.74 but I have more accurate result with 6.32...) /!\
		local constantSolarRadiation = 1361 -- Solar Constant W/m²

		local relativePressure = dz.devices(idxPressure).barometer  -- from dz sensor updated with other script or a sensor
		
		local year = os.date('%Y')
		local numOfDay = os.date('%j')
		local nbDaysInYear = (leapYear(year) and 366 or 365)

		local angularSpeed = 360/365.25
		local declination = math.deg(math.asin(0.3978 * math.sin(math.rad(angularSpeed) *(numOfDay - (81 - 2 * math.sin((math.rad(angularSpeed) * (numOfDay - 2))))))))
		local timeDecimal = (os.date('!%H') + os.date('!%M') / 60) -- Coordinated Universal Time  (UTC)
		local solarHour = timeDecimal + (4 * longitude / 60 )    -- The solar Hour
		local hourlyAngle = 15 * ( 12 - solarHour )          -- hourly Angle of the sun
		local sunAltitude = math.deg(math.asin(math.sin(math.rad(latitude))* math.sin(math.rad(declination)) + math.cos(math.rad(latitude)) * math.cos(math.rad(declination)) * math.cos(math.rad(hourlyAngle))))-- the height of the sun in degree, compared with the horizon

		local azimuth = math.acos((math.sin(math.rad(declination)) - math.sin(math.rad(latitude)) * math.sin(math.rad(sunAltitude))) / (math.cos(math.rad(latitude)) * math.cos(math.rad(sunAltitude) ))) * 180 / math.pi -- deviation of the sun from the North, in degree
		local sinAzimuth = (math.cos(math.rad(declination)) * math.sin(math.rad(hourlyAngle))) / math.cos(math.rad(sunAltitude))
		if(sinAzimuth<0) then azimuth=360-azimuth end
		local sunstrokeDuration = math.deg(2/15 * math.acos(- math.tan(math.rad(latitude)) * math.tan(math.rad(declination)))) -- duration of sunstroke in the day . Not used in this calculation.
		local RadiationAtm = constantSolarRadiation * (1 +0.034 * math.cos( math.rad( 360 * numOfDay / nbDaysInYear ))) -- Sun radiation  (in W/m²) in the entrance of atmosphere.
		
		-- Coefficient of mitigation M
		local absolutePressure = relativePressure - dz.utils.round((altitude/ 8.3),1) -- hPa
		local sinusSunAltitude = math.sin(math.rad(sunAltitude))
		local M0 = math.sqrt(1229 + (614 * sinusSunAltitude)^2) - 614 * sinusSunAltitude
		--print ("MO= " .. M0)
		local M = M0 * relativePressure/absolutePressure
		
  		dz.log('triggerInfo: ' .. triggerInfo.type, dz.LOG_INFO)
		dz.log('', dz.LOG_INFO)
		dz.log('==============  SUN  LOG ==================', dz.LOG_INFO)
		dz.log('Latitude: ' .. latitude .. ', Longitude: ' .. longitude, dz.LOG_INFO)
		dz.log('Home altitude = ' .. tostring(altitude) .. ' m', dz.LOG_INFO)
		dz.log('Angular Speed = ' .. angularSpeed .. ' per day', dz.LOG_INFO)
		dz.log('Declination = ' .. declination .. '°', dz.LOG_INFO)
		dz.log('Universal Coordinated Time (UTC) '.. timeDecimal ..' H.dd', dz.LOG_INFO)
		dz.log('Solar Hour '.. solarHour ..' H.dd', dz.LOG_INFO)
		dz.log('Altitude of the sun = ' .. sunAltitude .. '°', dz.LOG_INFO)
		dz.log('Angular hourly = '.. hourlyAngle .. '°', dz.LOG_INFO)
		dz.log('Azimuth of the sun = ' .. azimuth .. '°', dz.LOG_INFO)
		dz.log('Duration of the sun stroke of the day = ' .. dz.utils.round(sunstrokeDuration,2) ..' H.dd', dz.LOG_INFO)
		dz.log('Radiation max in atmosphere = ' .. dz.utils.round(RadiationAtm,2) .. ' W/m²', dz.LOG_INFO)
		dz.log('Local relative pressure = ' .. relativePressure .. ' hPa', dz.LOG_INFO)
		dz.log('Absolute pressure in atmosphere = ' .. absolutePressure .. ' hPa', dz.LOG_INFO)
		dz.log('Coefficient of mitigation M = ' .. M ..' M0 = '..M0, dz.LOG_INFO)
		dz.log('', dz.LOG_INFO)

    	local okta = dz.devices(idxOkta).state
		--print('okta: ' .. okta)
		local Kc = 1-0.75*((okta/8)^3.4)  -- Factor of mitigation for the cloud layer
		--print('Kc= ' .. Kc)

		local directRadiation, scatteredRadiation, totalRadiation, Lux, weightedLux
		if sunAltitude > 1 then -- Below 1° of Altitude , the formulae reach their limit of precision.
			directRadiation = RadiationAtm * (0.6^M) * sinusSunAltitude
			--print ("directRadiation= " .. directRadiation)
			scatteredRadiation = RadiationAtm * (0.271 - 0.294 * (0.6^M)) * sinusSunAltitude
			--print ("scatteredRadiation=" .. scatteredRadiation)
			totalRadiation = scatteredRadiation + directRadiation
			Lux = totalRadiation / LuxToWm2  -- Radiation in Lux. 1 Lux = 0,0079 W/m²
			weightedLux = Lux * Kc   -- radiation of the Sun with the cloud layer
		elseif sunAltitude <= 1 and sunAltitude >= -7  then -- apply theoretical Lux of twilight
			directRadiation = 0
			scatteredRadiation = 0
			arbitraryTwilightLux=arbitraryTwilightLux-(1-sunAltitude)/8*arbitraryTwilightLux
			totalRadiation = scatteredRadiation + directRadiation + arbitraryTwilightLux 
		    Lux = totalRadiation / LuxToWm2  -- Radiation in Lux. 1 Lux = 0,0079 W/m²
			weightedLux = Lux * Kc   -- radiation of the Sun with the cloud layer
		elseif sunAltitude < -7 then  -- no management of nautical and astronomical twilight...
			directRadiation = 0
			scatteredRadiation = 0
			totalRadiation = 0
			Lux = 0
			weightedLux = 0  --  should be around 3,2 Lux for the nautic twilight. Nevertheless
		end
        weightedLux = dz.utils.round(weightedLux,0)
        totalRadiation = dz.utils.round(totalRadiation,0)
        
		-- dz.log('CloudCover = '.. dz_cloudCover, dz.LOG_INFO)
		dz.log('Okta = '.. okta, dz.LOG_INFO)
		dz.log('Kc = ' .. Kc, dz.LOG_INFO)
		dz.log('Direct Radiation = '.. dz.utils.round(directRadiation,0) ..' W/m²', dz.LOG_INFO)
		dz.log('Scattered Radiation = '.. dz.utils.round(scatteredRadiation,0) ..' W/m²', dz.LOG_INFO)
		dz.log('Total radiation = ' .. totalRadiation ..' W/m²', dz.LOG_INFO)
		dz.log('Total Radiation in lux = '.. dz.utils.round(Lux,0)..' Lux', dz.LOG_INFO)
		dz.log('Radiation through cloud layer  = '.. weightedLux..' Lux', dz.LOG_FORCE)

		-- No update if Lux is already 0. So lastUpdate of the Lux sensor will keep the time when Lux has reached 0.
		-- (Kind of timeofday['SunsetInMinutes'])
		if dz.devices(idxLux).lux + weightedLux > 0 then
			dz.devices(idxLux).updateLux(weightedLux)
		end
		dz.devices(idxSolarAzimuth).updateCustomSensor(dz.utils.round(azimuth,0))
		dz.devices(idxSolarAltitude).updateCustomSensor(dz.utils.round(sunAltitude,0))
		dz.devices(idxSolarRadiation).updateRadiation(totalRadiation)
	end
}
You can simplify to remove the lux part if you want
bagheerahans
Posts: 7
Joined: Tuesday 01 May 2018 12:23
Target OS: Raspberry Pi / ODroid
Domoticz version: V4.9700
Contact:

Re: Sun at South

Post by bagheerahans »

Thanks for the first step.
To make the on timer, I want to make a time scope.
Therfore, is the 'timeofday['SunAtSouthInMinutes']' compatible with the 'between aa and bb' ?
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Sun at South

Post by waaren »

bagheerahans wrote: Tuesday 07 July 2020 20:21 Thanks for the first step.
To make the on timer, I want to make a time scope.
Therfore, is the 'timeofday['SunAtSouthInMinutes']' compatible with the 'between aa and bb' ?
This is not implemented yet. Will have a look over the next couple of days if it can be made part of the dzVents framework like sunset and sunrise without too much hassle.
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Sun at South

Post by waaren »

bagheerahans wrote: Tuesday 07 July 2020 20:21 Thanks for the first step.
To make the on timer, I want to make a time scope.
Therefore, is the 'timeofday['SunAtSouthInMinutes']' compatible with the 'between aa and bb' ?
Implemented in build 12213 (dzVents 3.0.11) as solarnoon
(Thx to @dannybloe we are inclusive to domoticz users from the southern hemisphere :) )
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
bagheerahans
Posts: 7
Joined: Tuesday 01 May 2018 12:23
Target OS: Raspberry Pi / ODroid
Domoticz version: V4.9700
Contact:

Re: Sun at South

Post by bagheerahans »

I upgrade to beta version (without any problem). And start using solarnoon.
Works good! Thanks.

My roller blind are now running with:

Code: Select all

return {
    on = {
        devices = {'Gordijn voor', 'Gordijn achter', 'ZomerStandGordijn'},
        timer = {'at solarnoon'} },
    logging = {level = domoticz.LOG_DEBUG, marker = "Gordijn4"},

	execute = function(domoticz, device)

    if (device.isTimer or device.name == 'ZomerStandGordijn') then
        if (domoticz.time.matchesRule('between solarnoon and sunset') and domoticz.devices('ZomerStandGordijn').state == 'On') then
           domoticz.devices('Gordijn voor').switchSelector(20).afterSec(60)
           domoticz.log("Gordijn voor dicht")
        end
    end

    if (device.name == 'ZomerStandGordijn') then
        if (domoticz.time.matchesRule('between solarnoon and sunset') and domoticz.devices('ZomerStandGordijn').state == 'Off') then
           domoticz.devices('Gordijn voor').switchSelector(10)
           domoticz.log("Gordijn voor open")
        end
    end


    if(device.name == 'Gordijn voor' and device.state == 'Off') then
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,0,0')
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,1,0')
	end


    if(device.name == 'Gordijn voor' and device.state == 'Open') then
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,0,1')
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,1,0')
        domoticz.devices('Gordijn voor').switchOff().afterSec(60)
	end


    if(device.name == 'Gordijn voor' and device.state == 'Dicht') then
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,0,0')
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,1,1')
        domoticz.devices('Gordijn voor').switchOff().afterSec(60)
	end
	
	
    if(device.name == 'Gordijn achter' and device.state == 'Off') then
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,2,0')
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,3,0')
	end


	if(device.name == 'Gordijn achter' and device.state == 'Open') then
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,2,1')
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,3,0')
        domoticz.devices('Gordijn achter').switchOff().afterSec(60)
	end
	
	
	if(device.name == 'Gordijn achter' and device.state == 'Dicht') then
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,2,0')
    	domoticz.openURL('http://192.168.2.125/control?cmd=relay,3,1')
        domoticz.devices('Gordijn achter').switchOff().afterSec(60)
	end
	
end
}
ZomerStandGordijn is a switch, so it can be overruled, which is set by the average of solar radiation and a value with hysteresis.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest